@cdxgen/cdxgen 13.3.0 → 13.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/bin/cdxgen.js +90 -26
- package/bin/evinse.js +18 -1
- package/bin/repl.js +86 -0
- package/bin/sign.js +45 -15
- package/bin/tracebom.js +5 -1
- package/bin/validate.js +84 -18
- package/bin/verify.js +155 -86
- package/data/helpers/init.gradle +46 -21
- package/data/remediations.json +61 -0
- package/data/rules/dependency-sources.yaml +67 -0
- package/data/rules/scala-evidence.yaml +115 -0
- package/lib/audit/index.js +38 -6
- package/lib/cli/index.js +113 -54
- package/lib/cli/jsBom.js +50 -29
- package/lib/cli/jvmBom.js +665 -138
- package/lib/cli/managedBom.js +320 -200
- package/lib/cli/nativeBom.js +139 -30
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +17 -9
- package/lib/core/fs.js +3 -2
- package/lib/core/httpClient.js +159 -24
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/runState.js +39 -0
- package/lib/core/toml.js +72 -0
- package/lib/ecosystems/asarutils.js +4 -2
- package/lib/ecosystems/bunutils.js +5 -7
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/cmakeResolver.js +10 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +337 -22
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1967 -788
- package/lib/ecosystems/golem.js +28 -12
- package/lib/ecosystems/gradleutils.js +314 -30
- package/lib/ecosystems/jsEvidence.js +85 -24
- package/lib/ecosystems/kosi.js +21 -4
- package/lib/ecosystems/npmutils.js +64 -0
- package/lib/ecosystems/parsers-dotnet.js +567 -151
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +324 -61
- package/lib/ecosystems/parsers-js.js +92 -26
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +204 -12
- package/lib/ecosystems/parsers-misc.js +182 -55
- package/lib/ecosystems/parsers-python.js +141 -10
- package/lib/ecosystems/parsers-rust.js +4 -4
- package/lib/ecosystems/parsers-terraform.js +1573 -19
- package/lib/ecosystems/pythonutils.js +102 -6
- package/lib/ecosystems/remote/huggingface.js +3 -0
- package/lib/ecosystems/rubyutils.js +67 -17
- package/lib/ecosystems/rusi.js +22 -9
- package/lib/ecosystems/sbtutils.js +243 -187
- package/lib/ecosystems/scalacliutils.js +342 -0
- package/lib/ecosystems/scalasem.js +2618 -0
- package/lib/ecosystems/terraformRegistry.js +288 -0
- package/lib/ecosystems/utils.js +2 -0
- package/lib/ecosystems/vendoredCode.js +156 -0
- package/lib/evinser/evinser.js +578 -69
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +576 -128
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/atomUtils.js +171 -48
- package/lib/inventory/auditCategories.js +7 -0
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/deps.js +501 -124
- package/lib/inventory/dosai.js +248 -44
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +52 -6
- package/lib/inventory/evidenceUtils.js +154 -109
- package/lib/inventory/fetchBatch.js +574 -38
- package/lib/inventory/fetchDiskCache.js +597 -0
- package/lib/inventory/forgeEnricher.js +14 -5
- package/lib/inventory/jvmLocalRepos.js +917 -0
- package/lib/inventory/mcpConfigParser.js +2 -3
- package/lib/inventory/parentSelection.js +146 -0
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +8 -8
- package/lib/inventory/spdx.js +101 -0
- package/lib/inventory/traceRunner.js +88 -7
- package/lib/inventory/tracedCrypto.js +294 -0
- package/lib/managers/docker.js +33 -32
- package/lib/managers/oci.js +24 -2
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +16 -2
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/strictJson.js +322 -0
- package/lib/parsers/terraformConfig.js +1073 -0
- package/lib/parsers/terraformSource.js +652 -0
- package/lib/server/server.js +23 -0
- package/lib/stages/postgen/introspection/markers.js +1 -1
- package/lib/stages/postgen/postgen.js +15 -1
- package/lib/stages/postgen/specVersionCompat.js +32 -0
- package/lib/validator/index.js +25 -10
- package/lib/validator/reporters/json.js +1 -0
- package/package.json +21 -21
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +24 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +7 -4
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +3 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +9 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/httpClient.d.ts +13 -8
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/pluginRun.d.ts +97 -0
- package/types/lib/core/pluginRun.d.ts.map +1 -0
- package/types/lib/core/processTree.d.ts +36 -0
- package/types/lib/core/processTree.d.ts.map +1 -0
- package/types/lib/core/runState.d.ts +29 -0
- package/types/lib/core/runState.d.ts.map +1 -0
- package/types/lib/core/toml.d.ts +20 -0
- package/types/lib/core/toml.d.ts.map +1 -0
- package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +12 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
- package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
- package/types/lib/ecosystems/cppEvidence.d.ts +18 -2
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
- package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
- package/types/lib/ecosystems/denoutils.d.ts +9 -3
- package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +62 -13
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +50 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmutils.d.ts +15 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +44 -2
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +6 -1
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +3 -1
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +53 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +27 -2
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +21 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/sbtutils.d.ts +88 -2
- package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
- package/types/lib/ecosystems/scalasem.d.ts +311 -0
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
- package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
- package/types/lib/ecosystems/utils.d.ts +2 -2
- package/types/lib/ecosystems/utils.d.ts.map +1 -1
- package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
- package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
- package/types/lib/evinser/evinser.d.ts +75 -8
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +80 -4
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +82 -10
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +3 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
- package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
- package/types/lib/inventory/cacheCommands.d.ts +2 -1
- package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
- package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
- package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +59 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +57 -8
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts +53 -7
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts +10 -0
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +19 -4
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +83 -14
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
- package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/parentSelection.d.ts +34 -0
- package/types/lib/inventory/parentSelection.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts +80 -0
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +9 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +12 -0
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +42 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +3 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeCache.d.ts +16 -0
- package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +2 -1
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
- package/types/lib/parsers/cmakePresets.d.ts +78 -0
- package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
- package/types/lib/parsers/compileCommands.d.ts +115 -0
- package/types/lib/parsers/compileCommands.d.ts.map +1 -0
- package/types/lib/parsers/strictJson.d.ts +46 -0
- package/types/lib/parsers/strictJson.d.ts.map +1 -0
- package/types/lib/parsers/terraformConfig.d.ts +70 -0
- package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
- package/types/lib/parsers/terraformSource.d.ts +66 -0
- package/types/lib/parsers/terraformSource.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +8 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import { thoughtLog } from "../core/logger.js";
|
|
5
|
+
import { dirNameStr } from "../core/paths.js";
|
|
6
|
+
import { applyAlgorithmProperties } from "./cryptoAlgorithmFamily.js";
|
|
7
|
+
|
|
8
|
+
const cryptoOids = JSON.parse(
|
|
9
|
+
readFileSync(join(dirNameStr, "data", "crypto-oid.json"), "utf-8"),
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
// TLS key-exchange groups by their IANA registry name. A hybrid group pairs a
|
|
13
|
+
// classical curve with ML-KEM: each half has an OID, the pair has none, so the
|
|
14
|
+
// group is recorded as both algorithms.
|
|
15
|
+
const GROUP_ALGORITHMS = {
|
|
16
|
+
X25519MLKEM768: ["x25519", "ml-kem-768"],
|
|
17
|
+
SecP256r1MLKEM768: ["secp256r1", "ml-kem-768"],
|
|
18
|
+
SecP384r1MLKEM1024: ["secp384r1", "ml-kem-1024"],
|
|
19
|
+
X25519Kyber768Draft00: ["x25519"],
|
|
20
|
+
MLKEM512: ["ml-kem-512"],
|
|
21
|
+
MLKEM768: ["ml-kem-768"],
|
|
22
|
+
MLKEM1024: ["ml-kem-1024"],
|
|
23
|
+
x448: ["curveX448"],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const MLKEM_SECURITY_LEVELS = {
|
|
27
|
+
"ml-kem-512": 1,
|
|
28
|
+
"ml-kem-768": 3,
|
|
29
|
+
"ml-kem-1024": 5,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// The curve each classical key-exchange algorithm is defined over.
|
|
33
|
+
const KEY_AGREEMENT_CURVES = {
|
|
34
|
+
x25519: "x25519",
|
|
35
|
+
curveX448: "x448",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Closed enums of cryptoProperties.algorithmProperties in bom-1.7.schema.json.
|
|
39
|
+
const MODES = new Set(["cbc", "ecb", "ccm", "gcm", "cfb", "ofb", "ctr"]);
|
|
40
|
+
const CRYPTO_FUNCTIONS = new Set([
|
|
41
|
+
"generate",
|
|
42
|
+
"keygen",
|
|
43
|
+
"encrypt",
|
|
44
|
+
"decrypt",
|
|
45
|
+
"digest",
|
|
46
|
+
"tag",
|
|
47
|
+
"keyderive",
|
|
48
|
+
"sign",
|
|
49
|
+
"verify",
|
|
50
|
+
"encapsulate",
|
|
51
|
+
"decapsulate",
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Names the bulk cipher of a TLS cipher suite as a crypto-oid.json key, from
|
|
56
|
+
* either its IANA name (TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) or its OpenSSL
|
|
57
|
+
* name (ECDHE-RSA-AES128-GCM-SHA256).
|
|
58
|
+
*
|
|
59
|
+
* @param {string} suiteName Cipher suite name
|
|
60
|
+
* @returns {{name: string, bits: number, mode: string|undefined}|undefined} The cipher, when it has an OID
|
|
61
|
+
*/
|
|
62
|
+
export function suiteCipher(suiteName) {
|
|
63
|
+
const tokens = String(suiteName || "")
|
|
64
|
+
.toUpperCase()
|
|
65
|
+
.split(/[-_]/);
|
|
66
|
+
let cipher;
|
|
67
|
+
if (tokens.includes("CHACHA20")) {
|
|
68
|
+
cipher = { name: "chaCha20Poly1305", bits: 256, mode: undefined };
|
|
69
|
+
} else if (tokens.includes("3DES") || tokens.includes("CBC3")) {
|
|
70
|
+
cipher = { name: "des-EDE3-CBC", bits: 112, mode: "cbc" };
|
|
71
|
+
} else {
|
|
72
|
+
const aesIdx = tokens.findIndex((t) => t.startsWith("AES"));
|
|
73
|
+
if (aesIdx < 0) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
const bits =
|
|
77
|
+
Number.parseInt(tokens[aesIdx].substring(3), 10) ||
|
|
78
|
+
Number.parseInt(tokens[aesIdx + 1], 10);
|
|
79
|
+
const mode = ["GCM", "CCM"].find((m) => tokens.includes(m)) || "CBC";
|
|
80
|
+
cipher = { name: `aes${bits}-${mode}`, bits, mode: mode.toLowerCase() };
|
|
81
|
+
}
|
|
82
|
+
return cryptoOids[cipher.name]?.oid ? cipher : undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function oidKey(name) {
|
|
86
|
+
if (!name) {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
if (cryptoOids[name]?.oid) {
|
|
90
|
+
return name;
|
|
91
|
+
}
|
|
92
|
+
const lower = String(name).toLowerCase();
|
|
93
|
+
return cryptoOids[lower]?.oid ? lower : undefined;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function cipherProperties(cipher) {
|
|
97
|
+
const props = {
|
|
98
|
+
primitive: cipher.mode === "cbc" ? "block-cipher" : "ae",
|
|
99
|
+
parameterSetIdentifier: String(cipher.bits),
|
|
100
|
+
cryptoFunctions: ["encrypt", "decrypt"],
|
|
101
|
+
classicalSecurityLevel: cipher.bits,
|
|
102
|
+
};
|
|
103
|
+
if (cipher.mode) {
|
|
104
|
+
props.mode = cipher.mode;
|
|
105
|
+
}
|
|
106
|
+
return props;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function keyExchangeProperties(name) {
|
|
110
|
+
const level = MLKEM_SECURITY_LEVELS[name];
|
|
111
|
+
if (level) {
|
|
112
|
+
return {
|
|
113
|
+
primitive: "kem",
|
|
114
|
+
cryptoFunctions: ["keygen", "encapsulate", "decapsulate"],
|
|
115
|
+
nistQuantumSecurityLevel: level,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return { primitive: "key-agree", cryptoFunctions: ["keygen"] };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Keeps the values of an algorithm the traced CBOM described that fit the
|
|
122
|
+
// schema; the primitive itself is checked by applyAlgorithmProperties.
|
|
123
|
+
function knownAlgorithmProperties(props) {
|
|
124
|
+
const known = {};
|
|
125
|
+
if (typeof props?.primitive === "string") {
|
|
126
|
+
known.primitive = props.primitive === "aead" ? "ae" : props.primitive;
|
|
127
|
+
}
|
|
128
|
+
const mode = String(props?.mode || "").toLowerCase();
|
|
129
|
+
if (MODES.has(mode)) {
|
|
130
|
+
known.mode = mode;
|
|
131
|
+
}
|
|
132
|
+
const functions = (props?.cryptoFunctions || []).filter((f) =>
|
|
133
|
+
CRYPTO_FUNCTIONS.has(f),
|
|
134
|
+
);
|
|
135
|
+
if (functions.length) {
|
|
136
|
+
known.cryptoFunctions = functions;
|
|
137
|
+
}
|
|
138
|
+
for (const level of ["classicalSecurityLevel", "nistQuantumSecurityLevel"]) {
|
|
139
|
+
if (Number.isInteger(props?.[level])) {
|
|
140
|
+
known[level] = props[level];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return known;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Turns the CBOM safer-exec writes for a traced command into components that
|
|
148
|
+
* cdxgen can merge and validate:
|
|
149
|
+
*
|
|
150
|
+
* - every asset gets a bom-ref, without which the assets merge into one
|
|
151
|
+
* - algorithms carry their OID, and those without one are left out
|
|
152
|
+
* - hybrid key-exchange groups become their classical and ML-KEM halves
|
|
153
|
+
* - cipher suites become their bulk cipher, which the protocol's suites refer
|
|
154
|
+
* to by bom-ref
|
|
155
|
+
* - algorithm properties keep only values the schema allows
|
|
156
|
+
* - crypto libraries are left out: they are the traced library components,
|
|
157
|
+
* marked with cdx:dynamic:cryptoLibrary
|
|
158
|
+
*
|
|
159
|
+
* @param {Object[]} cbomComponents Components of the safer-exec CBOM
|
|
160
|
+
* @returns {Object[]} Normalized cryptographic-asset components
|
|
161
|
+
*/
|
|
162
|
+
export function normalizeTracedCryptoComponents(cbomComponents) {
|
|
163
|
+
const byRef = new Map();
|
|
164
|
+
const addAlgorithm = (name, algorithmProperties, curve) => {
|
|
165
|
+
const oid = cryptoOids[name].oid;
|
|
166
|
+
const ref = `crypto/algorithm/${encodeURIComponent(name)}@${oid}`;
|
|
167
|
+
if (!byRef.has(ref)) {
|
|
168
|
+
const component = {
|
|
169
|
+
type: "cryptographic-asset",
|
|
170
|
+
name,
|
|
171
|
+
"bom-ref": ref,
|
|
172
|
+
description: cryptoOids[name].description,
|
|
173
|
+
cryptoProperties: {
|
|
174
|
+
assetType: "algorithm",
|
|
175
|
+
algorithmProperties,
|
|
176
|
+
oid,
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
applyAlgorithmProperties(component, { name, curve });
|
|
180
|
+
byRef.set(ref, component);
|
|
181
|
+
}
|
|
182
|
+
return ref;
|
|
183
|
+
};
|
|
184
|
+
const protocols = [];
|
|
185
|
+
for (const comp of cbomComponents || []) {
|
|
186
|
+
const props = comp?.cryptoProperties;
|
|
187
|
+
if (comp?.type !== "cryptographic-asset" || !props) {
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (props.assetType === "protocol") {
|
|
191
|
+
protocols.push(comp);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (props.assetType !== "algorithm") {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const groupHalves = GROUP_ALGORITHMS[comp.name];
|
|
198
|
+
if (groupHalves) {
|
|
199
|
+
for (const half of groupHalves) {
|
|
200
|
+
addAlgorithm(
|
|
201
|
+
half,
|
|
202
|
+
keyExchangeProperties(half),
|
|
203
|
+
KEY_AGREEMENT_CURVES[half] ||
|
|
204
|
+
(MLKEM_SECURITY_LEVELS[half] ? "" : half),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const name = oidKey(comp.name);
|
|
210
|
+
if (name) {
|
|
211
|
+
addAlgorithm(
|
|
212
|
+
name,
|
|
213
|
+
MLKEM_SECURITY_LEVELS[name] || KEY_AGREEMENT_CURVES[name]
|
|
214
|
+
? keyExchangeProperties(name)
|
|
215
|
+
: knownAlgorithmProperties(props.algorithmProperties),
|
|
216
|
+
KEY_AGREEMENT_CURVES[name] ||
|
|
217
|
+
(/^(secp|brainpool)/.test(name) ? name : ""),
|
|
218
|
+
);
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const cipher = suiteCipher(comp.name);
|
|
222
|
+
if (cipher) {
|
|
223
|
+
addAlgorithm(cipher.name, cipherProperties(cipher));
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
thoughtLog(`Skipping traced algorithm '${comp.name}' without an OID.`);
|
|
227
|
+
}
|
|
228
|
+
for (const comp of protocols) {
|
|
229
|
+
const ref = `crypto/protocol/${encodeURIComponent(comp.name)}@${encodeURIComponent(comp.version || "unknown")}`;
|
|
230
|
+
const protocolProperties = {
|
|
231
|
+
...comp.cryptoProperties.protocolProperties,
|
|
232
|
+
};
|
|
233
|
+
// The traced suites list algorithm names; the schema wants bom-refs.
|
|
234
|
+
const cipherSuites = (protocolProperties.cipherSuites || []).map(
|
|
235
|
+
({ algorithms: _names, ...suite }) => {
|
|
236
|
+
const cipher = suiteCipher(suite.name);
|
|
237
|
+
return cipher
|
|
238
|
+
? {
|
|
239
|
+
...suite,
|
|
240
|
+
algorithms: [addAlgorithm(cipher.name, cipherProperties(cipher))],
|
|
241
|
+
}
|
|
242
|
+
: suite;
|
|
243
|
+
},
|
|
244
|
+
);
|
|
245
|
+
const existing = byRef.get(ref);
|
|
246
|
+
if (existing) {
|
|
247
|
+
const suites = existing.cryptoProperties.protocolProperties.cipherSuites;
|
|
248
|
+
for (const suite of cipherSuites) {
|
|
249
|
+
if (!suites.some((s) => s.name === suite.name)) {
|
|
250
|
+
suites.push(suite);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
protocolProperties.cipherSuites = cipherSuites;
|
|
256
|
+
const component = {
|
|
257
|
+
type: "cryptographic-asset",
|
|
258
|
+
name: comp.name,
|
|
259
|
+
"bom-ref": ref,
|
|
260
|
+
cryptoProperties: {
|
|
261
|
+
assetType: "protocol",
|
|
262
|
+
protocolProperties,
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
if (comp.version) {
|
|
266
|
+
component.version = comp.version;
|
|
267
|
+
}
|
|
268
|
+
byRef.set(ref, component);
|
|
269
|
+
}
|
|
270
|
+
return Array.from(byRef.values());
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Maps the crypto libraries safer-exec reported to the paths they were loaded
|
|
275
|
+
* from.
|
|
276
|
+
*
|
|
277
|
+
* @param {Object[]} cryptoEntries safer-exec crypto-library and crypto-cipher audit entries
|
|
278
|
+
* @returns {Map<string, {name: string, version: string}>} Crypto libraries by path
|
|
279
|
+
*/
|
|
280
|
+
export function cryptoLibrariesByPath(cryptoEntries) {
|
|
281
|
+
const libraries = new Map();
|
|
282
|
+
for (const entry of cryptoEntries || []) {
|
|
283
|
+
if (entry?.type !== "crypto-library" || !entry.path || !entry.name) {
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
if (!libraries.has(entry.path)) {
|
|
287
|
+
libraries.set(entry.path, {
|
|
288
|
+
name: String(entry.name),
|
|
289
|
+
version: entry.version ? String(entry.version) : "",
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return libraries;
|
|
294
|
+
}
|
package/lib/managers/docker.js
CHANGED
|
@@ -1336,58 +1336,59 @@ export const getImage = async (fullImageName) => {
|
|
|
1336
1336
|
let dockerCmd;
|
|
1337
1337
|
if (needsCliFallback()) {
|
|
1338
1338
|
dockerCmd = getContainerCliCmd();
|
|
1339
|
-
|
|
1340
|
-
//
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
if (needsPull) {
|
|
1356
|
-
result = safeSpawnSync(dockerCmd, ["pull", fullImageName]);
|
|
1357
|
-
if (result.status !== 0 || result.error) {
|
|
1339
|
+
// The local image is checked with the reference itself. `image inspect`
|
|
1340
|
+
// resolves the reference exactly as the pull below would, so a fully
|
|
1341
|
+
// qualified docker.io/library/... name and a digest reference both find
|
|
1342
|
+
// a local image. The `images` listing compared Repository:Tag strings
|
|
1343
|
+
// and missed both. docker, podman, nerdctl and wslc all implement
|
|
1344
|
+
// `image inspect`.
|
|
1345
|
+
let inspectResult = safeSpawnSync(dockerCmd, [
|
|
1346
|
+
"image",
|
|
1347
|
+
"inspect",
|
|
1348
|
+
fullImageName,
|
|
1349
|
+
]);
|
|
1350
|
+
if (inspectResult.status !== 0 || inspectResult.error) {
|
|
1351
|
+
const pullResult = safeSpawnSync(dockerCmd, ["pull", fullImageName]);
|
|
1352
|
+
if (pullResult.status !== 0 || pullResult.error) {
|
|
1358
1353
|
if (isWslcCmd(dockerCmd)) {
|
|
1359
1354
|
// wslc runs Docker Engine inside its own VM, so its errors may
|
|
1360
1355
|
// mention a docker daemon; the Docker Desktop and DOCKER_CMD hints
|
|
1361
1356
|
// below do not apply. Show wslc's own error instead.
|
|
1362
|
-
if (isWslcSessionFailure(
|
|
1357
|
+
if (isWslcSessionFailure(pullResult.stderr)) {
|
|
1363
1358
|
printWslcSessionFailureHint();
|
|
1364
|
-
} else if (
|
|
1365
|
-
console.log(
|
|
1359
|
+
} else if (pullResult.stderr) {
|
|
1360
|
+
console.log(pullResult.stderr);
|
|
1366
1361
|
}
|
|
1367
|
-
} else if (
|
|
1362
|
+
} else if (
|
|
1363
|
+
pullResult.stderr?.includes("docker daemon is not running")
|
|
1364
|
+
) {
|
|
1368
1365
|
console.log(
|
|
1369
1366
|
"Ensure Docker for Desktop is running as an administrator with 'Exposing daemon on TCP without TLS' setting turned on.",
|
|
1370
1367
|
);
|
|
1371
|
-
} else if (
|
|
1368
|
+
} else if (pullResult.stderr?.includes("not found")) {
|
|
1372
1369
|
console.log(
|
|
1373
1370
|
"Set the environment variable DOCKER_CMD to use an alternative command such as nerdctl or podman.",
|
|
1374
1371
|
);
|
|
1375
|
-
} else if (
|
|
1376
|
-
console.log(
|
|
1372
|
+
} else if (pullResult.stderr) {
|
|
1373
|
+
console.log(pullResult.stderr);
|
|
1377
1374
|
}
|
|
1378
1375
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1376
|
+
inspectResult = safeSpawnSync(dockerCmd, [
|
|
1377
|
+
"image",
|
|
1378
|
+
"inspect",
|
|
1379
|
+
fullImageName,
|
|
1380
|
+
]);
|
|
1381
|
+
}
|
|
1382
|
+
if (inspectResult.status !== 0 || inspectResult.error) {
|
|
1383
|
+
if (inspectResult.stderr) {
|
|
1384
|
+
console.log(inspectResult.stderr);
|
|
1384
1385
|
}
|
|
1385
1386
|
suggestWslcAlternative(dockerCmd);
|
|
1386
1387
|
// Continue with the daemon client when the CLI fallback is unavailable
|
|
1387
1388
|
// or unable to inspect the image.
|
|
1388
1389
|
} else {
|
|
1389
1390
|
try {
|
|
1390
|
-
const stdout =
|
|
1391
|
+
const stdout = inspectResult.stdout;
|
|
1391
1392
|
if (stdout) {
|
|
1392
1393
|
const inspectData = JSON.parse(Buffer.from(stdout).toString());
|
|
1393
1394
|
if (inspectData && Array.isArray(inspectData)) {
|
package/lib/managers/oci.js
CHANGED
|
@@ -283,7 +283,10 @@ async function getOciToken(registry, repository, scope) {
|
|
|
283
283
|
if (params.service)
|
|
284
284
|
reqUrl.searchParams.set("service", params.service);
|
|
285
285
|
reqUrl.searchParams.set("scope", `repository:${repository}:${scope}`);
|
|
286
|
+
// Tokens expire within minutes, so a cached one would be replayed
|
|
287
|
+
// after it stopped working.
|
|
286
288
|
const options = {
|
|
289
|
+
cache: false,
|
|
287
290
|
responseType: "json",
|
|
288
291
|
timeout: getOciTimeoutMs(),
|
|
289
292
|
};
|
|
@@ -410,11 +413,18 @@ async function pullBlob(registry, repository, digest, token) {
|
|
|
410
413
|
*
|
|
411
414
|
* @param {string} image OCI image reference (e.g. `"registry.example.com/org/app:tag"`)
|
|
412
415
|
* @param {string} [platform] OCI platform string (e.g. `"linux/amd64"`); no-op for JS implementation
|
|
416
|
+
* @param {Object} [options]
|
|
417
|
+
* @param {function(Buffer): void} [options.checkBytes] Called with the raw bytes of the BOM layer that is about to be returned. An error it throws is rethrown, and no other referrer is tried.
|
|
413
418
|
* @returns {Promise<Object|undefined>} Parsed CycloneDX BOM JSON object, or `undefined` if not found
|
|
414
419
|
*/
|
|
415
|
-
export async function getBomWithOras(
|
|
420
|
+
export async function getBomWithOras(
|
|
421
|
+
image,
|
|
422
|
+
_platform = undefined,
|
|
423
|
+
options = {},
|
|
424
|
+
) {
|
|
416
425
|
const { registry, repository, reference } = parseImageRef(image);
|
|
417
426
|
const token = await getOciToken(registry, repository, "pull");
|
|
427
|
+
let rejection;
|
|
418
428
|
|
|
419
429
|
try {
|
|
420
430
|
const targetManifest = await fetchManifest(
|
|
@@ -488,9 +498,10 @@ export async function getBomWithOras(image, _platform = undefined) {
|
|
|
488
498
|
bomLayer.digest,
|
|
489
499
|
token,
|
|
490
500
|
);
|
|
501
|
+
const text = blob.toString("utf8");
|
|
491
502
|
let bomJson;
|
|
492
503
|
try {
|
|
493
|
-
bomJson = JSON.parse(
|
|
504
|
+
bomJson = JSON.parse(text);
|
|
494
505
|
} catch {
|
|
495
506
|
// Defensive: the mediaType claimed JSON but the layer did not parse
|
|
496
507
|
// (truncated/corrupt blob). Skip it and try the next candidate.
|
|
@@ -509,6 +520,14 @@ export async function getBomWithOras(image, _platform = undefined) {
|
|
|
509
520
|
}
|
|
510
521
|
|
|
511
522
|
if (isCycloneDxBom(bomJson)) {
|
|
523
|
+
if (options.checkBytes) {
|
|
524
|
+
try {
|
|
525
|
+
options.checkBytes(blob);
|
|
526
|
+
} catch (err) {
|
|
527
|
+
rejection = err;
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
512
531
|
return bomJson;
|
|
513
532
|
}
|
|
514
533
|
}
|
|
@@ -517,6 +536,9 @@ export async function getBomWithOras(image, _platform = undefined) {
|
|
|
517
536
|
`Unable to pull the SBOM attachment for ${image} natively! ${e.message}`,
|
|
518
537
|
);
|
|
519
538
|
}
|
|
539
|
+
if (rejection) {
|
|
540
|
+
throw rejection;
|
|
541
|
+
}
|
|
520
542
|
return undefined;
|
|
521
543
|
}
|
|
522
544
|
|
|
@@ -197,3 +197,72 @@ export function resolveCmakeCacheFacts(map) {
|
|
|
197
197
|
fetchContentBase,
|
|
198
198
|
};
|
|
199
199
|
}
|
|
200
|
+
|
|
201
|
+
/** CMake's compiler ids, mapped to the family names the C/C++ toolchain uses. */
|
|
202
|
+
const CMAKE_COMPILER_FAMILIES = {
|
|
203
|
+
GNU: "gcc",
|
|
204
|
+
Clang: "clang",
|
|
205
|
+
AppleClang: "apple-clang",
|
|
206
|
+
MSVC: "msvc",
|
|
207
|
+
IntelLLVM: "icx",
|
|
208
|
+
Intel: "icc",
|
|
209
|
+
NVIDIA: "nvcc",
|
|
210
|
+
NVHPC: "nvhpc",
|
|
211
|
+
PGI: "nvhpc",
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Parse a compiler description CMake writes when it configures a build
|
|
216
|
+
* (`CMakeFiles/<cmake version>/CMake<LANG>Compiler.cmake`): the compiler's
|
|
217
|
+
* path, CMake's id for it and its version, all identified by CMake itself.
|
|
218
|
+
*
|
|
219
|
+
* @param {string} text File contents
|
|
220
|
+
* @returns {{language: string, compiler: string, family: string, id: string, version: string}|null}
|
|
221
|
+
* `null` when the text names no compiler
|
|
222
|
+
*/
|
|
223
|
+
export function parseCmakeCompilerFile(text) {
|
|
224
|
+
if (!text || typeof text !== "string") {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const values = new Map();
|
|
228
|
+
for (const rawLine of text.split(/\r\n|\r|\n/)) {
|
|
229
|
+
const line = rawLine.trim();
|
|
230
|
+
if (!line.startsWith("set(CMAKE_") || !line.endsWith(")")) {
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const body = line.slice(4, -1).trim();
|
|
234
|
+
const space = body.search(/\s/);
|
|
235
|
+
if (space < 0) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const key = body.slice(0, space);
|
|
239
|
+
let value = body.slice(space).trim();
|
|
240
|
+
if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) {
|
|
241
|
+
value = value.slice(1, -1);
|
|
242
|
+
}
|
|
243
|
+
if (!values.has(key)) {
|
|
244
|
+
values.set(key, value);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
for (const [key, compiler] of values) {
|
|
248
|
+
const match = key.match(/^CMAKE_([A-Z]+)_COMPILER$/);
|
|
249
|
+
if (!match || !compiler) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const language = match[1];
|
|
253
|
+
const id = values.get(`CMAKE_${language}_COMPILER_ID`) || "";
|
|
254
|
+
const variant = values.get(`CMAKE_${language}_COMPILER_FRONTEND_VARIANT`);
|
|
255
|
+
let family = CMAKE_COMPILER_FAMILIES[id] || id.toLowerCase() || "unknown";
|
|
256
|
+
if (id === "Clang" && variant === "MSVC") {
|
|
257
|
+
family = "clang-cl";
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
language,
|
|
261
|
+
compiler,
|
|
262
|
+
family,
|
|
263
|
+
id,
|
|
264
|
+
version: values.get(`CMAKE_${language}_COMPILER_VERSION`) || "",
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
@@ -188,10 +188,15 @@ export function parseCmakeCommands(text) {
|
|
|
188
188
|
return commands;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
// Upper bound on an expanded value; a set() chain that doubles a variable can
|
|
192
|
+
// otherwise grow it beyond the maximum string length.
|
|
193
|
+
const MAX_EXPANDED_LENGTH = 65536;
|
|
194
|
+
|
|
191
195
|
/**
|
|
192
196
|
* Replace `${VAR}` references with the values of simple `set()` calls. A
|
|
193
197
|
* reference to an unknown variable is left in place, so callers can tell a
|
|
194
|
-
* resolved value from an unresolved one.
|
|
198
|
+
* resolved value from an unresolved one. Expansion stops at the previous
|
|
199
|
+
* round once the text would exceed 64 KiB.
|
|
195
200
|
*
|
|
196
201
|
* @param {string} value Argument text
|
|
197
202
|
* @param {Map<string,string>} variables Known variable values
|
|
@@ -203,6 +208,9 @@ export function expandCmakeVariables(value, variables) {
|
|
|
203
208
|
let next = "";
|
|
204
209
|
let i = 0;
|
|
205
210
|
while (i < result.length) {
|
|
211
|
+
if (next.length > MAX_EXPANDED_LENGTH) {
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
206
214
|
const start = result.indexOf("${", i);
|
|
207
215
|
if (start === -1) {
|
|
208
216
|
next += result.slice(i);
|
|
@@ -247,7 +255,13 @@ export function cmakeSetVariables(commands) {
|
|
|
247
255
|
rest[0] === "CACHE" ||
|
|
248
256
|
rest[0] === "PARENT_SCOPE"
|
|
249
257
|
) {
|
|
250
|
-
|
|
258
|
+
// An appending set(FOO "${FOO} ...") of an undefined FOO starts from an
|
|
259
|
+
// empty value in CMake; dropping the self-reference also keeps it from
|
|
260
|
+
// being substituted into itself on every later expansion round.
|
|
261
|
+
variables.set(
|
|
262
|
+
key,
|
|
263
|
+
expandCmakeVariables(value, variables).replaceAll(`\${${key}}`, ""),
|
|
264
|
+
);
|
|
251
265
|
}
|
|
252
266
|
}
|
|
253
267
|
return variables;
|