@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.
Files changed (283) hide show
  1. package/README.md +32 -15
  2. package/bin/cdxgen.js +243 -122
  3. package/bin/evinse.js +7 -0
  4. package/bin/sign.js +88 -14
  5. package/bin/tracebom.js +64 -4
  6. package/bin/validate.js +29 -7
  7. package/bin/verify.js +79 -39
  8. package/data/crypto-oid.json +88 -12
  9. package/data/remediations.json +47 -0
  10. package/data/rules/ai-bom.yaml +78 -0
  11. package/data/rules/cargo-rust.yaml +132 -0
  12. package/data/rules/mcp-servers.yaml +122 -0
  13. package/lib/audit/targets.js +6 -1
  14. package/lib/cli/bomAssembly.js +117 -15
  15. package/lib/cli/cliOptions.js +93 -1
  16. package/lib/cli/index.js +235 -5
  17. package/lib/cli/jsBom.js +108 -26
  18. package/lib/cli/jvmBom.js +68 -12
  19. package/lib/cli/managedBom.js +264 -41
  20. package/lib/cli/nativeBom.js +923 -15
  21. package/lib/core/activity.js +120 -2
  22. package/lib/core/env.js +44 -7
  23. package/lib/core/fs.js +174 -6
  24. package/lib/core/metadataProperties.js +97 -0
  25. package/lib/core/projectConfig.js +31 -4
  26. package/lib/core/userAgent.js +151 -0
  27. package/lib/ecosystems/bunutils.js +269 -101
  28. package/lib/ecosystems/cargoCache.js +364 -0
  29. package/lib/ecosystems/caxa.js +447 -7
  30. package/lib/ecosystems/cmakeResolver.js +66 -0
  31. package/lib/ecosystems/cppEvidence.js +104 -33
  32. package/lib/ecosystems/ecosystems.js +260 -99
  33. package/lib/ecosystems/golem.js +11 -1
  34. package/lib/ecosystems/gradleutils.js +185 -9
  35. package/lib/ecosystems/jsEvidence.js +19 -20
  36. package/lib/ecosystems/kosi.js +1263 -0
  37. package/lib/ecosystems/npmutils.js +110 -9
  38. package/lib/ecosystems/parsers-crystal.js +179 -0
  39. package/lib/ecosystems/parsers-dotnet.js +120 -49
  40. package/lib/ecosystems/parsers-elm.js +816 -0
  41. package/lib/ecosystems/parsers-gleam.js +1 -1
  42. package/lib/ecosystems/parsers-go.js +47 -9
  43. package/lib/ecosystems/parsers-js.js +171 -50
  44. package/lib/ecosystems/parsers-julia.js +268 -0
  45. package/lib/ecosystems/parsers-lua.js +218 -0
  46. package/lib/ecosystems/parsers-misc.js +919 -161
  47. package/lib/ecosystems/parsers-nim.js +221 -0
  48. package/lib/ecosystems/parsers-opam.js +180 -0
  49. package/lib/ecosystems/parsers-perl.js +277 -0
  50. package/lib/ecosystems/parsers-python.js +27 -15
  51. package/lib/ecosystems/parsers-r.js +148 -0
  52. package/lib/ecosystems/parsers-rebar.js +411 -0
  53. package/lib/ecosystems/parsers-rust.js +954 -33
  54. package/lib/ecosystems/parsers-spack.js +204 -0
  55. package/lib/ecosystems/parsers-stack.js +264 -0
  56. package/lib/ecosystems/parsers-terraform.js +194 -0
  57. package/lib/ecosystems/rubyutils.js +1426 -166
  58. package/lib/ecosystems/rusi.js +158 -7
  59. package/lib/evinser/evinser.js +547 -129
  60. package/lib/evinser/swiftsem.js +1914 -302
  61. package/lib/helpers/agenticToolutils.js +925 -0
  62. package/lib/helpers/bomSigner.js +652 -221
  63. package/lib/inventory/agentFormulationParser.js +57 -4
  64. package/lib/inventory/aiCollector.js +504 -10
  65. package/lib/inventory/aiInventory.js +12 -0
  66. package/lib/inventory/analyzer.js +488 -30
  67. package/lib/inventory/astWorker.js +6 -0
  68. package/lib/inventory/atomUtils.js +568 -31
  69. package/lib/inventory/auditCategories.js +8 -0
  70. package/lib/inventory/cbomutils.js +30 -0
  71. package/lib/inventory/cdxrs.js +110 -14
  72. package/lib/inventory/communityAiConfigParser.js +215 -2
  73. package/lib/inventory/depsUtils.js +183 -11
  74. package/lib/inventory/display.js +4 -5
  75. package/lib/inventory/dosai.js +627 -13
  76. package/lib/inventory/dynamic.js +39 -1
  77. package/lib/inventory/envcontext.js +9 -6
  78. package/lib/inventory/evidenceUtils.js +26 -5
  79. package/lib/inventory/fetchBatch.js +190 -19
  80. package/lib/inventory/fetchRate.js +74 -11
  81. package/lib/inventory/forgeEnricher.js +3 -2
  82. package/lib/inventory/jvmToolEnv.js +6 -5
  83. package/lib/inventory/mcpConfigParser.js +166 -2
  84. package/lib/inventory/mcpDiscovery.js +11 -0
  85. package/lib/inventory/mcpServerManifest.js +389 -0
  86. package/lib/inventory/mcpbBundle.js +419 -0
  87. package/lib/inventory/plugins.js +3 -0
  88. package/lib/inventory/protobom.js +62 -89
  89. package/lib/inventory/protobomLoader.js +4 -0
  90. package/lib/inventory/purl.js +152 -3
  91. package/lib/inventory/rustFormulationParser.js +1 -1
  92. package/lib/inventory/sbomDocument.js +2 -70
  93. package/lib/inventory/source.js +47 -23
  94. package/lib/inventory/stylePackageRefs.js +41 -0
  95. package/lib/inventory/svelteUtils.js +524 -0
  96. package/lib/inventory/traceRunner.js +210 -21
  97. package/lib/managers/binary.js +115 -24
  98. package/lib/managers/docker.js +440 -20
  99. package/lib/managers/oci.js +39 -1
  100. package/lib/parsers/cmakeLists.js +452 -0
  101. package/lib/parsers/huggingfaceUtils.js +15 -7
  102. package/lib/parsers/largeJson.js +454 -0
  103. package/lib/parsers/npmrc.js +19 -1
  104. package/lib/parsers/onnx.js +294 -0
  105. package/lib/parsers/safetensors.js +114 -0
  106. package/lib/stages/postgen/introspection/markers.js +29 -6
  107. package/lib/stages/postgen/postgen.js +237 -16
  108. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
  109. package/lib/validator/bomValidator.js +6 -16
  110. package/lib/validator/index.js +48 -10
  111. package/lib/validator/reporters/console.js +3 -0
  112. package/lib/validator/reporters/json.js +1 -0
  113. package/lib/validator/reporters/sarif.js +7 -0
  114. package/package.json +27 -27
  115. package/types/lib/audit/targets.d.ts.map +1 -1
  116. package/types/lib/cli/bomAssembly.d.ts +2 -1
  117. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  118. package/types/lib/cli/cliOptions.d.ts +33 -1
  119. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  120. package/types/lib/cli/index.d.ts +2 -2
  121. package/types/lib/cli/index.d.ts.map +1 -1
  122. package/types/lib/cli/jsBom.d.ts +23 -0
  123. package/types/lib/cli/jsBom.d.ts.map +1 -1
  124. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  125. package/types/lib/cli/managedBom.d.ts +33 -0
  126. package/types/lib/cli/managedBom.d.ts.map +1 -1
  127. package/types/lib/cli/nativeBom.d.ts +88 -8
  128. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  129. package/types/lib/core/activity.d.ts.map +1 -1
  130. package/types/lib/core/env.d.ts +21 -0
  131. package/types/lib/core/env.d.ts.map +1 -1
  132. package/types/lib/core/fs.d.ts +55 -0
  133. package/types/lib/core/fs.d.ts.map +1 -1
  134. package/types/lib/core/metadataProperties.d.ts +39 -0
  135. package/types/lib/core/metadataProperties.d.ts.map +1 -0
  136. package/types/lib/core/projectConfig.d.ts +6 -2
  137. package/types/lib/core/projectConfig.d.ts.map +1 -1
  138. package/types/lib/core/userAgent.d.ts +56 -0
  139. package/types/lib/core/userAgent.d.ts.map +1 -0
  140. package/types/lib/ecosystems/bunutils.d.ts +12 -5
  141. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  142. package/types/lib/ecosystems/cargoCache.d.ts +75 -0
  143. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
  144. package/types/lib/ecosystems/caxa.d.ts +14 -0
  145. package/types/lib/ecosystems/caxa.d.ts.map +1 -1
  146. package/types/lib/ecosystems/cmakeResolver.d.ts +23 -0
  147. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  148. package/types/lib/ecosystems/cppEvidence.d.ts +2 -2
  149. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  150. package/types/lib/ecosystems/ecosystems.d.ts +15 -1
  151. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  152. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  153. package/types/lib/ecosystems/gradleutils.d.ts +40 -4
  154. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  155. package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
  156. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  157. package/types/lib/ecosystems/kosi.d.ts +86 -0
  158. package/types/lib/ecosystems/kosi.d.ts.map +1 -0
  159. package/types/lib/ecosystems/npmutils.d.ts +42 -0
  160. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  161. package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
  162. package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
  163. package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
  164. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  165. package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
  166. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
  167. package/types/lib/ecosystems/parsers-go.d.ts +10 -0
  168. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  169. package/types/lib/ecosystems/parsers-js.d.ts +10 -4
  170. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  171. package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
  172. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
  173. package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
  174. package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
  175. package/types/lib/ecosystems/parsers-misc.d.ts +90 -3
  176. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  177. package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
  178. package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
  179. package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
  180. package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
  181. package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
  182. package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
  183. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  184. package/types/lib/ecosystems/parsers-r.d.ts +35 -0
  185. package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
  186. package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
  187. package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
  188. package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
  189. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  190. package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
  191. package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
  192. package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
  193. package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
  194. package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
  195. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
  196. package/types/lib/ecosystems/pythonutils.d.ts +1 -1
  197. package/types/lib/ecosystems/rubyutils.d.ts +143 -2
  198. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  199. package/types/lib/ecosystems/rusi.d.ts +13 -0
  200. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  201. package/types/lib/evinser/evinser.d.ts +81 -2
  202. package/types/lib/evinser/evinser.d.ts.map +1 -1
  203. package/types/lib/evinser/swiftsem.d.ts +280 -15
  204. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  205. package/types/lib/helpers/agenticToolutils.d.ts +87 -0
  206. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
  207. package/types/lib/helpers/bomSigner.d.ts +106 -13
  208. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  209. package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
  210. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  211. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  212. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  213. package/types/lib/inventory/atomUtils.d.ts +145 -3
  214. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  215. package/types/lib/inventory/auditCategories.d.ts +4 -0
  216. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  217. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  218. package/types/lib/inventory/cdxrs.d.ts +8 -3
  219. package/types/lib/inventory/cdxrs.d.ts.map +1 -1
  220. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  221. package/types/lib/inventory/depsUtils.d.ts +15 -0
  222. package/types/lib/inventory/depsUtils.d.ts.map +1 -1
  223. package/types/lib/inventory/display.d.ts.map +1 -1
  224. package/types/lib/inventory/dosai.d.ts +87 -1
  225. package/types/lib/inventory/dosai.d.ts.map +1 -1
  226. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  227. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  228. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  229. package/types/lib/inventory/fetchBatch.d.ts +34 -0
  230. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  231. package/types/lib/inventory/fetchRate.d.ts +9 -6
  232. package/types/lib/inventory/fetchRate.d.ts.map +1 -1
  233. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  234. package/types/lib/inventory/jvmToolEnv.d.ts +5 -4
  235. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  236. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  237. package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
  238. package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
  239. package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
  240. package/types/lib/inventory/mcpbBundle.d.ts +19 -0
  241. package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
  242. package/types/lib/inventory/plugins.d.ts.map +1 -1
  243. package/types/lib/inventory/protobom.d.ts +18 -13
  244. package/types/lib/inventory/protobom.d.ts.map +1 -1
  245. package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
  246. package/types/lib/inventory/purl.d.ts +71 -2
  247. package/types/lib/inventory/purl.d.ts.map +1 -1
  248. package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
  249. package/types/lib/inventory/source.d.ts.map +1 -1
  250. package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
  251. package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
  252. package/types/lib/inventory/svelteUtils.d.ts +124 -0
  253. package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
  254. package/types/lib/inventory/traceRunner.d.ts +47 -1
  255. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  256. package/types/lib/managers/binary.d.ts +30 -1
  257. package/types/lib/managers/binary.d.ts.map +1 -1
  258. package/types/lib/managers/docker.d.ts +29 -0
  259. package/types/lib/managers/docker.d.ts.map +1 -1
  260. package/types/lib/managers/oci.d.ts +14 -0
  261. package/types/lib/managers/oci.d.ts.map +1 -1
  262. package/types/lib/parsers/cmakeLists.d.ts +76 -0
  263. package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
  264. package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
  265. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  266. package/types/lib/parsers/largeJson.d.ts +52 -0
  267. package/types/lib/parsers/largeJson.d.ts.map +1 -0
  268. package/types/lib/parsers/npmrc.d.ts +5 -0
  269. package/types/lib/parsers/npmrc.d.ts.map +1 -1
  270. package/types/lib/parsers/onnx.d.ts +28 -0
  271. package/types/lib/parsers/onnx.d.ts.map +1 -0
  272. package/types/lib/parsers/safetensors.d.ts +22 -0
  273. package/types/lib/parsers/safetensors.d.ts.map +1 -0
  274. package/types/lib/stages/postgen/introspection/markers.d.ts +3 -2
  275. package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -1
  276. package/types/lib/stages/postgen/postgen.d.ts +33 -0
  277. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  278. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  279. package/types/lib/validator/index.d.ts +10 -1
  280. package/types/lib/validator/index.d.ts.map +1 -1
  281. package/types/lib/validator/reporters/console.d.ts.map +1 -1
  282. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  283. package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
@@ -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",
@@ -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
+ }
@@ -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
@@ -25,7 +25,10 @@ import {
25
25
  CDXGEN_VERSION,
26
26
  frameworksList,
27
27
  } from "../core/state.js";
28
- import { isPkgTypeOnlyImport } from "../ecosystems/npmutils.js";
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
- encodeForPurl,
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
- if (comp.repository || comp.homepage) {
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 = encodeForPurl(parentComponent["bom-ref"]);
459
- if (isValidPurl(candidate)) {
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: encodeForPurl(group) || null,
1020
- name: encodeForPurl(name),
1062
+ namespace: group || null,
1063
+ name,
1021
1064
  version: version || null,
1022
1065
  qualifiers: pkg.qualifiers || null,
1023
- subpath: encodeForPurl(pkg.subpath) || null,
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
- authorsList.push({ name: aauthor });
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
- const overrideParent = determineParentComponent(options);
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);