@cdxgen/cdxgen 13.0.1 → 13.2.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 +30 -16
- package/bin/audit.js +6 -0
- package/bin/cdxgen.js +141 -7
- package/bin/evinse.js +3 -1
- package/data/component-tags.json +8 -1
- package/data/crypto-oid.json +88 -12
- package/data/frameworks-list.json +86 -1
- package/data/remediations.json +1167 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/asar-archives.yaml +1 -1
- package/data/rules/build-fidelity.yaml +782 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/data/rules/package-integrity.yaml +13 -13
- package/lib/audit/index.js +48 -4
- package/lib/audit/scoring.js +2 -8
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +32 -1
- package/lib/cli/cliOptions.js +94 -7
- package/lib/cli/index.js +268 -49
- package/lib/cli/jsBom.js +91 -5
- package/lib/cli/jvmBom.js +1515 -1314
- package/lib/cli/managedBom.js +249 -28
- package/lib/cli/nativeBom.js +986 -10
- package/lib/core/activity.js +120 -2
- package/lib/core/buildLedger.js +799 -0
- package/lib/core/deferredExit.js +141 -0
- package/lib/core/env.js +108 -2
- package/lib/core/fs.js +60 -0
- package/lib/core/logger.js +12 -99
- package/lib/core/severity.js +16 -0
- package/lib/core/syncFileWriter.js +106 -0
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +13 -5
- package/lib/ecosystems/cppEvidence.js +40 -2
- package/lib/ecosystems/ecosystems.js +250 -91
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +498 -17
- package/lib/ecosystems/kosi.js +1256 -0
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +69 -39
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-js.js +26 -0
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-jvm.js +9 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +188 -2
- package/lib/ecosystems/parsers-nim.js +221 -0
- package/lib/ecosystems/parsers-opam.js +180 -0
- package/lib/ecosystems/parsers-perl.js +277 -0
- package/lib/ecosystems/parsers-python.js +172 -36
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +944 -19
- package/lib/ecosystems/parsers-spack.js +204 -0
- package/lib/ecosystems/parsers-stack.js +264 -0
- package/lib/ecosystems/parsers-terraform.js +194 -0
- package/lib/ecosystems/pypiClassifiers.js +326 -0
- package/lib/ecosystems/pythonutils.js +49 -1
- package/lib/ecosystems/rubyutils.js +13 -0
- package/lib/ecosystems/rusi.js +126 -6
- package/lib/evinser/evinser.js +141 -13
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/helpers/remote/dependency-track.js +81 -14
- package/lib/inventory/agentFormulationParser.js +57 -4
- package/lib/inventory/aiCollector.js +504 -10
- package/lib/inventory/aiInventory.js +12 -0
- package/lib/inventory/analyzer.js +488 -30
- package/lib/inventory/astWorker.js +6 -0
- package/lib/inventory/auditCategories.js +17 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +7 -2
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/depsUtils.js +72 -0
- package/lib/inventory/dosai.js +508 -22
- package/lib/inventory/envcontext.js +215 -24
- package/lib/inventory/evidenceUtils.js +13 -0
- package/lib/inventory/fetchBatch.js +131 -2
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +594 -0
- package/lib/inventory/mcpConfigParser.js +166 -2
- package/lib/inventory/mcpDiscovery.js +11 -0
- package/lib/inventory/mcpServerManifest.js +389 -0
- package/lib/inventory/mcpbBundle.js +419 -0
- package/lib/inventory/osinfo.js +1 -0
- package/lib/inventory/plugins.js +3 -0
- package/lib/inventory/protobom.js +54 -88
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/source.js +10 -4
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/inventory/toolRequirements.js +903 -0
- package/lib/managers/binary.js +24 -0
- package/lib/managers/docker.js +26 -5
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/server/server.js +1 -1
- package/lib/stages/postgen/annotationBuilder.js +97 -0
- package/lib/stages/postgen/auditBom.js +76 -79
- package/lib/stages/postgen/introspection/annotate.js +320 -0
- package/lib/stages/postgen/introspection/commandFacts.js +293 -0
- package/lib/stages/postgen/introspection/emit.js +192 -0
- package/lib/stages/postgen/introspection/formulationEvidence.js +427 -0
- package/lib/stages/postgen/introspection/markers.js +250 -0
- package/lib/stages/postgen/introspection/reflect.js +1214 -0
- package/lib/stages/postgen/introspection/report.js +1208 -0
- package/lib/stages/postgen/introspection/score.js +1067 -0
- package/lib/stages/postgen/introspection/shapeCommand.js +304 -0
- package/lib/stages/postgen/parentEdge.js +156 -0
- package/lib/stages/postgen/postgen.js +284 -3
- package/lib/stages/postgen/ruleEngine.js +17 -0
- package/lib/stages/pregen/envAudit.js +42 -2
- package/lib/stages/pregen/pregen.js +1171 -6
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/lib/validator/bomValidator.js +11 -2
- package/package.json +26 -26
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/audit/scoring.d.ts +2 -8
- package/types/lib/audit/scoring.d.ts.map +1 -1
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +2 -1
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +10 -2
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +23 -0
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +33 -0
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +88 -8
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/buildLedger.d.ts +272 -0
- package/types/lib/core/buildLedger.d.ts.map +1 -0
- package/types/lib/core/deferredExit.d.ts +84 -0
- package/types/lib/core/deferredExit.d.ts.map +1 -0
- package/types/lib/core/env.d.ts +43 -1
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +21 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/logger.d.ts +2 -11
- package/types/lib/core/logger.d.ts.map +1 -1
- package/types/lib/core/severity.d.ts +11 -0
- package/types/lib/core/severity.d.ts.map +1 -0
- package/types/lib/core/syncFileWriter.d.ts +33 -0
- package/types/lib/core/syncFileWriter.d.ts.map +1 -0
- package/types/lib/core/userAgent.d.ts +56 -0
- package/types/lib/core/userAgent.d.ts.map +1 -0
- package/types/lib/ecosystems/cargoCache.d.ts +75 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +15 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +53 -0
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +73 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
- package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +23 -0
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
- package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
- package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
- package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-r.d.ts +35 -0
- package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
- package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
- package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
- package/types/lib/ecosystems/pypiClassifiers.d.ts +80 -0
- package/types/lib/ecosystems/pypiClassifiers.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +21 -1
- package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +34 -1
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +87 -0
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
- package/types/lib/helpers/remote/dependency-track.d.ts +45 -4
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
- package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +12 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +43 -0
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +43 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchRate.d.ts +9 -6
- package/types/lib/inventory/fetchRate.d.ts.map +1 -1
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +190 -0
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -0
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
- package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
- package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
- package/types/lib/inventory/mcpbBundle.d.ts +19 -0
- package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
- package/types/lib/inventory/osinfo.d.ts.map +1 -1
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts +18 -13
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
- package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
- package/types/lib/inventory/svelteUtils.d.ts +124 -0
- package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
- package/types/lib/inventory/toolRequirements.d.ts +216 -0
- package/types/lib/inventory/toolRequirements.d.ts.map +1 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/onnx.d.ts +28 -0
- package/types/lib/parsers/onnx.d.ts.map +1 -0
- package/types/lib/parsers/safetensors.d.ts +22 -0
- package/types/lib/parsers/safetensors.d.ts.map +1 -0
- package/types/lib/stages/postgen/annotationBuilder.d.ts +53 -0
- package/types/lib/stages/postgen/annotationBuilder.d.ts.map +1 -0
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/introspection/annotate.d.ts +81 -0
- package/types/lib/stages/postgen/introspection/annotate.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/commandFacts.d.ts +32 -0
- package/types/lib/stages/postgen/introspection/commandFacts.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/emit.d.ts +71 -0
- package/types/lib/stages/postgen/introspection/emit.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts +154 -0
- package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +58 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/reflect.d.ts +77 -0
- package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/report.d.ts +85 -0
- package/types/lib/stages/postgen/introspection/report.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/score.d.ts +149 -0
- package/types/lib/stages/postgen/introspection/score.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/shapeCommand.d.ts +79 -0
- package/types/lib/stages/postgen/introspection/shapeCommand.d.ts.map +1 -0
- package/types/lib/stages/postgen/parentEdge.d.ts +35 -0
- package/types/lib/stages/postgen/parentEdge.d.ts.map +1 -0
- package/types/lib/stages/postgen/postgen.d.ts +35 -2
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +131 -0
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# Cargo and Rust Rules
|
|
2
|
+
# Categories: cargo-security, cargo-compliance
|
|
3
|
+
# Evaluates the cdx:cargo:* properties cdxgen emits for Rust projects, with the
|
|
4
|
+
# native-library rules keyed on the `links` evidence that only `--deep` can
|
|
5
|
+
# produce.
|
|
6
|
+
|
|
7
|
+
- id: CARGO-SEC-001
|
|
8
|
+
name: "Statically linked native library has no resolvable version"
|
|
9
|
+
description: "A -sys crate compiles a C library into the artifact, but nothing in the build states which version, so no advisory can be matched against it"
|
|
10
|
+
severity: medium
|
|
11
|
+
category: cargo-security
|
|
12
|
+
dry-run-support: full
|
|
13
|
+
condition: |
|
|
14
|
+
components[
|
|
15
|
+
$prop($, 'cdx:cargo:linkage') = 'static'
|
|
16
|
+
and ($not($exists(version)) or version = '')
|
|
17
|
+
]
|
|
18
|
+
location: |
|
|
19
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
20
|
+
message: "Native library '{{ name }}' is compiled into the artifact by '{{ $prop($, 'cdx:cargo:providedBy') }}' with no resolvable version"
|
|
21
|
+
mitigation: "Pin the library version explicitly, or build against a crate release whose version encodes the upstream version as build metadata. Until the version is known, the bundled library has to be reviewed by hand: a vulnerability scanner matching on purl cannot see it"
|
|
22
|
+
evidence: |
|
|
23
|
+
{
|
|
24
|
+
"nativeLibrary": $prop($, 'cdx:cargo:nativeLibrary'),
|
|
25
|
+
"providedBy": $prop($, 'cdx:cargo:providedBy'),
|
|
26
|
+
"linkageEvidence": $prop($, 'cdx:cargo:linkageEvidence'),
|
|
27
|
+
"versionCandidates": $prop($, 'cdx:cargo:upstreamVersionCandidates')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
- id: CARGO-SEC-002
|
|
31
|
+
name: "Native C library is vendored into the Rust artifact"
|
|
32
|
+
description: "The resolver enabled a vendoring feature, so the library's own advisories and license obligations travel with the binary rather than with the host"
|
|
33
|
+
severity: low
|
|
34
|
+
category: cargo-security
|
|
35
|
+
dry-run-support: full
|
|
36
|
+
condition: |
|
|
37
|
+
components[
|
|
38
|
+
$prop($, 'cdx:cargo:linkage') = 'static'
|
|
39
|
+
and $exists(version)
|
|
40
|
+
and version != ''
|
|
41
|
+
]
|
|
42
|
+
location: |
|
|
43
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
44
|
+
message: "Native library '{{ name }}' version '{{ version }}' is compiled into the artifact by '{{ $prop($, 'cdx:cargo:providedBy') }}'"
|
|
45
|
+
mitigation: "Track this library's advisories directly; the cargo dependency graph will not report them. Confirm that updating the -sys crate is enough to pick up a library fix, and that the vendored build is rebuilt when the library is patched"
|
|
46
|
+
evidence: |
|
|
47
|
+
{
|
|
48
|
+
"nativeLibrary": $prop($, 'cdx:cargo:nativeLibrary'),
|
|
49
|
+
"providedBy": $prop($, 'cdx:cargo:providedBy'),
|
|
50
|
+
"linkageEvidence": $prop($, 'cdx:cargo:linkageEvidence'),
|
|
51
|
+
"upstreamVersionSource": $prop($, 'cdx:cargo:upstreamVersionSource')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
- id: CARGO-SEC-003
|
|
55
|
+
name: "Rust artifact links a native library the environment must provide"
|
|
56
|
+
description: "A -sys crate links a C library dynamically, so the deployed artifact depends on a library version this BOM cannot state"
|
|
57
|
+
severity: low
|
|
58
|
+
category: cargo-security
|
|
59
|
+
dry-run-support: full
|
|
60
|
+
condition: |
|
|
61
|
+
components[$prop($, 'cdx:cargo:linkage') = 'dynamic']
|
|
62
|
+
location: |
|
|
63
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
64
|
+
message: "Native library '{{ name }}' is linked dynamically by '{{ $prop($, 'cdx:cargo:providedBy') }}' and has to be present and patched on the deployment host"
|
|
65
|
+
mitigation: "Record the library version the deployment environment provides, through a container or host inventory. Where the artifact must be self-contained, enable the crate's vendoring feature so the version becomes part of this BOM"
|
|
66
|
+
evidence: |
|
|
67
|
+
{
|
|
68
|
+
"nativeLibrary": $prop($, 'cdx:cargo:nativeLibrary'),
|
|
69
|
+
"providedBy": $prop($, 'cdx:cargo:providedBy')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
- id: CARGO-COMP-001
|
|
73
|
+
name: "Vendored library version is ambiguous between shipped variants"
|
|
74
|
+
description: "The crate ships several variants of the library and the enabled features do not say which one is compiled, so the version was left unstated"
|
|
75
|
+
severity: medium
|
|
76
|
+
category: cargo-compliance
|
|
77
|
+
dry-run-support: full
|
|
78
|
+
condition: |
|
|
79
|
+
components[
|
|
80
|
+
$prop($, 'cdx:cargo:upstreamVersionCandidates') != null
|
|
81
|
+
]
|
|
82
|
+
location: |
|
|
83
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
84
|
+
message: "Native library '{{ name }}' could be any of '{{ $prop($, 'cdx:cargo:upstreamVersionCandidates') }}' depending on which variant '{{ $prop($, 'cdx:cargo:providedBy') }}' compiles"
|
|
85
|
+
mitigation: "Read the crate's build script to see which variant the enabled features select, then record the version. The variants can carry different licenses, so a license review cannot be completed from this BOM alone"
|
|
86
|
+
evidence: |
|
|
87
|
+
{
|
|
88
|
+
"versionCandidates": $prop($, 'cdx:cargo:upstreamVersionCandidates'),
|
|
89
|
+
"providedBy": $prop($, 'cdx:cargo:providedBy'),
|
|
90
|
+
"resolvedFeatures": $prop($, 'cdx:cargo:resolvedFeatures')
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
- id: CARGO-SEC-004
|
|
94
|
+
name: "Crate resolved from a registry other than crates.io"
|
|
95
|
+
description: "A crate came from an alternative or private registry, which can publish any name and version, including ones that also exist on crates.io"
|
|
96
|
+
severity: medium
|
|
97
|
+
category: cargo-security
|
|
98
|
+
dry-run-support: full
|
|
99
|
+
condition: |
|
|
100
|
+
$append(components, formulation.components)[
|
|
101
|
+
$prop($, 'cdx:cargo:alternateRegistry') = 'true'
|
|
102
|
+
]
|
|
103
|
+
location: |
|
|
104
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
105
|
+
message: "Crate '{{ name }}' was resolved from '{{ $prop($, 'cdx:cargo:sourceUrl') }}' rather than crates.io"
|
|
106
|
+
mitigation: "Confirm the registry is one your organisation operates and that the crate name is not also claimed on crates.io, which would let a resolver change pull a different crate. Verify the registry authenticates publishers and serves checksums"
|
|
107
|
+
evidence: |
|
|
108
|
+
{
|
|
109
|
+
"sourceKind": $prop($, 'cdx:cargo:sourceKind'),
|
|
110
|
+
"sourceUrl": $prop($, 'cdx:cargo:sourceUrl')
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
- id: CARGO-SEC-005
|
|
114
|
+
name: "Procedural macro crate executes during compilation"
|
|
115
|
+
description: "A proc-macro crate runs arbitrary code in the compiler on the build host, before any of the project's own code runs"
|
|
116
|
+
severity: low
|
|
117
|
+
category: cargo-security
|
|
118
|
+
dry-run-support: full
|
|
119
|
+
condition: |
|
|
120
|
+
$append(components, formulation.components)[
|
|
121
|
+
$prop($, 'cdx:cargo:procMacro') = 'true'
|
|
122
|
+
]
|
|
123
|
+
location: |
|
|
124
|
+
{ "bomRef": $."bom-ref", "purl": purl }
|
|
125
|
+
message: "Crate '{{ name }}' is a procedural macro and runs in the compiler on every build"
|
|
126
|
+
mitigation: "Treat this crate's compromise as equivalent to build-host compromise. Review its publisher and release history, pin it, and confirm the build host has no credentials a compile-time macro should not reach"
|
|
127
|
+
evidence: |
|
|
128
|
+
{
|
|
129
|
+
"dependencyKind": $prop($, 'cdx:cargo:dependencyKind'),
|
|
130
|
+
"publisher": $prop($, 'cdx:cargo:publisher'),
|
|
131
|
+
"resolvedFeatures": $prop($, 'cdx:cargo:resolvedFeatures')
|
|
132
|
+
}
|
|
@@ -386,3 +386,125 @@
|
|
|
386
386
|
"configuredServiceCount": $prop($, 'cdx:mcp:configuredServiceCount'),
|
|
387
387
|
"configuredServiceNames": $prop($, 'cdx:mcp:configuredServiceNames')
|
|
388
388
|
}
|
|
389
|
+
|
|
390
|
+
- id: MCP-009
|
|
391
|
+
name: "MCP configuration relies on deprecated protocol features"
|
|
392
|
+
description: "The MCP 2026-07-28 specification deprecates the legacy HTTP+SSE transport, dynamic client registration, sampling, logging, and roots. Configurations that still depend on these features need a migration plan before the deprecation window closes."
|
|
393
|
+
severity: medium
|
|
394
|
+
category: mcp-server
|
|
395
|
+
dry-run-support: full
|
|
396
|
+
standards:
|
|
397
|
+
owasp-ai-top-10:
|
|
398
|
+
- "LLM05: Supply Chain Vulnerabilities"
|
|
399
|
+
- "LLM03:2025 Supply Chain"
|
|
400
|
+
owasp-agentic-top-10-2026:
|
|
401
|
+
- "Agentic Supply Chain"
|
|
402
|
+
eu-ai-act:
|
|
403
|
+
- "Technical documentation and traceability"
|
|
404
|
+
eu-cra:
|
|
405
|
+
- "Technical documentation for software components"
|
|
406
|
+
nist-ai-rmf:
|
|
407
|
+
- "Govern"
|
|
408
|
+
- "Map"
|
|
409
|
+
nist-ssdf:
|
|
410
|
+
- "Plan migration away from deprecated protocol features"
|
|
411
|
+
condition: |
|
|
412
|
+
$auditServices($)[
|
|
413
|
+
$nullSafeProp($, 'cdx:mcp:deprecatedFeatures') != ''
|
|
414
|
+
]
|
|
415
|
+
location: |
|
|
416
|
+
{
|
|
417
|
+
"bomRef": $."bom-ref",
|
|
418
|
+
"file": $prop($, 'internal:SrcFile')
|
|
419
|
+
}
|
|
420
|
+
message: "MCP service '{{ name }}' uses deprecated protocol features"
|
|
421
|
+
mitigation: "Migrate off legacy SSE transport, dynamic client registration, sampling, logging, and roots to the MCP 2026-07-28 replacements within the deprecation window."
|
|
422
|
+
evidence: |
|
|
423
|
+
{
|
|
424
|
+
"protocolVersion": $prop($, 'cdx:mcp:protocolVersion'),
|
|
425
|
+
"deprecatedFeatures": $prop($, 'cdx:mcp:deprecatedFeatures'),
|
|
426
|
+
"transport": $prop($, 'cdx:mcp:transport')
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
- id: MCP-010
|
|
430
|
+
name: "MCP bundle declares secret configuration fields"
|
|
431
|
+
description: "MCPB/DXT bundles that declare secret-typed user_config fields have those values injected into the bundled server's environment on launch. Declaring such a field is the correct way to build a bundle, so this is an inventory disclosure to aid credential-handling review rather than a defect."
|
|
432
|
+
severity: low
|
|
433
|
+
category: mcp-server
|
|
434
|
+
dry-run-support: full
|
|
435
|
+
standards:
|
|
436
|
+
owasp-ai-top-10:
|
|
437
|
+
- "LLM05: Supply Chain Vulnerabilities"
|
|
438
|
+
- "LLM03:2025 Supply Chain"
|
|
439
|
+
owasp-agentic-top-10-2026:
|
|
440
|
+
- "Agentic Supply Chain"
|
|
441
|
+
eu-ai-act:
|
|
442
|
+
- "Technical documentation and traceability"
|
|
443
|
+
eu-cra:
|
|
444
|
+
- "Software supply chain visibility"
|
|
445
|
+
nist-ai-rmf:
|
|
446
|
+
- "Govern"
|
|
447
|
+
nist-ssdf:
|
|
448
|
+
- "Review secret handling in installable AI extensions"
|
|
449
|
+
condition: |
|
|
450
|
+
components[
|
|
451
|
+
$prop($, 'cdx:file:kind') = 'mcp-bundle'
|
|
452
|
+
and $prop($, 'cdx:mcpb:injectsSecretEnv') = 'true'
|
|
453
|
+
]
|
|
454
|
+
location: |
|
|
455
|
+
{
|
|
456
|
+
"bomRef": $."bom-ref",
|
|
457
|
+
"file": $prop($, 'internal:SrcFile')
|
|
458
|
+
}
|
|
459
|
+
message: "MCP bundle '{{ name }}' declares secret configuration fields injected as environment variables"
|
|
460
|
+
mitigation: "Review the declared secret fields and prefer runtime secret stores or references over values captured through the extension manifest."
|
|
461
|
+
evidence: |
|
|
462
|
+
{
|
|
463
|
+
"serverType": $prop($, 'cdx:mcpb:serverType'),
|
|
464
|
+
"secretFieldCount": $prop($, 'cdx:mcpb:secretFieldCount'),
|
|
465
|
+
"platformBinaryCount": $prop($, 'cdx:mcpb:platformBinaryCount')
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
- id: MCP-011
|
|
469
|
+
name: "MCP bundle ships precompiled platform binaries"
|
|
470
|
+
description: "MCPB/DXT bundles that ship precompiled per-platform binaries execute vendor-provided native code on the host with no build-from-source step. These binaries deserve provenance and signing review before a bundle is installed."
|
|
471
|
+
severity: medium
|
|
472
|
+
category: mcp-server
|
|
473
|
+
dry-run-support: full
|
|
474
|
+
attack:
|
|
475
|
+
tactics: [TA0002]
|
|
476
|
+
techniques: [T1195.002]
|
|
477
|
+
standards:
|
|
478
|
+
owasp-ai-top-10:
|
|
479
|
+
- "LLM05: Supply Chain Vulnerabilities"
|
|
480
|
+
- "LLM03:2025 Supply Chain"
|
|
481
|
+
owasp-agentic-top-10-2026:
|
|
482
|
+
- "Agentic Supply Chain"
|
|
483
|
+
eu-ai-act:
|
|
484
|
+
- "Technical documentation and traceability"
|
|
485
|
+
- "Accuracy, robustness and cybersecurity"
|
|
486
|
+
eu-cra:
|
|
487
|
+
- "Software supply chain visibility"
|
|
488
|
+
- "Protection against untrusted code execution"
|
|
489
|
+
nist-ai-rmf:
|
|
490
|
+
- "Map"
|
|
491
|
+
- "Manage"
|
|
492
|
+
nist-ssdf:
|
|
493
|
+
- "Verify provenance of precompiled binaries before execution"
|
|
494
|
+
condition: |
|
|
495
|
+
components[
|
|
496
|
+
$prop($, 'cdx:file:kind') = 'mcp-bundle'
|
|
497
|
+
and $number($nullSafeProp($, 'cdx:mcpb:platformBinaryCount')) > 0
|
|
498
|
+
]
|
|
499
|
+
location: |
|
|
500
|
+
{
|
|
501
|
+
"bomRef": $."bom-ref",
|
|
502
|
+
"file": $prop($, 'internal:SrcFile')
|
|
503
|
+
}
|
|
504
|
+
message: "MCP bundle '{{ name }}' ships precompiled platform binaries"
|
|
505
|
+
mitigation: "Verify the provenance and signatures of the bundled platform binaries, or prefer a bundle that builds from source, before installing."
|
|
506
|
+
evidence: |
|
|
507
|
+
{
|
|
508
|
+
"serverType": $prop($, 'cdx:mcpb:serverType'),
|
|
509
|
+
"platformBinaryCount": $prop($, 'cdx:mcpb:platformBinaryCount')
|
|
510
|
+
}
|
|
@@ -250,7 +250,7 @@
|
|
|
250
250
|
and $prop($, 'cdx:cargo:hasNativeBuild') = 'true'
|
|
251
251
|
and $count($$.components[
|
|
252
252
|
$prop($, 'cdx:github:action:ecosystem') = 'cargo'
|
|
253
|
-
and $contains($
|
|
253
|
+
and $contains($nullSafeProp($, 'cdx:github:action:role'), 'toolchain')
|
|
254
254
|
and $prop($, 'cdx:github:action:versionPinningType') != 'sha'
|
|
255
255
|
]) > 0
|
|
256
256
|
]
|
|
@@ -267,7 +267,7 @@
|
|
|
267
267
|
"buildScriptCapabilities": $prop($, 'cdx:cargo:buildScriptCapabilities'),
|
|
268
268
|
"toolchainActions": $$.components[
|
|
269
269
|
$prop($, 'cdx:github:action:ecosystem') = 'cargo'
|
|
270
|
-
and $contains($
|
|
270
|
+
and $contains($nullSafeProp($, 'cdx:github:action:role'), 'toolchain')
|
|
271
271
|
and $prop($, 'cdx:github:action:versionPinningType') != 'sha'
|
|
272
272
|
].$prop($, 'cdx:github:action:uses')
|
|
273
273
|
}
|
|
@@ -283,17 +283,17 @@
|
|
|
283
283
|
$prop($, 'cdx:rust:buildTool') = 'cargo'
|
|
284
284
|
and $prop($, 'cdx:cargo:hasNativeBuild') = 'true'
|
|
285
285
|
and (
|
|
286
|
-
$contains($
|
|
287
|
-
or $contains($
|
|
288
|
-
or $contains($
|
|
286
|
+
$contains($nullSafeProp($, 'cdx:cargo:buildScriptCapabilities'), 'process-execution')
|
|
287
|
+
or $contains($nullSafeProp($, 'cdx:cargo:buildScriptCapabilities'), 'network-access')
|
|
288
|
+
or $contains($nullSafeProp($, 'cdx:cargo:nativeBuildIndicators'), '-sys')
|
|
289
289
|
)
|
|
290
290
|
and $count($$.components[
|
|
291
291
|
$prop($, 'cdx:github:step:usesCargo') = 'true'
|
|
292
292
|
and (
|
|
293
|
-
$contains($
|
|
294
|
-
or $contains($
|
|
295
|
-
or $contains($
|
|
296
|
-
or $contains($
|
|
293
|
+
$contains($nullSafeProp($, 'cdx:github:step:cargoSubcommands'), 'build')
|
|
294
|
+
or $contains($nullSafeProp($, 'cdx:github:step:cargoSubcommands'), 'test')
|
|
295
|
+
or $contains($nullSafeProp($, 'cdx:github:step:cargoSubcommands'), 'package')
|
|
296
|
+
or $contains($nullSafeProp($, 'cdx:github:step:cargoSubcommands'), 'publish')
|
|
297
297
|
)
|
|
298
298
|
]) > 0
|
|
299
299
|
]
|
|
@@ -311,10 +311,10 @@
|
|
|
311
311
|
"cargoCommands": $$.components[
|
|
312
312
|
$prop($, 'cdx:github:step:usesCargo') = 'true'
|
|
313
313
|
and (
|
|
314
|
-
$contains($
|
|
315
|
-
or $contains($
|
|
316
|
-
or $contains($
|
|
317
|
-
or $contains($
|
|
314
|
+
$contains($nullSafeProp($, 'cdx:github:step:cargoSubcommands'), 'build')
|
|
315
|
+
or $contains($nullSafeProp($, 'cdx:github:step:cargoSubcommands'), 'test')
|
|
316
|
+
or $contains($nullSafeProp($, 'cdx:github:step:cargoSubcommands'), 'package')
|
|
317
|
+
or $contains($nullSafeProp($, 'cdx:github:step:cargoSubcommands'), 'publish')
|
|
318
318
|
)
|
|
319
319
|
].$prop($, 'cdx:github:step:command')
|
|
320
320
|
}
|
package/lib/audit/index.js
CHANGED
|
@@ -6,9 +6,11 @@ import process from "node:process";
|
|
|
6
6
|
import { createBom } from "../cli/index.js";
|
|
7
7
|
import {
|
|
8
8
|
isDryRun,
|
|
9
|
+
isSecureMode,
|
|
9
10
|
readEnvironmentVariable,
|
|
10
11
|
recordActivity,
|
|
11
12
|
} from "../core/activity.js";
|
|
13
|
+
import { getRemediationCatalog } from "../core/buildLedger.js";
|
|
12
14
|
import {
|
|
13
15
|
getTmpDir,
|
|
14
16
|
safeExistsSync,
|
|
@@ -53,6 +55,12 @@ import {
|
|
|
53
55
|
isHbomLikeBom,
|
|
54
56
|
isObomLikeBom,
|
|
55
57
|
} from "../stages/postgen/auditBom.js";
|
|
58
|
+
import {
|
|
59
|
+
isIntrospectionEnabled,
|
|
60
|
+
reflectOnRun,
|
|
61
|
+
} from "../stages/postgen/introspection/reflect.js";
|
|
62
|
+
import { buildIntrospectionJson } from "../stages/postgen/introspection/report.js";
|
|
63
|
+
import { scoreReflection } from "../stages/postgen/introspection/score.js";
|
|
56
64
|
import { postProcess } from "../stages/postgen/postgen.js";
|
|
57
65
|
import { formatTargetLabel } from "./progress.js";
|
|
58
66
|
import { renderAuditReport } from "./reporters.js";
|
|
@@ -259,7 +267,7 @@ export async function runDirectBomAuditFromBoms(inputBoms, options = {}) {
|
|
|
259
267
|
...directAuditOptions,
|
|
260
268
|
getBomWithOras,
|
|
261
269
|
});
|
|
262
|
-
|
|
270
|
+
const result = {
|
|
263
271
|
auditOptions: directAuditOptions,
|
|
264
272
|
bomFormat: getCycloneDxFormat(inputBom.bomJson),
|
|
265
273
|
findings,
|
|
@@ -268,7 +276,43 @@ export async function runDirectBomAuditFromBoms(inputBoms, options = {}) {
|
|
|
268
276
|
specVersion: inputBom.bomJson?.specVersion,
|
|
269
277
|
status: "audited",
|
|
270
278
|
summary: summarizeDirectAuditFindings(findings),
|
|
271
|
-
}
|
|
279
|
+
};
|
|
280
|
+
if (isIntrospectionEnabled(options)) {
|
|
281
|
+
// The audited BOM was not generated by this run, so there is no ledger
|
|
282
|
+
// of its own; the verdict rests on BOM structure alone and says so.
|
|
283
|
+
try {
|
|
284
|
+
const reflection = await reflectOnRun(inputBom.bomJson, options, {
|
|
285
|
+
ledgerEvents: [],
|
|
286
|
+
ledgerSource: "none",
|
|
287
|
+
projectPath: inputBom.source || "",
|
|
288
|
+
});
|
|
289
|
+
reflection.scoring = scoreReflection(
|
|
290
|
+
reflection,
|
|
291
|
+
getRemediationCatalog(),
|
|
292
|
+
{
|
|
293
|
+
secureMode: isSecureMode,
|
|
294
|
+
inContainer:
|
|
295
|
+
readEnvironmentVariable("CDXGEN_IN_CONTAINER") === "true",
|
|
296
|
+
},
|
|
297
|
+
);
|
|
298
|
+
reflection.bom = {
|
|
299
|
+
serialNumber: inputBom.bomJson?.serialNumber,
|
|
300
|
+
componentCount: Array.isArray(inputBom.bomJson?.components)
|
|
301
|
+
? inputBom.bomJson.components.length
|
|
302
|
+
: 0,
|
|
303
|
+
};
|
|
304
|
+
result.introspection = buildIntrospectionJson(
|
|
305
|
+
reflection,
|
|
306
|
+
reflection.scoring,
|
|
307
|
+
options,
|
|
308
|
+
);
|
|
309
|
+
} catch (err) {
|
|
310
|
+
console.warn(
|
|
311
|
+
`Build introspection failed to complete and was skipped: ${err?.message}`,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
results.push(result);
|
|
272
316
|
}
|
|
273
317
|
const summary = {
|
|
274
318
|
findingsBySeverity: {
|
|
@@ -1744,7 +1788,7 @@ async function recheckFindingsAgainstDefaultBranch(
|
|
|
1744
1788
|
if (!bomNSData?.bomJson) {
|
|
1745
1789
|
return undefined;
|
|
1746
1790
|
}
|
|
1747
|
-
const processedBomNSData = postProcess(
|
|
1791
|
+
const processedBomNSData = await postProcess(
|
|
1748
1792
|
bomNSData,
|
|
1749
1793
|
childOptions,
|
|
1750
1794
|
defaultSourceSelection.scanDir,
|
|
@@ -1948,7 +1992,7 @@ export async function auditTarget(target, options) {
|
|
|
1948
1992
|
options,
|
|
1949
1993
|
);
|
|
1950
1994
|
}
|
|
1951
|
-
const processedBomNSData = postProcess(
|
|
1995
|
+
const processedBomNSData = await postProcess(
|
|
1952
1996
|
bomNSData,
|
|
1953
1997
|
childOptions,
|
|
1954
1998
|
sourceSelection.scanDir,
|
package/lib/audit/scoring.js
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
import { thoughtLog } from "../core/logger.js";
|
|
2
|
+
import { SEVERITY_ORDER } from "../core/severity.js";
|
|
2
3
|
import {
|
|
3
4
|
hasRegistryProvenanceEvidenceProperties,
|
|
4
5
|
hasTrustedPublishingProperties,
|
|
5
6
|
} from "../inventory/provenanceUtils.js";
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
export const SEVERITY_ORDER = {
|
|
9
|
-
none: -1,
|
|
10
|
-
low: 0,
|
|
11
|
-
medium: 1,
|
|
12
|
-
high: 2,
|
|
13
|
-
critical: 3,
|
|
14
|
-
};
|
|
8
|
+
export { SEVERITY_ORDER };
|
|
15
9
|
|
|
16
10
|
const BASE_FINDING_WEIGHT = {
|
|
17
11
|
low: 4,
|
package/lib/audit/targets.js
CHANGED
|
@@ -465,8 +465,13 @@ export async function enrichInputBomsWithRegistryMetadata(inputBoms) {
|
|
|
465
465
|
process.env.CDXGEN_FETCH_PKG_METADATA = "true";
|
|
466
466
|
try {
|
|
467
467
|
const enrichedCandidates = [
|
|
468
|
+
// The audit scores publisher drift, ownership and release cadence, none
|
|
469
|
+
// of which the local Cargo registry records: it knows when a version was
|
|
470
|
+
// published but not by whom. This path pays the registry's rate limit
|
|
471
|
+
// deliberately rather than audit against metadata that cannot answer the
|
|
472
|
+
// questions the rules ask.
|
|
468
473
|
...(cargoCandidates.length
|
|
469
|
-
? await getCratesMetadata(cargoCandidates)
|
|
474
|
+
? await getCratesMetadata(cargoCandidates, { preferLocalCache: false })
|
|
470
475
|
: []),
|
|
471
476
|
...(npmCandidates.length ? await getNpmMetadata(npmCandidates) : []),
|
|
472
477
|
...(pypiCandidates.length
|
package/lib/cli/bomAssembly.js
CHANGED
|
@@ -200,6 +200,8 @@ export const determineParentComponent = (options) => {
|
|
|
200
200
|
* @param {object|undefined} detectedParent Parent the generator derived
|
|
201
201
|
* @param {object[]} components The component list, appended to in place
|
|
202
202
|
* @param {object[]} dependencies Edges the generator produced
|
|
203
|
+
* @param {object} options CLI options
|
|
204
|
+
* @param {string} ptype Package type
|
|
203
205
|
* @returns {object[]} Edges to emit, re-anchored when an override applies
|
|
204
206
|
*/
|
|
205
207
|
function anchorDetectedParent(
|
|
@@ -207,6 +209,8 @@ function anchorDetectedParent(
|
|
|
207
209
|
detectedParent,
|
|
208
210
|
components,
|
|
209
211
|
dependencies,
|
|
212
|
+
options,
|
|
213
|
+
ptype,
|
|
210
214
|
) {
|
|
211
215
|
const detectedRef = detectedParent?.["bom-ref"];
|
|
212
216
|
const overrideRef = overrideParent?.["bom-ref"];
|
|
@@ -225,7 +229,20 @@ function anchorDetectedParent(
|
|
|
225
229
|
// `metadata.component` is cleaned of. As a regular component it must go
|
|
226
230
|
// through the same conversion or schema validation rejects the document
|
|
227
231
|
// (issue #4320). The copy keeps the caller's context object untouched.
|
|
228
|
-
|
|
232
|
+
//
|
|
233
|
+
// Its workspace members go through `listComponents`, the same conversion
|
|
234
|
+
// `addMetadata` gives them when the detected parent is the one reaching
|
|
235
|
+
// `metadata.component`. Without it they keep the raw parser shape
|
|
236
|
+
// (`homepage: { url }`) that the nested Component schema rejects
|
|
237
|
+
// (issue #4326).
|
|
238
|
+
components.push(
|
|
239
|
+
cleanParentComponent({
|
|
240
|
+
...detectedParent,
|
|
241
|
+
components: detectedParent.components?.length
|
|
242
|
+
? listComponents(options, {}, detectedParent.components, ptype)
|
|
243
|
+
: undefined,
|
|
244
|
+
}),
|
|
245
|
+
);
|
|
229
246
|
}
|
|
230
247
|
// The caller owns the array it passed, and postProcess merges these edges
|
|
231
248
|
// across generators, so the re-anchoring is returned rather than spliced in.
|
|
@@ -1080,7 +1097,19 @@ export function addComponent(
|
|
|
1080
1097
|
component.modelCard = pkg.modelCard || undefined;
|
|
1081
1098
|
component.data = pkg.data || undefined;
|
|
1082
1099
|
}
|
|
1100
|
+
if (options.specVersion >= 1.7) {
|
|
1101
|
+
// An external component is provided by the deployment environment rather
|
|
1102
|
+
// than bundled with the product. The schema defaults it to false, so it
|
|
1103
|
+
// is only worth emitting when true.
|
|
1104
|
+
component.isExternal = pkg.isExternal ? true : undefined;
|
|
1105
|
+
}
|
|
1083
1106
|
component["type"] = determinePackageType(pkg);
|
|
1107
|
+
// `version` is optional in every spec version cdxgen emits. An empty string
|
|
1108
|
+
// is not a version, and a consumer reading one has to guess whether it means
|
|
1109
|
+
// "unknown" or "the empty release"; leaving the field out says it plainly.
|
|
1110
|
+
if (!component.version) {
|
|
1111
|
+
delete component.version;
|
|
1112
|
+
}
|
|
1084
1113
|
if (pkg["bom-ref"]) {
|
|
1085
1114
|
component["bom-ref"] = pkg["bom-ref"];
|
|
1086
1115
|
} else if (purlString) {
|
|
@@ -1446,6 +1475,8 @@ export const buildBomNSData = (options, pkgInfo, ptype, context) => {
|
|
|
1446
1475
|
context.parentComponent,
|
|
1447
1476
|
components,
|
|
1448
1477
|
context.dependencies || [],
|
|
1478
|
+
options,
|
|
1479
|
+
ptype,
|
|
1449
1480
|
);
|
|
1450
1481
|
if (
|
|
1451
1482
|
components &&
|
package/lib/cli/cliOptions.js
CHANGED
|
@@ -88,6 +88,21 @@ const POST_BUILD_SUPPORTED_PROJECT_TYPES = new Set([
|
|
|
88
88
|
// Helpers
|
|
89
89
|
// ---------------------------------------------------------------------------
|
|
90
90
|
|
|
91
|
+
/**
|
|
92
|
+
* The formulation privacy notice, shared by every path that turns formulation
|
|
93
|
+
* collection on automatically: the warning about emails and secrets is
|
|
94
|
+
* identical whichever caller adds the section.
|
|
95
|
+
*
|
|
96
|
+
* @returns {OptionsWarning} The notice.
|
|
97
|
+
*/
|
|
98
|
+
function formulationPrivacyNotice() {
|
|
99
|
+
return {
|
|
100
|
+
level: "info",
|
|
101
|
+
message:
|
|
102
|
+
"NOTE: Automatically collecting formulation information. The section may include sensitive data such as emails and secrets.\nPlease review the generated SBOM before distribution or LLM training.",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
91
106
|
/**
|
|
92
107
|
* Check whether a value was explicitly provided by the user (i.e., is not
|
|
93
108
|
* the yargs default). Yargs always populates default values, so we cannot
|
|
@@ -175,7 +190,39 @@ function resolveOutputTarget(args, { filePath, isRemoteOrPurl }) {
|
|
|
175
190
|
: resolve(join(filePath, args.output));
|
|
176
191
|
}
|
|
177
192
|
|
|
178
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Resolve the introspection opt-in from the flag and the environment.
|
|
195
|
+
*
|
|
196
|
+
* The parser maps `CDXGEN_INTROSPECT` onto the same boolean the flag sets, so
|
|
197
|
+
* a value outside the documented opt-in vocabulary — an exported but empty
|
|
198
|
+
* variable most of all — arrives indistinguishable from `--no-introspect`.
|
|
199
|
+
* The command line is therefore the authority on refusal, and the environment
|
|
200
|
+
* speaks only through `true` and `1`. An unmentioned flag stays undefined so a
|
|
201
|
+
* profile can still enable it.
|
|
202
|
+
*
|
|
203
|
+
* @param {Object} args Parsed CLI arguments.
|
|
204
|
+
* @param {string[]} argv Raw command-line arguments the parser was given.
|
|
205
|
+
* @returns {boolean|undefined} The requested state, or undefined when nothing asked.
|
|
206
|
+
*/
|
|
207
|
+
function resolveIntrospectOption(args, argv) {
|
|
208
|
+
const refused =
|
|
209
|
+
args.introspect === false &&
|
|
210
|
+
argv.some((argument) =>
|
|
211
|
+
["--no-introspect", "--introspect=false"].includes(argument),
|
|
212
|
+
);
|
|
213
|
+
if (refused) {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
return args.introspect === true ||
|
|
217
|
+
["true", "1"].includes(readEnvironmentVariable("CDXGEN_INTROSPECT"))
|
|
218
|
+
? true
|
|
219
|
+
: undefined;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function buildInitialOptions(
|
|
223
|
+
args,
|
|
224
|
+
{ filePath, isRemoteOrPurl, argv = process.argv },
|
|
225
|
+
) {
|
|
179
226
|
const res = Object.assign({}, args, {
|
|
180
227
|
projectType: args.type,
|
|
181
228
|
multiProject: args.recurse,
|
|
@@ -188,6 +235,10 @@ export function buildInitialOptions(args, { filePath, isRemoteOrPurl }) {
|
|
|
188
235
|
include: args.include || args.includeRegex,
|
|
189
236
|
noIgnore: args.noIgnore,
|
|
190
237
|
gitAiNotesRef: args.gitAiNotesRef || "refs/notes/ai",
|
|
238
|
+
// Introspection: the flag or the documented environment opt-in. An
|
|
239
|
+
// explicit --no-introspect stays `false` and beats both, while an
|
|
240
|
+
// unmentioned flag stays undefined so a profile can still enable it.
|
|
241
|
+
introspect: resolveIntrospectOption(args, argv),
|
|
191
242
|
// License normalization and enrichment are on by default; the only escape
|
|
192
243
|
// hatch is the corresponding environment variable.
|
|
193
244
|
licenseEnhance:
|
|
@@ -318,6 +369,20 @@ export function applyPostConstructionOverrides(options, context) {
|
|
|
318
369
|
options.installDeps = false;
|
|
319
370
|
}
|
|
320
371
|
|
|
372
|
+
// --- Introspection CI gate threshold -----------------------------------
|
|
373
|
+
if (options.introspectFailBelow !== undefined) {
|
|
374
|
+
const threshold = Number(options.introspectFailBelow);
|
|
375
|
+
if (!Number.isFinite(threshold) || threshold < 0 || threshold > 100) {
|
|
376
|
+
warnings.push({
|
|
377
|
+
level: "error",
|
|
378
|
+
message:
|
|
379
|
+
"--introspect-fail-below requires a number between 0 and 100, where absent means the gate never fails.",
|
|
380
|
+
});
|
|
381
|
+
} else {
|
|
382
|
+
options.introspectFailBelow = threshold;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
321
386
|
// When a standard is requested, the minimum spec version is 1.6
|
|
322
387
|
if (options.standard) {
|
|
323
388
|
if (!userSetSpecVersion) {
|
|
@@ -381,6 +446,8 @@ export function applyAdvancedOptions(options, context = {}) {
|
|
|
381
446
|
const secureMode = context.isSecureMode ?? isSecureMode;
|
|
382
447
|
const warnings = [];
|
|
383
448
|
const isHbomOnly = isHbomOnlyProjectTypes(options.projectType);
|
|
449
|
+
const includeFormulationBeforeProfiles = options.includeFormulation === true;
|
|
450
|
+
const introspectExplicitlyDisabled = options.introspect === false;
|
|
384
451
|
|
|
385
452
|
// --- Profile expansion -------------------------------------------------
|
|
386
453
|
switch (options.profile) {
|
|
@@ -412,6 +479,14 @@ export function applyAdvancedOptions(options, context = {}) {
|
|
|
412
479
|
case "license-compliance":
|
|
413
480
|
process.env.FETCH_LICENSE = "true";
|
|
414
481
|
break;
|
|
482
|
+
case "introspect":
|
|
483
|
+
options.introspect = true;
|
|
484
|
+
options.introspectAnnotate = options.introspectAnnotate !== false;
|
|
485
|
+
// The ledger's own record of what ran; formulation is where a consumer
|
|
486
|
+
// looks for the build environment the SBOM was produced in.
|
|
487
|
+
options.includeFormulation = !isHbomOnly;
|
|
488
|
+
options.evidence = true;
|
|
489
|
+
break;
|
|
415
490
|
case "ml-tiny":
|
|
416
491
|
process.env.FETCH_LICENSE = "true";
|
|
417
492
|
options.deep = false;
|
|
@@ -471,17 +546,29 @@ export function applyAdvancedOptions(options, context = {}) {
|
|
|
471
546
|
}
|
|
472
547
|
}
|
|
473
548
|
|
|
549
|
+
// An explicit --no-introspect negates the introspect profile's headline
|
|
550
|
+
// capability while keeping the bundle's other effects: the user asked for
|
|
551
|
+
// the profile minus introspection.
|
|
552
|
+
if (options.profile === "introspect" && introspectExplicitlyDisabled) {
|
|
553
|
+
options.introspect = false;
|
|
554
|
+
warnings.push({
|
|
555
|
+
level: "info",
|
|
556
|
+
message:
|
|
557
|
+
"NOTE: Introspection stays off because --no-introspect was passed explicitly; the other introspect profile settings still apply.",
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
474
561
|
// --- BOM audit → auto-formulation --------------------------------------
|
|
475
|
-
|
|
562
|
+
const introspectProfileAddsFormulation =
|
|
563
|
+
options.profile === "introspect" && !includeFormulationBeforeProfiles;
|
|
564
|
+
if (options.bomAudit || introspectProfileAddsFormulation) {
|
|
476
565
|
if (isHbomOnly) {
|
|
477
566
|
// HBOM-only: skip formulation
|
|
478
567
|
} else if (!options.includeFormulation) {
|
|
479
|
-
warnings.push(
|
|
480
|
-
level: "info",
|
|
481
|
-
message:
|
|
482
|
-
"NOTE: Automatically collecting formulation information. The section may include sensitive data such as emails and secrets.\nPlease review the generated SBOM before distribution or LLM training.",
|
|
483
|
-
});
|
|
568
|
+
warnings.push(formulationPrivacyNotice());
|
|
484
569
|
options.includeFormulation = true;
|
|
570
|
+
} else if (introspectProfileAddsFormulation) {
|
|
571
|
+
warnings.push(formulationPrivacyNotice());
|
|
485
572
|
}
|
|
486
573
|
}
|
|
487
574
|
|