@cdxgen/cdxgen 13.1.0 → 13.3.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 +32 -15
- package/bin/cdxgen.js +243 -122
- package/bin/evinse.js +7 -0
- package/bin/sign.js +88 -14
- package/bin/tracebom.js +64 -4
- package/bin/validate.js +29 -7
- package/bin/verify.js +79 -39
- package/data/crypto-oid.json +88 -12
- package/data/remediations.json +47 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +117 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +235 -5
- package/lib/cli/jsBom.js +108 -26
- package/lib/cli/jvmBom.js +68 -12
- package/lib/cli/managedBom.js +264 -41
- package/lib/cli/nativeBom.js +923 -15
- package/lib/core/activity.js +120 -2
- package/lib/core/env.js +44 -7
- package/lib/core/fs.js +174 -6
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/projectConfig.js +31 -4
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/bunutils.js +269 -101
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +447 -7
- package/lib/ecosystems/cmakeResolver.js +66 -0
- package/lib/ecosystems/cppEvidence.js +104 -33
- package/lib/ecosystems/ecosystems.js +260 -99
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +185 -9
- package/lib/ecosystems/jsEvidence.js +19 -20
- package/lib/ecosystems/kosi.js +1263 -0
- package/lib/ecosystems/npmutils.js +110 -9
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +120 -49
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-go.js +47 -9
- package/lib/ecosystems/parsers-js.js +171 -50
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +919 -161
- 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 +27 -15
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +954 -33
- 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/rubyutils.js +1426 -166
- package/lib/ecosystems/rusi.js +158 -7
- package/lib/evinser/evinser.js +547 -129
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/helpers/bomSigner.js +652 -221
- 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/atomUtils.js +568 -31
- package/lib/inventory/auditCategories.js +8 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +110 -14
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +627 -13
- package/lib/inventory/dynamic.js +39 -1
- package/lib/inventory/envcontext.js +9 -6
- package/lib/inventory/evidenceUtils.js +26 -5
- package/lib/inventory/fetchBatch.js +190 -19
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +6 -5
- 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/plugins.js +3 -0
- package/lib/inventory/protobom.js +62 -89
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/source.js +47 -23
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/inventory/traceRunner.js +210 -21
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +440 -20
- package/lib/managers/oci.js +39 -1
- package/lib/parsers/cmakeLists.js +452 -0
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/stages/postgen/introspection/markers.js +29 -6
- package/lib/stages/postgen/postgen.js +237 -16
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +48 -10
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +1 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +27 -27
- package/types/lib/audit/targets.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 +2 -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/env.d.ts +21 -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/metadataProperties.d.ts +39 -0
- package/types/lib/core/metadataProperties.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/userAgent.d.ts +56 -0
- package/types/lib/core/userAgent.d.ts.map +1 -0
- 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 +75 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
- 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 +23 -0
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +2 -2
- 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/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +40 -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 +86 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- package/types/lib/ecosystems/npmutils.d.ts +42 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- 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-go.d.ts +10 -0
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +10 -4
- 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-lua.d.ts +50 -0
- package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +90 -3
- 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/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +143 -2
- 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/evinser/evinser.d.ts +81 -2
- 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/agenticToolutils.d.ts +87 -0
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
- package/types/lib/helpers/bomSigner.d.ts +106 -13
- package/types/lib/helpers/bomSigner.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/atomUtils.d.ts +145 -3
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +4 -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/cdxrs.d.ts +8 -3
- package/types/lib/inventory/cdxrs.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.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 +87 -1
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.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 +34 -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 +5 -4
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- 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/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/purl.d.ts +71 -2
- package/types/lib/inventory/purl.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.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/traceRunner.d.ts +47 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- 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 +14 -0
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +76 -0
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- 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/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/introspection/markers.d.ts +3 -2
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +33 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -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/data/remediations.json
CHANGED
|
@@ -709,6 +709,30 @@
|
|
|
709
709
|
},
|
|
710
710
|
"docs": "https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES"
|
|
711
711
|
},
|
|
712
|
+
"dosai.semantics.persist-failed": {
|
|
713
|
+
"ecosystem": "csharp",
|
|
714
|
+
"kind": "evidence.degraded",
|
|
715
|
+
"impact": "none",
|
|
716
|
+
"title": "The combined dosai report could not be written to the semantics slices file; the SBOM evidence is unaffected",
|
|
717
|
+
"targetTier": "resolved",
|
|
718
|
+
"actions": [
|
|
719
|
+
{
|
|
720
|
+
"kind": "config",
|
|
721
|
+
"detail": "Check that the --semantics-slices-file path is writable and has room for a report the size of the dosai slices."
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
"kind": "rerun"
|
|
725
|
+
}
|
|
726
|
+
],
|
|
727
|
+
"verify": {
|
|
728
|
+
"rules": [],
|
|
729
|
+
"expectTier": "resolved",
|
|
730
|
+
"eventsCleared": [
|
|
731
|
+
"dosai.semantics.persist-failed"
|
|
732
|
+
]
|
|
733
|
+
},
|
|
734
|
+
"docs": "https://cyclonedx.github.io/cdxgen/#/EVINSE"
|
|
735
|
+
},
|
|
712
736
|
"cocoa.pods.missing": {
|
|
713
737
|
"ecosystem": "cocoa",
|
|
714
738
|
"kind": "fallback.engaged",
|
|
@@ -905,6 +929,29 @@
|
|
|
905
929
|
},
|
|
906
930
|
"docs": "https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES?id=rust"
|
|
907
931
|
},
|
|
932
|
+
"rust.cargo-metadata-unavailable": {
|
|
933
|
+
"ecosystem": "rust",
|
|
934
|
+
"kind": "command.failed",
|
|
935
|
+
"impact": "components",
|
|
936
|
+
"title": "cargo metadata could not be executed, so dependency kinds and platform gates were recovered from the manifests alone",
|
|
937
|
+
"targetTier": "lockfile",
|
|
938
|
+
"actions": [
|
|
939
|
+
{
|
|
940
|
+
"kind": "build",
|
|
941
|
+
"command": "{{cargo}} metadata --format-version 1 --locked"
|
|
942
|
+
},
|
|
943
|
+
{
|
|
944
|
+
"kind": "rerun"
|
|
945
|
+
}
|
|
946
|
+
],
|
|
947
|
+
"verify": {
|
|
948
|
+
"rules": [
|
|
949
|
+
"BF-RS-001"
|
|
950
|
+
],
|
|
951
|
+
"expectTier": "lockfile"
|
|
952
|
+
},
|
|
953
|
+
"docs": "https://cyclonedx.github.io/cdxgen/#/PROJECT_TYPES?id=rust"
|
|
954
|
+
},
|
|
908
955
|
"php.composer.missing": {
|
|
909
956
|
"ecosystem": "php",
|
|
910
957
|
"kind": "evidence.degraded",
|
package/data/rules/ai-bom.yaml
CHANGED
|
@@ -266,3 +266,81 @@
|
|
|
266
266
|
"parameterCount": $prop($, 'cdx:ai:parameterCount'),
|
|
267
267
|
"source": $prop($, 'cdx:ai:source')
|
|
268
268
|
}
|
|
269
|
+
|
|
270
|
+
- id: AIS-003
|
|
271
|
+
name: "Model artifact uses unsafe pickle deserialization"
|
|
272
|
+
description: "PyTorch pickle model artifacts (.bin/.pt/.pth/.ckpt) execute arbitrary code when loaded, so an untrusted or unverified artifact can run attacker-controlled code in the loading process."
|
|
273
|
+
severity: high
|
|
274
|
+
category: ai-security
|
|
275
|
+
dry-run-support: full
|
|
276
|
+
attack:
|
|
277
|
+
tactics: [TA0002]
|
|
278
|
+
techniques: [T1059, T1195.002]
|
|
279
|
+
standards:
|
|
280
|
+
owasp-ai-top-10:
|
|
281
|
+
- "LLM05: Supply Chain Vulnerabilities"
|
|
282
|
+
- "LLM03:2025 Supply Chain"
|
|
283
|
+
owasp-agentic-top-10-2026:
|
|
284
|
+
- "Agentic Supply Chain"
|
|
285
|
+
eu-ai-act:
|
|
286
|
+
- "Accuracy, robustness and cybersecurity"
|
|
287
|
+
- "Technical documentation and traceability"
|
|
288
|
+
eu-cra:
|
|
289
|
+
- "Software supply chain visibility"
|
|
290
|
+
- "Protection against untrusted code execution"
|
|
291
|
+
nist-ai-rmf:
|
|
292
|
+
- "Map"
|
|
293
|
+
- "Manage"
|
|
294
|
+
nist-ssdf:
|
|
295
|
+
- "Verify provenance of model artifacts before loading"
|
|
296
|
+
condition: |
|
|
297
|
+
components[
|
|
298
|
+
type = 'machine-learning-model'
|
|
299
|
+
and $prop($, 'cdx:ai:unsafeDeserialization') = 'true'
|
|
300
|
+
]
|
|
301
|
+
location: |
|
|
302
|
+
{
|
|
303
|
+
"bomRef": $."bom-ref"
|
|
304
|
+
}
|
|
305
|
+
message: "Model artifact '{{ name }}' loads via unsafe pickle deserialization"
|
|
306
|
+
mitigation: "Prefer safetensors over pickle checkpoints, or verify artifact provenance and load pickle models only from trusted sources in a sandbox."
|
|
307
|
+
evidence: |
|
|
308
|
+
{
|
|
309
|
+
"artifactFormat": $prop($, 'cdx:ai:artifactFormat'),
|
|
310
|
+
"source": $prop($, 'cdx:ai:source'),
|
|
311
|
+
"quantizationMethod": $prop($, 'cdx:ai:quantizationMethod')
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
- id: AIS-004
|
|
315
|
+
name: "ONNX model artifact lacks exporter provenance"
|
|
316
|
+
description: "ONNX files record the exporting framework and opset versions in their protobuf header. An artifact without this metadata cannot be traced to the toolchain that produced it, which weakens provenance review and can hide compatibility or security-relevant export settings."
|
|
317
|
+
severity: low
|
|
318
|
+
category: ai-governance
|
|
319
|
+
dry-run-support: full
|
|
320
|
+
standards:
|
|
321
|
+
owasp-ai-top-10:
|
|
322
|
+
- "LLM05: Supply Chain Vulnerabilities"
|
|
323
|
+
- "LLM03:2025 Supply Chain"
|
|
324
|
+
eu-ai-act:
|
|
325
|
+
- "Technical documentation and traceability"
|
|
326
|
+
nist-ai-rmf:
|
|
327
|
+
- "Map"
|
|
328
|
+
- "Govern"
|
|
329
|
+
condition: |
|
|
330
|
+
components[
|
|
331
|
+
type = 'machine-learning-model'
|
|
332
|
+
and $prop($, 'cdx:ai:artifactFormat') = 'onnx'
|
|
333
|
+
and not $hasProp($, 'cdx:onnx:producer')
|
|
334
|
+
]
|
|
335
|
+
location: |
|
|
336
|
+
{
|
|
337
|
+
"bomRef": $."bom-ref"
|
|
338
|
+
}
|
|
339
|
+
message: "ONNX model artifact '{{ name }}' carries no exporter provenance"
|
|
340
|
+
mitigation: "Re-export the model with a producer that records framework and opset metadata, or record the exporter and opset versions as component properties so the artifact remains traceable."
|
|
341
|
+
evidence: |
|
|
342
|
+
{
|
|
343
|
+
"artifactFormat": $prop($, 'cdx:ai:artifactFormat'),
|
|
344
|
+
"source": $prop($, 'cdx:ai:source'),
|
|
345
|
+
"modelId": $prop($, 'cdx:ai:modelId')
|
|
346
|
+
}
|
|
@@ -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
|
+
}
|
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
|
@@ -25,7 +25,10 @@ import {
|
|
|
25
25
|
CDXGEN_VERSION,
|
|
26
26
|
frameworksList,
|
|
27
27
|
} from "../core/state.js";
|
|
28
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
isPkgTypeOnlyImport,
|
|
30
|
+
setNpmCleanedVersionProperty,
|
|
31
|
+
} from "../ecosystems/npmutils.js";
|
|
29
32
|
import { parsePackageJsonName } from "../ecosystems/parsers-js.js";
|
|
30
33
|
import {
|
|
31
34
|
AI_INSTRUCTION_FILE_KINDS,
|
|
@@ -50,8 +53,7 @@ import {
|
|
|
50
53
|
import { enrichComponentWithMcpMetadata } from "../inventory/mcp.js";
|
|
51
54
|
import {
|
|
52
55
|
applyPurl,
|
|
53
|
-
|
|
54
|
-
isValidPurl,
|
|
56
|
+
canonicalPurlFromLooseString,
|
|
55
57
|
npmPurl,
|
|
56
58
|
tryBuildPurl,
|
|
57
59
|
} from "../inventory/purl.js";
|
|
@@ -182,6 +184,31 @@ export const determineParentComponent = (options) => {
|
|
|
182
184
|
return parentComponent;
|
|
183
185
|
};
|
|
184
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Tells whether a `--project-name`/`--project-version` override names the very
|
|
189
|
+
* project the generator detected, rather than a larger project containing it.
|
|
190
|
+
*
|
|
191
|
+
* The override is typed `application` because it can name anything. When its
|
|
192
|
+
* name, group, and version match the detected parent, the two describe the same
|
|
193
|
+
* project, and the detected parent is the better description of it: it carries
|
|
194
|
+
* the ecosystem purl and the manifest metadata. Treating it as a subproject
|
|
195
|
+
* would list the project as a dependency of itself under a second purl.
|
|
196
|
+
*
|
|
197
|
+
* @param {object|undefined} overrideParent Parent built from the options
|
|
198
|
+
* @param {object|undefined} detectedParent Parent the generator derived
|
|
199
|
+
* @returns {boolean} true when both describe the same project
|
|
200
|
+
*/
|
|
201
|
+
const overrideNamesDetectedParent = (overrideParent, detectedParent) => {
|
|
202
|
+
if (!overrideParent || !detectedParent?.purl) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
return (
|
|
206
|
+
overrideParent.name === detectedParent.name &&
|
|
207
|
+
(overrideParent.group || "") === (detectedParent.group || "") &&
|
|
208
|
+
`${overrideParent.version}` === `${detectedParent.version}`
|
|
209
|
+
);
|
|
210
|
+
};
|
|
211
|
+
|
|
185
212
|
/**
|
|
186
213
|
* Re-anchor a dependency graph onto a caller-supplied parent component.
|
|
187
214
|
*
|
|
@@ -338,7 +365,8 @@ export const componentToSimpleFullName = (comp) => {
|
|
|
338
365
|
/**
|
|
339
366
|
* Strips transient keys (`evidence`, `_integrity`, `license`, `qualifiers`,
|
|
340
367
|
* `repository`, `homepage`) from a parent component while preserving licenses
|
|
341
|
-
* and external references (bug #1519).
|
|
368
|
+
* and external references (bug #1519). External references whose URL is not a
|
|
369
|
+
* valid IRI reference are dropped.
|
|
342
370
|
*
|
|
343
371
|
* @param {object} comp Parent component to clean in place
|
|
344
372
|
* @returns {object} The cleaned parent component
|
|
@@ -356,10 +384,15 @@ export const cleanParentComponent = (comp) => {
|
|
|
356
384
|
}
|
|
357
385
|
delete comp.license;
|
|
358
386
|
delete comp.qualifiers;
|
|
359
|
-
|
|
387
|
+
// Components get their references validated in listComponents. The parent
|
|
388
|
+
// skips that conversion, so validate here too: an unusable URL taken from a
|
|
389
|
+
// manifest must not make the whole document invalid.
|
|
390
|
+
if (comp.repository || comp.homepage || comp.externalReferences?.length) {
|
|
360
391
|
const externalReferences = addExternalReferences(comp);
|
|
361
392
|
if (externalReferences?.length) {
|
|
362
393
|
comp.externalReferences = externalReferences;
|
|
394
|
+
} else {
|
|
395
|
+
delete comp.externalReferences;
|
|
363
396
|
}
|
|
364
397
|
}
|
|
365
398
|
delete comp.repository;
|
|
@@ -454,12 +487,18 @@ export function addMetadata(parentComponent = {}, options = {}, context = {}) {
|
|
|
454
487
|
// name-based bom-ref, and blindly copying that here manufactured invalid
|
|
455
488
|
// purls such as `"purl": "swift-smoke"`. CycloneDX requires `purl` to be
|
|
456
489
|
// a valid Package URL when present, so absent is the only correct answer.
|
|
490
|
+
// A bom-ref holds the decoded purl (`pkg:npm/app@1.0.0+build`), so it is
|
|
491
|
+
// split on its separators and rebuilt rather than escaped wholesale,
|
|
492
|
+
// which would also escape the `@` before the version.
|
|
457
493
|
if (!parentComponent["purl"] && parentComponent["bom-ref"]) {
|
|
458
|
-
const candidate =
|
|
459
|
-
|
|
494
|
+
const candidate = canonicalPurlFromLooseString(
|
|
495
|
+
parentComponent["bom-ref"],
|
|
496
|
+
);
|
|
497
|
+
if (candidate) {
|
|
460
498
|
parentComponent["purl"] = candidate;
|
|
461
499
|
}
|
|
462
500
|
}
|
|
501
|
+
setNpmCleanedVersionProperty(parentComponent);
|
|
463
502
|
}
|
|
464
503
|
if (parentComponent?.components) {
|
|
465
504
|
parentComponent.components = listComponents(
|
|
@@ -841,6 +880,7 @@ export function addExternalReferences(opkg) {
|
|
|
841
880
|
}
|
|
842
881
|
}
|
|
843
882
|
return externalReferences
|
|
883
|
+
.filter((reference) => typeof reference?.url === "string")
|
|
844
884
|
.map((reference) => ({ ...reference, url: reference.url.trim() }))
|
|
845
885
|
.filter((reference) => isValidIriReference(reference.url));
|
|
846
886
|
}
|
|
@@ -1014,13 +1054,16 @@ export function addComponent(
|
|
|
1014
1054
|
let purl = pkg.purl;
|
|
1015
1055
|
if (!purl && ptype) {
|
|
1016
1056
|
try {
|
|
1057
|
+
// Raw values: cdx-purl escapes each component itself, so escaping
|
|
1058
|
+
// them here first turned `@scope` into `%2540scope` and
|
|
1059
|
+
// `libstdc++6` into `libstdc%252B%252B6`.
|
|
1017
1060
|
purl = new Purl({
|
|
1018
1061
|
type: ptype,
|
|
1019
|
-
namespace:
|
|
1020
|
-
name
|
|
1062
|
+
namespace: group || null,
|
|
1063
|
+
name,
|
|
1021
1064
|
version: version || null,
|
|
1022
1065
|
qualifiers: pkg.qualifiers || null,
|
|
1023
|
-
subpath:
|
|
1066
|
+
subpath: pkg.subpath || null,
|
|
1024
1067
|
});
|
|
1025
1068
|
} catch {
|
|
1026
1069
|
// purl construction may fail for types requiring a namespace (maven,
|
|
@@ -1071,9 +1114,10 @@ export function addComponent(
|
|
|
1071
1114
|
} else {
|
|
1072
1115
|
compScope = "required";
|
|
1073
1116
|
}
|
|
1074
|
-
} else if (impPkgs.length && compScope !== "excluded") {
|
|
1075
|
-
compScope = "optional";
|
|
1076
1117
|
}
|
|
1118
|
+
// A package missing from the imports keeps its scope. Usage analysis
|
|
1119
|
+
// only promotes: the ecosystem passes have already scoped the package
|
|
1120
|
+
// list, and a missing import is not evidence that a package is optional.
|
|
1077
1121
|
}
|
|
1078
1122
|
let component = {
|
|
1079
1123
|
author,
|
|
@@ -1089,6 +1133,11 @@ export function addComponent(
|
|
|
1089
1133
|
purl: purlString,
|
|
1090
1134
|
externalReferences: addExternalReferences(pkg),
|
|
1091
1135
|
};
|
|
1136
|
+
// A CPE from the collector (caxa records one for the bundled Node.js
|
|
1137
|
+
// runtime) is how NVD-based scanners match the component.
|
|
1138
|
+
if (typeof pkg.cpe === "string" && pkg.cpe.length) {
|
|
1139
|
+
component.cpe = pkg.cpe;
|
|
1140
|
+
}
|
|
1092
1141
|
if (options.specVersion >= 1.5) {
|
|
1093
1142
|
component.pedigree = pkg.pedigree || undefined;
|
|
1094
1143
|
}
|
|
@@ -1097,7 +1146,26 @@ export function addComponent(
|
|
|
1097
1146
|
component.modelCard = pkg.modelCard || undefined;
|
|
1098
1147
|
component.data = pkg.data || undefined;
|
|
1099
1148
|
}
|
|
1149
|
+
if (options.specVersion >= 1.7) {
|
|
1150
|
+
// An external component is provided by the deployment environment rather
|
|
1151
|
+
// than bundled with the product. The schema defaults it to false, so it
|
|
1152
|
+
// is only worth emitting when true.
|
|
1153
|
+
component.isExternal = pkg.isExternal ? true : undefined;
|
|
1154
|
+
// A range describes an external dependency named without a resolved
|
|
1155
|
+
// version, such as the Ruby a gem requires. The schema allows it only
|
|
1156
|
+
// on an external component, and never next to a version.
|
|
1157
|
+
component.versionRange =
|
|
1158
|
+
pkg.isExternal && !pkg.version
|
|
1159
|
+
? pkg.versionRange || undefined
|
|
1160
|
+
: undefined;
|
|
1161
|
+
}
|
|
1100
1162
|
component["type"] = determinePackageType(pkg);
|
|
1163
|
+
// `version` is optional in every spec version cdxgen emits. An empty string
|
|
1164
|
+
// is not a version, and a consumer reading one has to guess whether it means
|
|
1165
|
+
// "unknown" or "the empty release"; leaving the field out says it plainly.
|
|
1166
|
+
if (!component.version) {
|
|
1167
|
+
delete component.version;
|
|
1168
|
+
}
|
|
1101
1169
|
if (pkg["bom-ref"]) {
|
|
1102
1170
|
component["bom-ref"] = pkg["bom-ref"];
|
|
1103
1171
|
} else if (purlString) {
|
|
@@ -1119,9 +1187,16 @@ export function addComponent(
|
|
|
1119
1187
|
if (options.specVersion >= 1.6 && component.author) {
|
|
1120
1188
|
const authorsList = [];
|
|
1121
1189
|
for (const aauthor of component.author.split(",")) {
|
|
1122
|
-
|
|
1190
|
+
// Registries hand out comma separated lists without a consistent
|
|
1191
|
+
// spacing, so `A, B` and `A,B` must produce the same names
|
|
1192
|
+
const name = aauthor.trim();
|
|
1193
|
+
if (name.length) {
|
|
1194
|
+
authorsList.push({ name });
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
if (authorsList.length) {
|
|
1198
|
+
component.authors = authorsList;
|
|
1123
1199
|
}
|
|
1124
|
-
component.authors = authorsList;
|
|
1125
1200
|
delete component.author;
|
|
1126
1201
|
}
|
|
1127
1202
|
// Downgrade authors section for < 1.5 :(
|
|
@@ -1157,6 +1232,7 @@ export function addComponent(
|
|
|
1157
1232
|
if (pkg.properties?.length) {
|
|
1158
1233
|
component.properties = pkg.properties;
|
|
1159
1234
|
}
|
|
1235
|
+
setNpmCleanedVersionProperty(component);
|
|
1160
1236
|
if (isTypeOnlyPkg) {
|
|
1161
1237
|
component.properties = component.properties || [];
|
|
1162
1238
|
if (
|
|
@@ -1273,6 +1349,25 @@ export function determinePackageType(pkg) {
|
|
|
1273
1349
|
) {
|
|
1274
1350
|
return "library";
|
|
1275
1351
|
}
|
|
1352
|
+
// A gem is a framework only when the curated list names that very gem:
|
|
1353
|
+
// the prefix matches below took rails-html-sanitizer and sinatra-contrib
|
|
1354
|
+
// for their frameworks, and the bare words meant for other ecosystems
|
|
1355
|
+
// turned childprocess ("chi") and jquery-rails ("jquery") into
|
|
1356
|
+
// frameworks. Any other gem is a library, unless it ships executables,
|
|
1357
|
+
// which makes it an application (discussions 4405 and 4408). A
|
|
1358
|
+
// description or keyword that merely mentions a framework is no evidence
|
|
1359
|
+
// of one: every gem whose summary says "tracing framework" would
|
|
1360
|
+
// otherwise become a framework component.
|
|
1361
|
+
if (purl.type === "gem") {
|
|
1362
|
+
if (frameworksList.all.includes(`pkg:gem/${purl.name}`)) {
|
|
1363
|
+
return "framework";
|
|
1364
|
+
}
|
|
1365
|
+
const hasExecutables = pkg.properties?.some(
|
|
1366
|
+
(prop) =>
|
|
1367
|
+
prop.name === "cdx:gem:executables" && prop.value?.trim().length,
|
|
1368
|
+
);
|
|
1369
|
+
return hasExecutables ? "application" : "library";
|
|
1370
|
+
}
|
|
1276
1371
|
for (const cf of frameworksList.all) {
|
|
1277
1372
|
if (
|
|
1278
1373
|
pkg.purl.startsWith(cf) ||
|
|
@@ -1454,7 +1549,14 @@ export const buildBomNSData = (options, pkgInfo, ptype, context) => {
|
|
|
1454
1549
|
}
|
|
1455
1550
|
const nsMapping = context.nsMapping || {};
|
|
1456
1551
|
const services = context.services || [];
|
|
1457
|
-
|
|
1552
|
+
let overrideParent = determineParentComponent(options);
|
|
1553
|
+
// A caller-supplied parentComponent is always honoured as given
|
|
1554
|
+
if (
|
|
1555
|
+
overrideParent !== options.parentComponent &&
|
|
1556
|
+
overrideNamesDetectedParent(overrideParent, context.parentComponent)
|
|
1557
|
+
) {
|
|
1558
|
+
overrideParent = undefined;
|
|
1559
|
+
}
|
|
1458
1560
|
const parentComponent = overrideParent || context.parentComponent;
|
|
1459
1561
|
const metadata = addMetadata(parentComponent, options, context);
|
|
1460
1562
|
const components = listComponents(options, allImports, pkgInfo, ptype);
|