@cdxgen/cdxgen 13.2.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 +33 -14
- package/bin/cdxgen.js +310 -138
- package/bin/evinse.js +22 -0
- package/bin/repl.js +86 -0
- package/bin/sign.js +119 -15
- package/bin/tracebom.js +69 -5
- package/bin/validate.js +107 -19
- package/bin/verify.js +172 -63
- package/data/helpers/init.gradle +46 -21
- package/data/remediations.json +85 -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/bomAssembly.js +105 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +156 -58
- package/lib/cli/jsBom.js +99 -53
- package/lib/cli/jvmBom.js +708 -147
- package/lib/cli/managedBom.js +415 -224
- package/lib/cli/nativeBom.js +264 -44
- package/lib/core/activity.js +12 -0
- package/lib/core/env.js +22 -15
- package/lib/core/fs.js +176 -7
- package/lib/core/httpClient.js +159 -24
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/pluginRun.js +184 -0
- package/lib/core/processTree.js +100 -0
- package/lib/core/projectConfig.js +31 -4
- 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 +274 -108
- package/lib/ecosystems/cargoCache.js +231 -8
- package/lib/ecosystems/caxa.js +434 -2
- package/lib/ecosystems/cmakeResolver.js +76 -21
- package/lib/ecosystems/cppBuildContext.js +631 -0
- package/lib/ecosystems/cppEvidence.js +399 -51
- package/lib/ecosystems/cppIncludeResolver.js +294 -0
- package/lib/ecosystems/denoutils.js +93 -66
- package/lib/ecosystems/ecosystems.js +1983 -793
- package/lib/ecosystems/golem.js +28 -12
- package/lib/ecosystems/gradleutils.js +356 -37
- package/lib/ecosystems/jsEvidence.js +104 -44
- package/lib/ecosystems/kosi.js +31 -7
- package/lib/ecosystems/npmutils.js +174 -9
- package/lib/ecosystems/parsers-dotnet.js +616 -159
- package/lib/ecosystems/parsers-elm.js +46 -28
- package/lib/ecosystems/parsers-gleam.js +3 -4
- package/lib/ecosystems/parsers-go.js +371 -70
- package/lib/ecosystems/parsers-js.js +263 -76
- package/lib/ecosystems/parsers-julia.js +2 -3
- package/lib/ecosystems/parsers-jvm.js +204 -12
- package/lib/ecosystems/parsers-misc.js +929 -216
- package/lib/ecosystems/parsers-python.js +167 -24
- package/lib/ecosystems/parsers-rust.js +28 -22
- 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 +1492 -182
- package/lib/ecosystems/rusi.js +54 -10
- 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 +1008 -191
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +2 -2
- package/lib/helpers/bomSigner.js +1101 -222
- package/lib/inventory/aiCollector.js +4 -1
- package/lib/inventory/analyzer.js +110 -127
- package/lib/inventory/atomUtils.js +703 -43
- package/lib/inventory/auditCategories.js +7 -0
- package/lib/inventory/buildToolRateLimit.js +144 -0
- package/lib/inventory/cacheCommands.js +2 -1
- package/lib/inventory/cdxrs.js +103 -12
- package/lib/inventory/cmakeBuildDirs.js +254 -0
- package/lib/inventory/deps.js +501 -124
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +798 -57
- package/lib/inventory/dosaiParsers.js +310 -15
- package/lib/inventory/dynamic.js +90 -6
- package/lib/inventory/evidenceUtils.js +166 -113
- package/lib/inventory/fetchBatch.js +690 -55
- 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/protobom.js +8 -1
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +3 -4
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/scalaCoords.js +153 -0
- package/lib/inventory/source.js +45 -27
- package/lib/inventory/spdx.js +101 -0
- package/lib/inventory/traceRunner.js +295 -25
- package/lib/inventory/tracedCrypto.js +294 -0
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +467 -46
- package/lib/managers/oci.js +63 -3
- package/lib/parsers/cmakeCache.js +69 -0
- package/lib/parsers/cmakeLists.js +466 -0
- package/lib/parsers/cmakePresets.js +400 -0
- package/lib/parsers/compileCommands.js +557 -0
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- 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 +83 -16
- package/lib/stages/postgen/specVersionCompat.js +32 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +64 -11
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +2 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +23 -23
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +2 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +33 -1
- package/types/lib/cli/cliOptions.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 +55 -0
- 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/metadataProperties.d.ts +39 -0
- package/types/lib/core/metadataProperties.d.ts.map +1 -0
- 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/projectConfig.d.ts +6 -2
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- 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 +12 -5
- 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/caxa.d.ts +14 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +28 -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 +17 -1
- 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 +70 -4
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +13 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmutils.d.ts +57 -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 +54 -2
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +16 -5
- 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 +70 -4
- 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-rust.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 +164 -3
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts +13 -0
- 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 +132 -15
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +280 -15
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +182 -13
- 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 +219 -5
- 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/cdxrs.d.ts +8 -3
- package/types/lib/inventory/cdxrs.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/depsUtils.d.ts +15 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +121 -9
- 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/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/purl.d.ts +71 -2
- package/types/lib/inventory/purl.d.ts.map +1 -1
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.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 +59 -1
- 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/binary.d.ts +30 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +29 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +17 -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 +77 -0
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
- 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/largeJson.d.ts +52 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +5 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -1
- 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/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +18 -1
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
package/bin/verify.js
CHANGED
|
@@ -13,13 +13,19 @@ import {
|
|
|
13
13
|
retrieveCdxgenVersion,
|
|
14
14
|
safeExistsSync,
|
|
15
15
|
} from "../lib/ecosystems/utils.js";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
displayValue,
|
|
18
|
+
loadSharedSecret,
|
|
19
|
+
loadVerificationKey,
|
|
20
|
+
verifyBomSignatures,
|
|
21
|
+
} from "../lib/helpers/bomSigner.js";
|
|
17
22
|
import {
|
|
18
23
|
getNonCycloneDxErrorMessage,
|
|
19
24
|
isCycloneDxBom,
|
|
20
25
|
} from "../lib/inventory/bomUtils.js";
|
|
21
26
|
import { isProtoBomPath } from "../lib/inventory/protobomLoader.js";
|
|
22
27
|
import { getBomWithOras } from "../lib/managers/oci.js";
|
|
28
|
+
import { parseStrictJson } from "../lib/parsers/strictJson.js";
|
|
23
29
|
|
|
24
30
|
const dirName = dirNameStr;
|
|
25
31
|
|
|
@@ -36,14 +42,33 @@ const args = _yargs
|
|
|
36
42
|
description: "The platform to validate. No default",
|
|
37
43
|
})
|
|
38
44
|
.option("public-key", {
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
type: "array",
|
|
46
|
+
string: true,
|
|
47
|
+
description:
|
|
48
|
+
"Trusted public key in PEM format. Repeat to trust several keys; a signature entry verifies when one of them verifies it.",
|
|
49
|
+
})
|
|
50
|
+
.option("secret-key", {
|
|
51
|
+
type: "string",
|
|
52
|
+
description:
|
|
53
|
+
"File holding the shared secret for HMAC (HS256, HS384, HS512) signatures. HMAC signatures are only accepted with this option.",
|
|
41
54
|
})
|
|
42
55
|
.option("deep", {
|
|
43
56
|
type: "boolean",
|
|
44
57
|
default: true,
|
|
45
58
|
description:
|
|
46
|
-
"Strictly verify all nested component, service, and annotation signatures against the provided
|
|
59
|
+
"Strictly verify all nested component, service, and annotation signatures against the provided keys. Pass --no-deep to verify only the root signature.",
|
|
60
|
+
})
|
|
61
|
+
.option("require-all", {
|
|
62
|
+
type: "boolean",
|
|
63
|
+
default: false,
|
|
64
|
+
description:
|
|
65
|
+
"Require every entry of a signers or chain signature to verify with one of the keys, instead of at least one.",
|
|
66
|
+
})
|
|
67
|
+
.option("allow-nested-only", {
|
|
68
|
+
type: "boolean",
|
|
69
|
+
default: false,
|
|
70
|
+
description:
|
|
71
|
+
"Accept a BOM without a root signature when every component, service, and annotation carries a signature that verifies. No signature then covers the other members of the BOM.",
|
|
47
72
|
})
|
|
48
73
|
.completion("completion", "Generate bash/zsh completion")
|
|
49
74
|
.epilogue("for documentation, visit https://cdxgen.github.io/cdxgen")
|
|
@@ -87,7 +112,7 @@ async function getBom(args) {
|
|
|
87
112
|
process.exit(1);
|
|
88
113
|
}
|
|
89
114
|
try {
|
|
90
|
-
return
|
|
115
|
+
return parseStrictJson(fs.readFileSync(args.input));
|
|
91
116
|
} catch (error) {
|
|
92
117
|
console.log(`Failed to parse '${args.input}': ${error.message}`);
|
|
93
118
|
process.exit(1);
|
|
@@ -98,11 +123,32 @@ async function getBom(args) {
|
|
|
98
123
|
args.input.includes("docker") ||
|
|
99
124
|
args.input.includes("ghcr")
|
|
100
125
|
) {
|
|
101
|
-
|
|
126
|
+
try {
|
|
127
|
+
return await getBomWithOras(args.input, args.platform, {
|
|
128
|
+
checkBytes: parseStrictJson,
|
|
129
|
+
});
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.log(
|
|
132
|
+
`Failed to parse the BOM attached to '${args.input}': ${error.message}`,
|
|
133
|
+
);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
102
136
|
}
|
|
103
137
|
return undefined;
|
|
104
138
|
}
|
|
105
139
|
|
|
140
|
+
const publicKeyFiles = args.publicKey || [];
|
|
141
|
+
if (publicKeyFiles.length && args.secretKey) {
|
|
142
|
+
console.log("Use either --public-key or --secret-key, not both.");
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
if (!publicKeyFiles.length && !args.secretKey) {
|
|
146
|
+
console.log(
|
|
147
|
+
"Name the trusted key with --public-key <file>, or --secret-key <file> for HMAC signatures.",
|
|
148
|
+
);
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
|
|
106
152
|
const bomJson = await getBom(args);
|
|
107
153
|
|
|
108
154
|
if (!bomJson) {
|
|
@@ -114,76 +160,139 @@ if (!isCycloneDxBom(bomJson)) {
|
|
|
114
160
|
process.exit(1);
|
|
115
161
|
}
|
|
116
162
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
163
|
+
const verificationKeys = [];
|
|
164
|
+
for (const keyFile of args.secretKey ? [args.secretKey] : publicKeyFiles) {
|
|
165
|
+
if (!safeExistsSync(keyFile)) {
|
|
166
|
+
console.log(
|
|
167
|
+
args.secretKey
|
|
168
|
+
? `Shared secret '${keyFile}' for signature verification is missing!`
|
|
169
|
+
: `Public key '${keyFile}' for signature verification is missing!`,
|
|
170
|
+
);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
verificationKeys.push(
|
|
175
|
+
args.secretKey
|
|
176
|
+
? loadSharedSecret(fs.readFileSync(keyFile))
|
|
177
|
+
: loadVerificationKey(fs.readFileSync(keyFile, "utf8")),
|
|
178
|
+
);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.log(`Unable to use '${keyFile}': ${error.message}`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
120
183
|
}
|
|
121
184
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
185
|
+
function describeEntry(node, entry) {
|
|
186
|
+
const parts = [displayValue(entry.algorithm)];
|
|
187
|
+
if (entry.keyId !== undefined) {
|
|
188
|
+
parts.push(`keyId '${displayValue(entry.keyId)}'`);
|
|
189
|
+
}
|
|
190
|
+
const position =
|
|
191
|
+
node.form === "single" ? "" : `${node.form}[${entry.index}] `;
|
|
192
|
+
return `${position}(${parts.join(", ")})`;
|
|
127
193
|
}
|
|
128
194
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (verifyNested) {
|
|
134
|
-
for (const comp of bomJson.components || []) {
|
|
135
|
-
if (comp.signature) {
|
|
136
|
-
checkedNested++;
|
|
137
|
-
if (!verifyNode(comp, publicKeyStr)) {
|
|
138
|
-
console.log(
|
|
139
|
-
`Component '${comp["bom-ref"] || comp.name}' signature is invalid!`,
|
|
140
|
-
);
|
|
141
|
-
hasInvalidNested = true;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
195
|
+
function printRejections(node) {
|
|
196
|
+
for (const reason of node.reasons) {
|
|
197
|
+
console.log(` - ${reason}`);
|
|
144
198
|
}
|
|
145
|
-
for (const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
`Service '${svc["bom-ref"] || svc.name}' signature is invalid!`,
|
|
151
|
-
);
|
|
152
|
-
hasInvalidNested = true;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
for (const ann of bomJson.annotations || []) {
|
|
157
|
-
if (ann.signature) {
|
|
158
|
-
checkedNested++;
|
|
159
|
-
if (!verifyNode(ann, publicKeyStr)) {
|
|
160
|
-
console.log(
|
|
161
|
-
`Annotation '${ann["bom-ref"] || ann.subject}' signature is invalid!`,
|
|
162
|
-
);
|
|
163
|
-
hasInvalidNested = true;
|
|
164
|
-
}
|
|
199
|
+
for (const entry of node.entries) {
|
|
200
|
+
for (const reason of entry.reasons) {
|
|
201
|
+
console.log(
|
|
202
|
+
` - ${node.form === "single" ? "" : `${node.form}[${entry.index}]: `}${reason}`,
|
|
203
|
+
);
|
|
165
204
|
}
|
|
166
205
|
}
|
|
167
206
|
}
|
|
168
207
|
|
|
169
|
-
|
|
208
|
+
const report = verifyBomSignatures(bomJson, verificationKeys, {
|
|
209
|
+
deep: args.deep,
|
|
210
|
+
allowNestedOnly: args.allowNestedOnly,
|
|
211
|
+
requireAll: args.requireAll,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
for (const error of report.errors) {
|
|
215
|
+
console.log(error);
|
|
216
|
+
}
|
|
217
|
+
const keyFiles = args.secretKey ? [args.secretKey] : publicKeyFiles;
|
|
218
|
+
if (args.requireAll && report.root?.form) {
|
|
219
|
+
report.root.keysMatched.forEach((matched, index) => {
|
|
220
|
+
if (!matched) {
|
|
221
|
+
console.log(` - key #${index + 1}: ${keyFiles[index]}`);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const invalidNested = report.nested.filter((item) => !item.verified);
|
|
226
|
+
for (const item of invalidNested) {
|
|
227
|
+
console.log(`${item.label} signature is invalid!`);
|
|
228
|
+
printRejections(item);
|
|
229
|
+
}
|
|
230
|
+
if (invalidNested.length) {
|
|
170
231
|
console.log("One or more nested signatures are invalid!");
|
|
232
|
+
if (report.root?.verified) {
|
|
233
|
+
console.log(
|
|
234
|
+
"The root signature verifies with this key. Nested signatures belong to the party that created them, so pass --no-deep to verify only the root signature with this key.",
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (report.root && !report.root.verified) {
|
|
239
|
+
console.log("BOM signature is invalid!");
|
|
240
|
+
printRejections(report.root);
|
|
241
|
+
if (!report.root.reasons.length && !report.root.match) {
|
|
242
|
+
console.log(
|
|
243
|
+
"If this BOM was signed with cdxgen 13.2.0 or earlier, re-sign it with this version of cdx-sign.",
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (!report.root && !bomJson.signature && !args.allowNestedOnly) {
|
|
248
|
+
console.log(
|
|
249
|
+
"Pass --allow-nested-only to accept a BOM whose components, services, and annotations are all signed.",
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
for (const notice of report.notices) {
|
|
253
|
+
console.log(`Note: ${notice}`);
|
|
254
|
+
}
|
|
255
|
+
if (!report.verified) {
|
|
171
256
|
process.exit(1);
|
|
172
257
|
}
|
|
173
258
|
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
259
|
+
if (report.root) {
|
|
260
|
+
const rootMatch = report.root.match;
|
|
261
|
+
const identifier = rootMatch.keyId
|
|
262
|
+
? `KeyId: '${displayValue(rootMatch.keyId)}'`
|
|
263
|
+
: `Algorithm: '${rootMatch.algorithm}'`;
|
|
264
|
+
console.log(`✓ Signature is valid! (Matched ${identifier})`);
|
|
265
|
+
const unverified = report.root.entries.filter((entry) => !entry.verified);
|
|
266
|
+
if (unverified.length) {
|
|
267
|
+
console.log(
|
|
268
|
+
`Warning: ${unverified.length} of ${report.root.entries.length} ${report.root.form} entries did not verify with the given key(s). Pass --require-all to fail in that case.`,
|
|
269
|
+
);
|
|
270
|
+
for (const entry of unverified) {
|
|
271
|
+
console.log(
|
|
272
|
+
` - ${describeEntry(report.root, entry)}: ${entry.reasons[0] || "not verified"}`,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
183
275
|
}
|
|
184
|
-
} else if (checkedNested > 0 && !hasInvalidNested) {
|
|
185
|
-
console.log(`✓ ${checkedNested} nested signature(s) are valid!`);
|
|
186
276
|
} else {
|
|
187
|
-
console.log(
|
|
188
|
-
|
|
277
|
+
console.log(`✓ ${report.nested.length} nested signature(s) are valid!`);
|
|
278
|
+
}
|
|
279
|
+
const MAX_LISTED_ENTRIES = 10;
|
|
280
|
+
const unverifiedNested = report.nested.flatMap((item) =>
|
|
281
|
+
item.entries
|
|
282
|
+
.filter((entry) => !entry.verified)
|
|
283
|
+
.map(
|
|
284
|
+
(entry) =>
|
|
285
|
+
`${item.label} ${describeEntry(item, entry)}: ${entry.reasons[0] || "not verified"}`,
|
|
286
|
+
),
|
|
287
|
+
);
|
|
288
|
+
if (unverifiedNested.length) {
|
|
289
|
+
console.log(
|
|
290
|
+
`Warning: ${unverifiedNested.length} nested signature entries did not verify with the given key(s). Pass --require-all to fail in that case.`,
|
|
291
|
+
);
|
|
292
|
+
for (const line of unverifiedNested.slice(0, MAX_LISTED_ENTRIES)) {
|
|
293
|
+
console.log(` - ${line}`);
|
|
294
|
+
}
|
|
295
|
+
if (unverifiedNested.length > MAX_LISTED_ENTRIES) {
|
|
296
|
+
console.log(` - and ${unverifiedNested.length - MAX_LISTED_ENTRIES} more`);
|
|
297
|
+
}
|
|
189
298
|
}
|
package/data/helpers/init.gradle
CHANGED
|
@@ -17,14 +17,51 @@ allprojects {
|
|
|
17
17
|
// attached to the matching component instead of guessing the first repository.
|
|
18
18
|
if (project.hasProperty('cdxgenResolveDistribution') &&
|
|
19
19
|
project.property('cdxgenResolveDistribution').toString() == 'true') {
|
|
20
|
-
|
|
20
|
+
// Shared by every project in the build, so a dependency declared in
|
|
21
|
+
// many subprojects is probed once, and a repository host that
|
|
22
|
+
// answers 429 is left alone for the rest of the build.
|
|
23
|
+
if (!gradle.ext.has('cdxgenResolvedKeys')) {
|
|
24
|
+
gradle.ext.cdxgenResolvedKeys = java.util.concurrent.ConcurrentHashMap.newKeySet()
|
|
25
|
+
gradle.ext.cdxgenPausedHosts = java.util.concurrent.ConcurrentHashMap.newKeySet()
|
|
26
|
+
}
|
|
27
|
+
def resolvedKeys = gradle.ext.cdxgenResolvedKeys
|
|
28
|
+
def pausedHosts = gradle.ext.cdxgenPausedHosts
|
|
29
|
+
// A HEAD request answers whether the repository serves the file
|
|
30
|
+
// without downloading it. Other URL schemes, such as file:, are
|
|
31
|
+
// opened and closed.
|
|
32
|
+
def artifactExists = { String artifactUrl ->
|
|
33
|
+
try {
|
|
34
|
+
def url = new URL(artifactUrl)
|
|
35
|
+
if (url.protocol == 'http' || url.protocol == 'https') {
|
|
36
|
+
if (pausedHosts.contains(url.host)) {
|
|
37
|
+
return false
|
|
38
|
+
}
|
|
39
|
+
def connection = (HttpURLConnection) url.openConnection()
|
|
40
|
+
connection.requestMethod = 'HEAD'
|
|
41
|
+
connection.instanceFollowRedirects = true
|
|
42
|
+
connection.connectTimeout = 10000
|
|
43
|
+
connection.readTimeout = 10000
|
|
44
|
+
def code = connection.responseCode
|
|
45
|
+
connection.disconnect()
|
|
46
|
+
if (code == 429) {
|
|
47
|
+
pausedHosts.add(url.host)
|
|
48
|
+
}
|
|
49
|
+
return code >= 200 && code < 300
|
|
50
|
+
}
|
|
51
|
+
def stream = url.openStream()
|
|
52
|
+
stream.close()
|
|
53
|
+
return true
|
|
54
|
+
} catch (Exception ignored) {
|
|
55
|
+
return false
|
|
56
|
+
}
|
|
57
|
+
}
|
|
21
58
|
project.configurations.each { configuration ->
|
|
22
59
|
configuration.dependencies.each { dependency ->
|
|
23
60
|
if (dependency.group == null || dependency.version == null) {
|
|
24
61
|
return
|
|
25
62
|
}
|
|
26
63
|
def depKey = String.format("%s:%s:%s", dependency.group, dependency.name, dependency.version)
|
|
27
|
-
if (resolvedKeys.
|
|
64
|
+
if (!resolvedKeys.add(depKey)) {
|
|
28
65
|
return
|
|
29
66
|
}
|
|
30
67
|
def groupPath = dependency.group.replace('.', '/')
|
|
@@ -40,28 +77,16 @@ allprojects {
|
|
|
40
77
|
def jarUrl = String.format("%s%s/%s/%s/%s-%s.jar", urlString,
|
|
41
78
|
groupPath, dependency.name, dependency.version,
|
|
42
79
|
dependency.name, dependency.version)
|
|
80
|
+
if (artifactExists(jarUrl)) {
|
|
81
|
+
println "<CDXGEN:distribution>:" + depKey + " -> " + jarUrl
|
|
82
|
+
break
|
|
83
|
+
}
|
|
43
84
|
def aarUrl = String.format("%s%s/%s/%s/%s-%s.aar", urlString,
|
|
44
85
|
groupPath, dependency.name, dependency.version,
|
|
45
86
|
dependency.name, dependency.version)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
inStreamJar.close()
|
|
50
|
-
println "<CDXGEN:distribution>:" + depKey + " -> " + jarUrl
|
|
51
|
-
resolvedKeys.add(depKey)
|
|
52
|
-
break
|
|
53
|
-
}
|
|
54
|
-
} catch (Exception ignored) {
|
|
55
|
-
}
|
|
56
|
-
try {
|
|
57
|
-
def inStreamAar = new URL(aarUrl).openStream()
|
|
58
|
-
if (inStreamAar != null) {
|
|
59
|
-
inStreamAar.close()
|
|
60
|
-
println "<CDXGEN:distribution>:" + depKey + " -> " + aarUrl
|
|
61
|
-
resolvedKeys.add(depKey)
|
|
62
|
-
break
|
|
63
|
-
}
|
|
64
|
-
} catch (Exception ignored) {
|
|
87
|
+
if (artifactExists(aarUrl)) {
|
|
88
|
+
println "<CDXGEN:distribution>:" + depKey + " -> " + aarUrl
|
|
89
|
+
break
|
|
65
90
|
}
|
|
66
91
|
}
|
|
67
92
|
}
|
package/data/remediations.json
CHANGED
|
@@ -263,6 +263,46 @@
|
|
|
263
263
|
},
|
|
264
264
|
"docs": "https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES?id=java"
|
|
265
265
|
},
|
|
266
|
+
"build.rate-limited": {
|
|
267
|
+
"ecosystem": "generic",
|
|
268
|
+
"kind": "evidence.degraded",
|
|
269
|
+
"impact": "transitive-deps",
|
|
270
|
+
"title": "A build tool's repository rate limited the machine, so its dependency tree did not resolve",
|
|
271
|
+
"targetTier": null,
|
|
272
|
+
"actions": [
|
|
273
|
+
{
|
|
274
|
+
"kind": "config",
|
|
275
|
+
"command": "Maven: declare a <mirror> in the Maven settings.xml, or set MVNW_REPOURL for the Maven wrapper"
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"kind": "config",
|
|
279
|
+
"command": "Gradle: declare the mirror repository in a Gradle init script"
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"kind": "config",
|
|
283
|
+
"command": "sbt: list the mirror in ~/.sbt/repositories and pass -Dsbt.override.build.repos=true in SBT_OPTS or .sbtopts"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"kind": "config",
|
|
287
|
+
"command": "Mill and scala-cli: set central.from and central.to in Coursier's mirror.properties, separating several central.from URLs with semicolons"
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
"kind": "env",
|
|
291
|
+
"command": "Point MAVEN_CENTRAL_URL at a mirror for cdxgen's own lookups"
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
"kind": "rerun"
|
|
295
|
+
}
|
|
296
|
+
],
|
|
297
|
+
"verify": {
|
|
298
|
+
"rules": [],
|
|
299
|
+
"expectTier": null,
|
|
300
|
+
"eventsCleared": [
|
|
301
|
+
"build.rate-limited"
|
|
302
|
+
]
|
|
303
|
+
},
|
|
304
|
+
"docs": "https://cyclonedx.github.io/cdxgen/#/ADVANCED"
|
|
305
|
+
},
|
|
266
306
|
"js.no-lockfile": {
|
|
267
307
|
"ecosystem": "npm",
|
|
268
308
|
"kind": "evidence.degraded",
|
|
@@ -709,6 +749,30 @@
|
|
|
709
749
|
},
|
|
710
750
|
"docs": "https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES"
|
|
711
751
|
},
|
|
752
|
+
"dosai.semantics.persist-failed": {
|
|
753
|
+
"ecosystem": "csharp",
|
|
754
|
+
"kind": "evidence.degraded",
|
|
755
|
+
"impact": "none",
|
|
756
|
+
"title": "The combined dosai report could not be written to the semantics slices file; the SBOM evidence is unaffected",
|
|
757
|
+
"targetTier": "resolved",
|
|
758
|
+
"actions": [
|
|
759
|
+
{
|
|
760
|
+
"kind": "config",
|
|
761
|
+
"detail": "Check that the --semantics-slices-file path is writable and has room for a report the size of the dosai slices."
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
"kind": "rerun"
|
|
765
|
+
}
|
|
766
|
+
],
|
|
767
|
+
"verify": {
|
|
768
|
+
"rules": [],
|
|
769
|
+
"expectTier": "resolved",
|
|
770
|
+
"eventsCleared": [
|
|
771
|
+
"dosai.semantics.persist-failed"
|
|
772
|
+
]
|
|
773
|
+
},
|
|
774
|
+
"docs": "https://cyclonedx.github.io/cdxgen/#/EVINSE"
|
|
775
|
+
},
|
|
712
776
|
"cocoa.pods.missing": {
|
|
713
777
|
"ecosystem": "cocoa",
|
|
714
778
|
"kind": "fallback.engaged",
|
|
@@ -1163,5 +1227,26 @@
|
|
|
1163
1227
|
]
|
|
1164
1228
|
},
|
|
1165
1229
|
"docs": "https://cyclonedx.github.io/cdxgen/#/ENV"
|
|
1230
|
+
},
|
|
1231
|
+
"policy.rate-limited": {
|
|
1232
|
+
"ecosystem": "generic",
|
|
1233
|
+
"kind": "evidence.degraded",
|
|
1234
|
+
"impact": "licenses",
|
|
1235
|
+
"title": "A registry rate limited this run, so its metadata came from local caches only",
|
|
1236
|
+
"targetTier": null,
|
|
1237
|
+
"actions": [
|
|
1238
|
+
{
|
|
1239
|
+
"kind": "env",
|
|
1240
|
+
"command": "Point MAVEN_CENTRAL_URL at a mirror or repository manager, or wait for the block to expire, then re-run"
|
|
1241
|
+
}
|
|
1242
|
+
],
|
|
1243
|
+
"verify": {
|
|
1244
|
+
"rules": [],
|
|
1245
|
+
"expectTier": null,
|
|
1246
|
+
"eventsCleared": [
|
|
1247
|
+
"policy.rate-limited"
|
|
1248
|
+
]
|
|
1249
|
+
},
|
|
1250
|
+
"docs": "https://cyclonedx.github.io/cdxgen/#/ENV"
|
|
1166
1251
|
}
|
|
1167
1252
|
}
|
|
@@ -234,3 +234,70 @@
|
|
|
234
234
|
"registry": $prop($, 'cdx:pypi:registry'),
|
|
235
235
|
"resolvedFrom": $prop($, 'cdx:pypi:resolved_from')
|
|
236
236
|
}
|
|
237
|
+
- id: PKG-012
|
|
238
|
+
name: "Terraform module from mutable VCS reference"
|
|
239
|
+
description: "Terraform modules sourced from git or mercurial without a commit or tag pin are not locked, so the next init can fetch different code"
|
|
240
|
+
severity: high
|
|
241
|
+
category: dependency-source
|
|
242
|
+
dry-run-support: full
|
|
243
|
+
condition: |
|
|
244
|
+
components[
|
|
245
|
+
$hasProp($, 'cdx:tf:kind') and $prop($, 'cdx:tf:kind') = 'module'
|
|
246
|
+
and $hasProp($, 'cdx:tf:module:sourceType')
|
|
247
|
+
and ($prop($, 'cdx:tf:module:sourceType') = 'git' or $prop($, 'cdx:tf:module:sourceType') = 'hg')
|
|
248
|
+
and ($prop($, 'cdx:tf:module:pinning') = 'branch' or $prop($, 'cdx:tf:module:pinning') = 'none')
|
|
249
|
+
]
|
|
250
|
+
location: |
|
|
251
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
252
|
+
message: "Terraform module '{{ name }}@{{ version }}' tracks VCS source '{{ $prop($, 'cdx:tf:module:source') }}' at a mutable reference"
|
|
253
|
+
mitigation: "Pin the module source with `?ref=<commit sha>` or a reviewed, signed tag; the dependency lock file does not cover modules"
|
|
254
|
+
evidence: |
|
|
255
|
+
{
|
|
256
|
+
"source": $prop($, 'cdx:tf:module:source'),
|
|
257
|
+
"sourceType": $prop($, 'cdx:tf:module:sourceType'),
|
|
258
|
+
"ref": $prop($, 'cdx:tf:module:ref'),
|
|
259
|
+
"pinning": $prop($, 'cdx:tf:module:pinning')
|
|
260
|
+
}
|
|
261
|
+
- id: PKG-013
|
|
262
|
+
name: "Terraform registry module not pinned to an exact version"
|
|
263
|
+
description: "Registry modules whose version argument is a range or absent resolve to whatever the registry serves at the next init"
|
|
264
|
+
severity: low
|
|
265
|
+
category: dependency-source
|
|
266
|
+
dry-run-support: full
|
|
267
|
+
condition: |
|
|
268
|
+
components[
|
|
269
|
+
$hasProp($, 'cdx:tf:kind') and $prop($, 'cdx:tf:kind') = 'module'
|
|
270
|
+
and $hasProp($, 'cdx:tf:module:sourceType') and $prop($, 'cdx:tf:module:sourceType') = 'registry'
|
|
271
|
+
and ($prop($, 'cdx:tf:module:pinning') = 'range' or $prop($, 'cdx:tf:module:pinning') = 'none')
|
|
272
|
+
]
|
|
273
|
+
location: |
|
|
274
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
275
|
+
message: "Terraform registry module '{{ $prop($, 'cdx:tf:address') }}' is not pinned to an exact version (pinning: {{ $prop($, 'cdx:tf:module:pinning') }})"
|
|
276
|
+
mitigation: "Set the module's `version` argument to a single exact version and review upgrades deliberately"
|
|
277
|
+
evidence: |
|
|
278
|
+
{
|
|
279
|
+
"constraints": $prop($, 'cdx:tf:constraints'),
|
|
280
|
+
"pinning": $prop($, 'cdx:tf:module:pinning'),
|
|
281
|
+
"address": $prop($, 'cdx:tf:address')
|
|
282
|
+
}
|
|
283
|
+
- id: PKG-014
|
|
284
|
+
name: "Terraform module source embeds credentials"
|
|
285
|
+
description: "A module source string carries a credential such as URL userinfo, a git sshkey parameter or S3 keys, exposing the secret to every reader of the configuration"
|
|
286
|
+
severity: high
|
|
287
|
+
category: dependency-source
|
|
288
|
+
dry-run-support: full
|
|
289
|
+
condition: |
|
|
290
|
+
components[
|
|
291
|
+
$hasProp($, 'cdx:tf:module:credentialInSource')
|
|
292
|
+
and $prop($, 'cdx:tf:module:credentialInSource') = 'true'
|
|
293
|
+
]
|
|
294
|
+
location: |
|
|
295
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
296
|
+
message: "Terraform module '{{ name }}@{{ version }}' resolves from a source that embeds credentials"
|
|
297
|
+
mitigation: "Rotate the exposed credential, move authentication to git credential helpers or an SSH agent, and keep the source URL credential-free"
|
|
298
|
+
evidence: |
|
|
299
|
+
{
|
|
300
|
+
"source": $prop($, 'cdx:tf:module:source'),
|
|
301
|
+
"sourceType": $prop($, 'cdx:tf:module:sourceType'),
|
|
302
|
+
"credentialInSource": $prop($, 'cdx:tf:module:credentialInSource')
|
|
303
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Scala Evinse and scalasem Rules
|
|
2
|
+
# Categories: scala-security, scala-compliance
|
|
3
|
+
# Evaluates cdx:scalasem:* properties and the crypto assets emitted by
|
|
4
|
+
# evinse -l scala and cdxgen --evidence for Scala projects.
|
|
5
|
+
|
|
6
|
+
- id: SCALA-SEC-001
|
|
7
|
+
name: "Scala source uses a weak cryptographic algorithm in runtime code"
|
|
8
|
+
description: "scalasem flagged a weak algorithm, mode or key size at a call site in main or generated sources (MD5, SHA-1, DES, ECB, or a key below the modern floor)"
|
|
9
|
+
severity: medium
|
|
10
|
+
category: scala-security
|
|
11
|
+
dry-run-support: full
|
|
12
|
+
condition: |
|
|
13
|
+
components[
|
|
14
|
+
type = 'cryptographic-asset'
|
|
15
|
+
and $prop($, 'cdx:scalasem:crypto:weak') = 'true'
|
|
16
|
+
and (
|
|
17
|
+
'main' in properties[name = 'cdx:scalasem:usageScopes'].value
|
|
18
|
+
or 'generated' in properties[name = 'cdx:scalasem:usageScopes'].value
|
|
19
|
+
)
|
|
20
|
+
]
|
|
21
|
+
location: |
|
|
22
|
+
{ "bomRef": $."bom-ref", "name": name }
|
|
23
|
+
message: "Weak cryptographic algorithm '{{ name }}' used in Scala source"
|
|
24
|
+
mitigation: "Move to the modern equivalent of this algorithm, mode and key size, and confirm no compatibility constraint forces the weak one. The locations name the exact call sites"
|
|
25
|
+
evidence: |
|
|
26
|
+
{
|
|
27
|
+
"algorithm": name,
|
|
28
|
+
"oid": cryptoProperties.oid,
|
|
29
|
+
"api": $prop($, 'cdx:scalasem:crypto:api'),
|
|
30
|
+
"locations": evidence.occurrences.(location & '#' & line)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
- id: SCALA-SEC-002
|
|
34
|
+
name: "Scala source calls an outbound service over plaintext transport"
|
|
35
|
+
description: "scalasem found an outbound HTTP or websocket client whose target URL starts with http:// or ws:// and is not a loopback address, so traffic and credentials cross the network unencrypted"
|
|
36
|
+
severity: medium
|
|
37
|
+
category: scala-security
|
|
38
|
+
dry-run-support: full
|
|
39
|
+
condition: |
|
|
40
|
+
services[
|
|
41
|
+
$exists($filter(endpoints, function($e) {
|
|
42
|
+
($contains($e, 'http://') or $contains($e, 'ws://'))
|
|
43
|
+
and $not($contains($e, /:\/\/(localhost|127\.[0-9.]+|\[::1\])(:|\/|$)/))
|
|
44
|
+
}))
|
|
45
|
+
and $prop($, 'cdx:scalasem:service:kind') != null
|
|
46
|
+
]
|
|
47
|
+
location: |
|
|
48
|
+
{ "name": name, "endpoints": endpoints }
|
|
49
|
+
message: "Outbound service '{{ name }}' is called over plaintext"
|
|
50
|
+
mitigation: "Switch the target to https:// or wss://, or a local socket if the peer is on the same host. The call sites are named by the service's cdx:scalasem:service:location properties"
|
|
51
|
+
evidence: |
|
|
52
|
+
{
|
|
53
|
+
"kind": $prop($, 'cdx:scalasem:service:kind'),
|
|
54
|
+
"client": $prop($, 'cdx:scalasem:service:client'),
|
|
55
|
+
"locations": $filter(properties, function($p) { $p.name = 'cdx:scalasem:service:location' }).value
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
- id: SCALA-SEC-003
|
|
59
|
+
name: "Admin-like Scala endpoint is not marked as authenticated"
|
|
60
|
+
description: "scalasem found an inbound route with an admin, internal, manage or debug path segment that the report does not mark as authenticated. The source analysis does not see middleware or gateways, so this is a path-based prompt for review"
|
|
61
|
+
severity: low
|
|
62
|
+
category: scala-security
|
|
63
|
+
dry-run-support: full
|
|
64
|
+
condition: |
|
|
65
|
+
services[
|
|
66
|
+
$exists($filter(endpoints, function($e) { $contains($e, /(^|\/)(admin|internal|manage|debug)(\/|$)/) }))
|
|
67
|
+
and $prop($, 'cdx:scalasem:endpoint:framework') != null
|
|
68
|
+
and $not(authenticated = true)
|
|
69
|
+
]
|
|
70
|
+
location: |
|
|
71
|
+
{ "name": name, "endpoints": endpoints }
|
|
72
|
+
message: "Endpoint '{{ name }}' looks administrative and is not marked as authenticated"
|
|
73
|
+
mitigation: "Confirm the route is protected by middleware or a gateway the source does not show, and otherwise require authentication on it. The declaration site is named by the service's cdx:scalasem:service:location property"
|
|
74
|
+
evidence: |
|
|
75
|
+
{
|
|
76
|
+
"framework": $prop($, 'cdx:scalasem:endpoint:framework'),
|
|
77
|
+
"handler": $prop($, 'cdx:scalasem:endpoint:handler'),
|
|
78
|
+
"locations": $filter(properties, function($p) { $p.name = 'cdx:scalasem:service:location' }).value
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
- id: SCALA-SEC-004
|
|
82
|
+
name: "Scala source uses a weak algorithm that has no registry OID"
|
|
83
|
+
description: "scalasem flagged a weak algorithm that the OID registry cannot name, such as an MD5 or SHA-1 based signature or an RC4 password scheme, so it is recorded as a property instead of a crypto asset"
|
|
84
|
+
severity: medium
|
|
85
|
+
category: scala-security
|
|
86
|
+
dry-run-support: full
|
|
87
|
+
condition: |
|
|
88
|
+
$append(components, metadata.component)[
|
|
89
|
+
$exists(properties[name = 'cdx:scalasem:crypto:weakFinding'])
|
|
90
|
+
]
|
|
91
|
+
location: |
|
|
92
|
+
{ "bomRef": $."bom-ref", "name": name }
|
|
93
|
+
message: "Weak cryptographic algorithm used in Scala source, recorded on '{{ name }}'"
|
|
94
|
+
mitigation: "Move to a modern algorithm. Each finding names the algorithm and its call site as name@file#line"
|
|
95
|
+
evidence: |
|
|
96
|
+
{
|
|
97
|
+
"findings": properties[name = 'cdx:scalasem:crypto:weakFinding'].value
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
- id: SCALA-COMP-001
|
|
101
|
+
name: "Scala semantic evidence is incomplete"
|
|
102
|
+
description: "The scalasem run behind this BOM was degraded, for example by class files no compiler could read, a missing classpath, a timeout or a failed analyzer, so the BOM's Scala evidence may be missing usages, crypto findings or services"
|
|
103
|
+
severity: low
|
|
104
|
+
category: scala-compliance
|
|
105
|
+
dry-run-support: full
|
|
106
|
+
condition: |
|
|
107
|
+
metadata.component[$prop($, 'cdx:scalasem:degraded') = 'true']
|
|
108
|
+
location: |
|
|
109
|
+
{ "bomRef": $."bom-ref", "name": name }
|
|
110
|
+
message: "The Scala semantic evidence for '{{ name }}' is incomplete"
|
|
111
|
+
mitigation: "Read the cdx:scalasem:diagnostic:* metadata properties for the cause, and build the project before scanning when the diagnostics name missing class files or a missing classpath"
|
|
112
|
+
evidence: |
|
|
113
|
+
{
|
|
114
|
+
"diagnostics": $filter(properties, function($p) { $contains($p.name, 'cdx:scalasem:diagnostic:') }).(name & '=' & value)
|
|
115
|
+
}
|