@cdxgen/cdxgen 13.5.0 → 13.7.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 (380) hide show
  1. package/README.md +17 -10
  2. package/bin/audit.js +7 -4
  3. package/bin/cdxgen.js +260 -37
  4. package/bin/convert.js +146 -22
  5. package/bin/evinse.js +28 -3
  6. package/bin/repl.js +47 -3
  7. package/bin/sign.js +211 -34
  8. package/bin/tracebom.js +18 -6
  9. package/bin/validate.js +24 -3
  10. package/bin/verify.js +113 -5
  11. package/data/behavior-taxonomy.schema.json +1491 -0
  12. package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
  13. package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
  14. package/data/license-aliases.json +24 -2
  15. package/data/pqc-migration.json +239 -0
  16. package/data/remediations.json +176 -0
  17. package/data/rules/ai-bom.yaml +1 -1
  18. package/data/rules/brotli-decoder.yaml +107 -0
  19. package/data/rules/cbom-security.yaml +4 -3
  20. package/data/rules/package-integrity.yaml +28 -0
  21. package/data/rules/vscode-extensions.yaml +75 -0
  22. package/data/spdx-license-list.json +121 -7
  23. package/data/spdx-licenses.json +69 -0
  24. package/data/spdx.schema.json +845 -2
  25. package/lib/audit/index.js +25 -3
  26. package/lib/cli/bomAssembly.js +172 -4
  27. package/lib/cli/cliOptions.js +121 -17
  28. package/lib/cli/index.js +720 -902
  29. package/lib/cli/jsBom.js +156 -64
  30. package/lib/cli/jvmBom.js +602 -86
  31. package/lib/cli/managedBom.js +161 -19
  32. package/lib/cli/nativeBom.js +52 -5
  33. package/lib/core/boundedFile.js +344 -0
  34. package/lib/core/brotliProbe.js +182 -0
  35. package/lib/core/env.js +34 -0
  36. package/lib/core/fs.js +138 -3
  37. package/lib/core/globPatterns.js +218 -0
  38. package/lib/core/httpClient.js +98 -1
  39. package/lib/core/lockPaths.js +38 -0
  40. package/lib/core/paths.js +24 -1
  41. package/lib/core/processTree.js +100 -8
  42. package/lib/core/projectConfig.js +332 -25
  43. package/lib/core/toolRegistry.js +184 -0
  44. package/lib/ecosystems/cppEvidence.js +263 -106
  45. package/lib/ecosystems/cppIncludeResolver.js +16 -5
  46. package/lib/ecosystems/denoutils.js +2 -2
  47. package/lib/ecosystems/ecosystems.js +25 -10
  48. package/lib/ecosystems/gemHostProbe.js +301 -0
  49. package/lib/ecosystems/golem.js +3 -8
  50. package/lib/ecosystems/gradleutils.js +1909 -229
  51. package/lib/ecosystems/jsEvidence.js +90 -38
  52. package/lib/ecosystems/kosi.js +3 -5
  53. package/lib/ecosystems/npmVersionResolution.js +376 -0
  54. package/lib/ecosystems/parsers-bazel.js +107 -6
  55. package/lib/ecosystems/parsers-js.js +505 -22
  56. package/lib/ecosystems/parsers-misc.js +209 -20
  57. package/lib/ecosystems/parsers-python.js +178 -30
  58. package/lib/ecosystems/parsers-rust.js +117 -15
  59. package/lib/ecosystems/pythonutils.js +128 -2
  60. package/lib/ecosystems/remote/huggingface.js +22 -0
  61. package/lib/ecosystems/rubyutils.js +538 -31
  62. package/lib/ecosystems/rusi.js +2 -4
  63. package/lib/ecosystems/scalacliutils.js +2 -0
  64. package/lib/ecosystems/scalasem.js +798 -152
  65. package/lib/ecosystems/vendoredCode.js +237 -3
  66. package/lib/ecosystems/venvProbe.js +360 -0
  67. package/lib/evinser/evinser.js +536 -71
  68. package/lib/evinser/scalasem.js +5 -1
  69. package/lib/helpers/agenticToolutils.js +525 -94
  70. package/lib/helpers/bomSigner.js +43 -9
  71. package/lib/helpers/jssSigner.js +1276 -0
  72. package/lib/helpers/vsixutils.js +1574 -315
  73. package/lib/inventory/agentBlueprint.js +193 -0
  74. package/lib/inventory/agentToolNames.js +145 -0
  75. package/lib/inventory/aiCollector.js +153 -7
  76. package/lib/inventory/aiInventory.js +44 -8
  77. package/lib/inventory/aiProviders.js +324 -0
  78. package/lib/inventory/atomUtils.js +77 -0
  79. package/lib/inventory/bomUtils.js +8 -1
  80. package/lib/inventory/brotliDecoder.js +142 -0
  81. package/lib/inventory/buildToolRateLimit.js +1 -1
  82. package/lib/inventory/cbomutils.js +56 -7
  83. package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
  84. package/lib/inventory/citations.js +31 -2
  85. package/lib/inventory/communityAiConfigParser.js +978 -80
  86. package/lib/inventory/cyclonedx20.js +152 -0
  87. package/lib/inventory/cyclonedx20Common.js +286 -0
  88. package/lib/inventory/cyclonedx20Derived.js +159 -0
  89. package/lib/inventory/cyclonedx20Parties.js +552 -0
  90. package/lib/inventory/cyclonedx20Read.js +768 -0
  91. package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
  92. package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
  93. package/lib/inventory/cyclonedx20ReadContext.js +380 -0
  94. package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
  95. package/lib/inventory/cyclonedx20ReadSections.js +599 -0
  96. package/lib/inventory/cyclonedx20Render.js +766 -0
  97. package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
  98. package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
  99. package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
  100. package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
  101. package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
  102. package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
  103. package/lib/inventory/cyclonedx20RenderSections.js +499 -0
  104. package/lib/inventory/cyclonedx20Tables.js +594 -0
  105. package/lib/inventory/deps.js +141 -13
  106. package/lib/inventory/dosai.js +375 -6
  107. package/lib/inventory/dosaiParsers.js +3 -6
  108. package/lib/inventory/dotnetRuntime.js +367 -0
  109. package/lib/inventory/ecosystemFormulationParsers.js +979 -0
  110. package/lib/inventory/envcontext.js +436 -48
  111. package/lib/inventory/evidenceUtils.js +13 -1
  112. package/lib/inventory/fetchBatch.js +1 -1
  113. package/lib/inventory/formulationParsers.js +124 -14
  114. package/lib/inventory/gtfobins.js +13 -0
  115. package/lib/inventory/hbom.js +45 -6
  116. package/lib/inventory/hbomLoader.js +166 -0
  117. package/lib/inventory/hostRuntimeProbe.js +686 -0
  118. package/lib/inventory/jvmLocalRepos.js +9 -2
  119. package/lib/inventory/jvmToolEnv.js +29 -0
  120. package/lib/inventory/licenseEnhancer.js +12 -0
  121. package/lib/inventory/mavenCentralMirror.js +534 -0
  122. package/lib/inventory/mcpConfigParser.js +5 -0
  123. package/lib/inventory/nodeBuildInfo.js +218 -0
  124. package/lib/inventory/nodeHostProbe.js +202 -0
  125. package/lib/inventory/occurrenceKey.js +90 -0
  126. package/lib/inventory/osPackageResolver.js +182 -0
  127. package/lib/inventory/phpBuildInfo.js +10 -59
  128. package/lib/inventory/phpHostProbe.js +32 -488
  129. package/lib/inventory/pqcMigration.js +426 -0
  130. package/lib/inventory/rubyBuildInfo.js +972 -0
  131. package/lib/inventory/rubyHostProbe.js +218 -0
  132. package/lib/inventory/rustFormulationParser.js +21 -10
  133. package/lib/inventory/scalaCoords.js +4 -3
  134. package/lib/inventory/scalaDedupe.js +440 -0
  135. package/lib/inventory/source.js +114 -8
  136. package/lib/managers/binary.js +337 -3
  137. package/lib/managers/docker.js +79 -0
  138. package/lib/parsers/huggingfaceManifest.js +9 -1
  139. package/lib/parsers/huggingfaceUtils.js +18 -0
  140. package/lib/parsers/kiroPermissions.js +166 -0
  141. package/lib/parsers/largeJson.js +28 -11
  142. package/lib/parsers/vscodeExtensionId.js +183 -0
  143. package/lib/server/server.js +48 -35
  144. package/lib/stages/postgen/postgen.js +41 -1
  145. package/lib/stages/postgen/ruleEngine.js +13 -5
  146. package/lib/stages/postgen/specVersionCompat.js +76 -0
  147. package/lib/stages/pregen/envAudit.js +1 -0
  148. package/lib/stages/pregen/pregen.js +114 -57
  149. package/lib/validator/bomValidator.js +14 -2
  150. package/lib/validator/complianceEngine.js +30 -3
  151. package/lib/validator/complianceRules.js +1416 -4
  152. package/lib/validator/generated/validate-1.6.mjs +1 -1
  153. package/lib/validator/generated/validate-1.7.mjs +1 -1
  154. package/lib/validator/generated/validate-2.0.mjs +57554 -13851
  155. package/lib/validator/index.js +49 -8
  156. package/lib/validator/reporters/json.js +6 -0
  157. package/package.json +11 -10
  158. package/types/bin/repl.d.ts.map +1 -1
  159. package/types/lib/audit/index.d.ts +6 -2
  160. package/types/lib/audit/index.d.ts.map +1 -1
  161. package/types/lib/cli/bomAssembly.d.ts +29 -1
  162. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  163. package/types/lib/cli/cliOptions.d.ts +6 -0
  164. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  165. package/types/lib/cli/index.d.ts.map +1 -1
  166. package/types/lib/cli/jsBom.d.ts +3 -18
  167. package/types/lib/cli/jsBom.d.ts.map +1 -1
  168. package/types/lib/cli/jvmBom.d.ts +15 -0
  169. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  170. package/types/lib/cli/managedBom.d.ts.map +1 -1
  171. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  172. package/types/lib/core/boundedFile.d.ts +100 -0
  173. package/types/lib/core/boundedFile.d.ts.map +1 -0
  174. package/types/lib/core/brotliProbe.d.ts +37 -0
  175. package/types/lib/core/brotliProbe.d.ts.map +1 -0
  176. package/types/lib/core/env.d.ts +14 -0
  177. package/types/lib/core/env.d.ts.map +1 -1
  178. package/types/lib/core/fs.d.ts +4 -0
  179. package/types/lib/core/fs.d.ts.map +1 -1
  180. package/types/lib/core/globPatterns.d.ts +70 -0
  181. package/types/lib/core/globPatterns.d.ts.map +1 -0
  182. package/types/lib/core/httpClient.d.ts +24 -0
  183. package/types/lib/core/httpClient.d.ts.map +1 -1
  184. package/types/lib/core/lockPaths.d.ts +19 -0
  185. package/types/lib/core/lockPaths.d.ts.map +1 -0
  186. package/types/lib/core/paths.d.ts +9 -0
  187. package/types/lib/core/paths.d.ts.map +1 -1
  188. package/types/lib/core/processTree.d.ts +17 -4
  189. package/types/lib/core/processTree.d.ts.map +1 -1
  190. package/types/lib/core/projectConfig.d.ts +47 -8
  191. package/types/lib/core/projectConfig.d.ts.map +1 -1
  192. package/types/lib/core/toolRegistry.d.ts +36 -0
  193. package/types/lib/core/toolRegistry.d.ts.map +1 -0
  194. package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
  195. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  196. package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
  197. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
  198. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  199. package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
  200. package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
  201. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  202. package/types/lib/ecosystems/gradleutils.d.ts +294 -20
  203. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  204. package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
  205. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  206. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  207. package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
  208. package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
  209. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
  210. package/types/lib/ecosystems/parsers-js.d.ts +17 -20
  211. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  212. package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
  213. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  214. package/types/lib/ecosystems/parsers-python.d.ts +10 -0
  215. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  216. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  217. package/types/lib/ecosystems/pythonutils.d.ts +19 -1
  218. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  219. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  220. package/types/lib/ecosystems/rubyutils.d.ts +144 -12
  221. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  222. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  223. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
  224. package/types/lib/ecosystems/scalasem.d.ts +58 -16
  225. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  226. package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
  227. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
  228. package/types/lib/ecosystems/venvProbe.d.ts +40 -0
  229. package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
  230. package/types/lib/evinser/evinser.d.ts +120 -13
  231. package/types/lib/evinser/evinser.d.ts.map +1 -1
  232. package/types/lib/evinser/scalasem.d.ts.map +1 -1
  233. package/types/lib/helpers/agenticToolutils.d.ts +53 -19
  234. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
  235. package/types/lib/helpers/bomSigner.d.ts +20 -3
  236. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  237. package/types/lib/helpers/jssSigner.d.ts +288 -0
  238. package/types/lib/helpers/jssSigner.d.ts.map +1 -0
  239. package/types/lib/helpers/vsixutils.d.ts +303 -19
  240. package/types/lib/helpers/vsixutils.d.ts.map +1 -1
  241. package/types/lib/inventory/agentBlueprint.d.ts +70 -0
  242. package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
  243. package/types/lib/inventory/agentToolNames.d.ts +51 -0
  244. package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
  245. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  246. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  247. package/types/lib/inventory/aiProviders.d.ts +85 -0
  248. package/types/lib/inventory/aiProviders.d.ts.map +1 -0
  249. package/types/lib/inventory/atomUtils.d.ts +19 -0
  250. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  251. package/types/lib/inventory/bomUtils.d.ts.map +1 -1
  252. package/types/lib/inventory/brotliDecoder.d.ts +58 -0
  253. package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
  254. package/types/lib/inventory/cbomutils.d.ts +17 -0
  255. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  256. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
  257. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
  258. package/types/lib/inventory/citations.d.ts +5 -0
  259. package/types/lib/inventory/citations.d.ts.map +1 -1
  260. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  261. package/types/lib/inventory/cyclonedx20.d.ts +99 -0
  262. package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
  263. package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
  264. package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
  265. package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
  266. package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
  267. package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
  268. package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
  269. package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
  270. package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
  271. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
  272. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
  273. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
  274. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
  275. package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
  276. package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
  277. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
  278. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
  279. package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
  280. package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
  281. package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
  282. package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
  283. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
  284. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
  285. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
  286. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
  287. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
  288. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
  289. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
  290. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
  291. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
  292. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
  293. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
  294. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
  295. package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
  296. package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
  297. package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
  298. package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
  299. package/types/lib/inventory/deps.d.ts +35 -2
  300. package/types/lib/inventory/deps.d.ts.map +1 -1
  301. package/types/lib/inventory/dosai.d.ts +36 -1
  302. package/types/lib/inventory/dosai.d.ts.map +1 -1
  303. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  304. package/types/lib/inventory/dotnetRuntime.d.ts +91 -0
  305. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
  306. package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
  307. package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
  308. package/types/lib/inventory/envcontext.d.ts +74 -12
  309. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  310. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  311. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  312. package/types/lib/inventory/gtfobins.d.ts.map +1 -1
  313. package/types/lib/inventory/hbom.d.ts +11 -0
  314. package/types/lib/inventory/hbom.d.ts.map +1 -1
  315. package/types/lib/inventory/hbomLoader.d.ts +54 -0
  316. package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
  317. package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
  318. package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
  319. package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
  320. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
  321. package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
  322. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  323. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
  324. package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
  325. package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
  326. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  327. package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
  328. package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
  329. package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
  330. package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
  331. package/types/lib/inventory/occurrenceKey.d.ts +52 -0
  332. package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
  333. package/types/lib/inventory/osPackageResolver.d.ts +38 -0
  334. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  335. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
  336. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
  337. package/types/lib/inventory/pqcMigration.d.ts +84 -0
  338. package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
  339. package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
  340. package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
  341. package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
  342. package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
  343. package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
  344. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  345. package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
  346. package/types/lib/inventory/scalaDedupe.d.ts +43 -0
  347. package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
  348. package/types/lib/inventory/source.d.ts +25 -0
  349. package/types/lib/inventory/source.d.ts.map +1 -1
  350. package/types/lib/managers/binary.d.ts.map +1 -1
  351. package/types/lib/managers/docker.d.ts +12 -0
  352. package/types/lib/managers/docker.d.ts.map +1 -1
  353. package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
  354. package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
  355. package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
  356. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  357. package/types/lib/parsers/kiroPermissions.d.ts +62 -0
  358. package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
  359. package/types/lib/parsers/largeJson.d.ts +4 -3
  360. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  361. package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
  362. package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
  363. package/types/lib/server/server.d.ts.map +1 -1
  364. package/types/lib/stages/postgen/postgen.d.ts +7 -0
  365. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  366. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  367. package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
  368. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  369. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  370. package/types/lib/stages/pregen/pregen.d.ts +12 -8
  371. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  372. package/types/lib/validator/bomValidator.d.ts +5 -1
  373. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  374. package/types/lib/validator/complianceEngine.d.ts +4 -4
  375. package/types/lib/validator/complianceEngine.d.ts.map +1 -1
  376. package/types/lib/validator/complianceRules.d.ts +14 -2
  377. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  378. package/types/lib/validator/index.d.ts +10 -0
  379. package/types/lib/validator/index.d.ts.map +1 -1
  380. package/types/lib/validator/reporters/json.d.ts.map +1 -1
@@ -6,6 +6,7 @@ import { build } from "@cdxgen/cdx-purl";
6
6
  import { safeExistsSync } from "../core/fs.js";
7
7
  import { thoughtLog } from "../core/logger.js";
8
8
  import { sanitizeBomUrl } from "../core/propertySanitizer.js";
9
+ import { vscodeExtensionPlatformFromPath } from "../parsers/vscodeExtensionId.js";
9
10
  import { isAgentSkillSpecValid } from "./agentSkillSpec.js";
10
11
  import {
11
12
  addDosaiReachabilityProperties,
@@ -22,6 +23,7 @@ import {
22
23
  } from "./dosaiParsers.js";
23
24
  import { createGtfoBinsPropertiesFromRow } from "./gtfobins.js";
24
25
  import { createLolbasProperties } from "./lolbas.js";
26
+ import { occurrenceLocationKey } from "./occurrenceKey.js";
25
27
  import { isOsPackagePurlType } from "./osPackagePurl.js";
26
28
  import {
27
29
  createOsQueryFallbackBomRef,
@@ -369,7 +371,7 @@ export function addEvidenceForDotnet(pkgList, slicesFile, options = {}) {
369
371
  addDosaiSetValue(
370
372
  purlLocationMap,
371
373
  modPurl,
372
- `${adep.Path}#${adep.LineNumber}`,
374
+ occurrenceLocationKey(adep.Path, adep.LineNumber),
373
375
  );
374
376
  }
375
377
  }
@@ -619,6 +621,13 @@ export function convertOSQueryResults(
619
621
  // The profile an add-on was found in is not part of its purl, so the
620
622
  // add-on ID is what tells apart two add-ons that share a name.
621
623
  qualifiers = { tag_id: firefoxAddonTagId(res.identifier) };
624
+ } else if (queryObj.purlType === "vscode-extension") {
625
+ // A platform-specific build is a different artefact from the one for
626
+ // another platform, and the extension scan names it the same way.
627
+ const targetPlatform = vscodeExtensionPlatformFromPath(res.path);
628
+ if (targetPlatform) {
629
+ qualifiers = { platform: targetPlatform };
630
+ }
622
631
  }
623
632
  if (name) {
624
633
  name = sanitizeOsQueryIdentity(name);
@@ -788,11 +797,14 @@ export function convertOSQueryResults(
788
797
  // profile. Where it was found is not part of what it is, so the path is an
789
798
  // occurrence rather than a subpath: a home directory stays out of the purl, and
790
799
  // one package installed in two places is one component with two occurrences.
800
+ // Chocolatey rows carry the absolute path of the package's .nuspec, which a
801
+ // purl subpath cannot hold, so it is kept as an occurrence like these.
791
802
  const LOCATED_PACKAGE_PURL_TYPES = new Set([
792
803
  "npm",
793
804
  "pypi",
794
805
  "chrome-extension",
795
806
  "vscode-extension",
807
+ "chocolatey",
796
808
  ]);
797
809
  const LOCATED_PACKAGE_CATEGORIES = new Set(["firefox_addons"]);
798
810
 
@@ -221,7 +221,7 @@ export function openHostCircuit(
221
221
  );
222
222
  } else if (breaker === "maven-central") {
223
223
  console.warn(
224
- `${host} ${what}, so cdxgen will not contact it for the next ${minutes} minute(s) and Maven metadata comes from local caches only. Set MAVEN_CENTRAL_URL to a mirror or repository manager, for example https://maven-central.storage-download.googleapis.com/maven2/, to keep license enrichment working.`,
224
+ `${host} ${what}, so cdxgen will not contact it for the next ${minutes} minute(s) and Maven metadata comes from local caches only. Set MAVEN_CENTRAL_MIRROR=true to use Google's Maven Central mirror, or name a repository manager in MAVEN_CENTRAL_URL, to keep license enrichment working.`,
225
225
  );
226
226
  } else {
227
227
  console.warn(
@@ -4,7 +4,9 @@ import { basename } from "node:path";
4
4
  import process from "node:process";
5
5
 
6
6
  import { readEnvironmentVariable } from "../core/activity.js";
7
+ import { PROJECT_TYPE_ALIASES } from "../core/env.js";
7
8
  import { getAllFiles } from "../core/fs.js";
9
+ import { thoughtLog } from "../core/logger.js";
8
10
  import { collectOSCryptoLibs } from "./cbomutils.js";
9
11
  import { azurePipelinesParser } from "./ciParsers/azurePipelines.js";
10
12
  import { circleCiParser } from "./ciParsers/circleCi.js";
@@ -12,6 +14,13 @@ import { githubActionsParser } from "./ciParsers/githubActions.js";
12
14
  import { gitlabCiParser } from "./ciParsers/gitlabCi.js";
13
15
  import { jenkinsParser } from "./ciParsers/jenkins.js";
14
16
  import { trimComponents } from "./depsUtils.js";
17
+ import {
18
+ dotnetProjectFormulationParser,
19
+ formulationSourcePath,
20
+ goModuleFormulationParser,
21
+ makefileFormulationParser,
22
+ pythonRequirementsFormulationParser,
23
+ } from "./ecosystemFormulationParsers.js";
15
24
  import {
16
25
  collectEnvInfo,
17
26
  getBranch,
@@ -47,8 +56,9 @@ function buildReadmeSecurityComponents(discoveryPath, options) {
47
56
  if (!scan.hasHiddenUnicode) {
48
57
  continue;
49
58
  }
59
+ const sourcePath = formulationSourcePath(filePath, discoveryPath);
50
60
  const properties = [
51
- { name: "internal:SrcFile", value: filePath },
61
+ { name: "internal:SrcFile", value: sourcePath },
52
62
  { name: "cdx:file:kind", value: "readme" },
53
63
  { name: "cdx:file:hasHiddenUnicode", value: "true" },
54
64
  {
@@ -71,7 +81,7 @@ function buildReadmeSecurityComponents(discoveryPath, options) {
71
81
  });
72
82
  }
73
83
  components.push({
74
- "bom-ref": `file:${filePath}`,
84
+ "bom-ref": `file:${sourcePath}`,
75
85
  name: basename(filePath),
76
86
  properties,
77
87
  type: "file",
@@ -90,13 +100,22 @@ function buildReadmeSecurityComponents(discoveryPath, options) {
90
100
  * {
91
101
  * id: string, // unique stable identifier
92
102
  * patterns: string[], // non-empty array of glob patterns for file discovery
93
- * parse(files: string[], options: Object): // synchronous function
103
+ * parse(files: string[], options: Object, context: { rootDir: string }):
104
+ * // synchronous function
94
105
  * { workflows?, components?, services?, properties?, dependencies? }
95
106
  * }
96
107
  * ```
108
+ *
109
+ * `context.rootDir` is the scanned project root. A parser that records file
110
+ * paths records them relative to it, so no path of the scanning machine
111
+ * reaches the BOM.
97
112
  */
98
113
  const _parsers = [
99
114
  rustFormulationParser,
115
+ makefileFormulationParser,
116
+ goModuleFormulationParser,
117
+ pythonRequirementsFormulationParser,
118
+ dotnetProjectFormulationParser,
100
119
  githubActionsParser,
101
120
  gitlabCiParser,
102
121
  jenkinsParser,
@@ -187,6 +206,59 @@ const ENV_BLOCKLIST = [
187
206
  "webhook",
188
207
  ];
189
208
 
209
+ /**
210
+ * Remove the parts of every URL in an environment value that can carry
211
+ * credentials: the userinfo before `@`, the query string and the fragment.
212
+ * The value is scanned with indexOf, one URL at a time, so the work stays
213
+ * linear in its length.
214
+ *
215
+ * @param {string} value environment variable value
216
+ * @returns {string} the value with URL credentials, queries and fragments removed
217
+ */
218
+ function stripUrlCredentials(value) {
219
+ const delimiters = " \t\r\n\"'`,;<>|";
220
+ let result = "";
221
+ let cursor = 0;
222
+ let schemeEnd = value.indexOf("://");
223
+ while (schemeEnd !== -1) {
224
+ const start = schemeEnd + 3;
225
+ let end = start;
226
+ while (end < value.length && !delimiters.includes(value[end])) {
227
+ end += 1;
228
+ }
229
+ const url = value.slice(start, end);
230
+ let authorityEnd = url.length;
231
+ for (const marker of ["/", "?", "#"]) {
232
+ const at = url.indexOf(marker);
233
+ if (at !== -1 && at < authorityEnd) {
234
+ authorityEnd = at;
235
+ }
236
+ }
237
+ const authority = url.slice(0, authorityEnd);
238
+ let rest = url.slice(authorityEnd);
239
+ for (const marker of ["?", "#"]) {
240
+ const at = rest.indexOf(marker);
241
+ if (at !== -1) {
242
+ rest = rest.slice(0, at);
243
+ }
244
+ }
245
+ result += `${value.slice(cursor, start)}${authority.slice(authority.lastIndexOf("@") + 1)}${rest}`;
246
+ cursor = end;
247
+ schemeEnd = value.indexOf("://", end);
248
+ }
249
+ return result + value.slice(cursor);
250
+ }
251
+
252
+ // The project types a scan asked for, as given on the command line.
253
+ function requestedProjectTypes(options) {
254
+ return [].concat(options?.projectType || []).flatMap((type) =>
255
+ `${type}`
256
+ .split(",")
257
+ .map((part) => part.trim().toLowerCase())
258
+ .filter(Boolean),
259
+ );
260
+ }
261
+
190
262
  /**
191
263
  * Build the formulation section for a CycloneDX BOM.
192
264
  *
@@ -285,15 +357,45 @@ export function addFormulationSection(filePath, options, context = {}) {
285
357
  }
286
358
  }
287
359
 
360
+ // The toolchains and OS crypto libraries are probed on the machine cdxgen
361
+ // runs on. A container image or root filesystem was built elsewhere, so the
362
+ // scanning host's tools say nothing about it.
363
+ // Only an explicit container type counts: hasAnyProjectType treats
364
+ // `universal` as every type, and a universal source scan runs on the host.
365
+ const requestedTypes = requestedProjectTypes(options);
366
+ const describesScanningHost = !requestedTypes.some((type) =>
367
+ PROJECT_TYPE_ALIASES.oci.includes(type),
368
+ );
369
+ // An OS inventory (obom) describes the live host. Its path is usually the
370
+ // working directory, such as a home directory or /, so the build-input,
371
+ // CI and README discovery below would walk a tree that is not a project.
372
+ const describesLiveHost =
373
+ requestedTypes.length > 0 &&
374
+ requestedTypes.every((type) => PROJECT_TYPE_ALIASES.os.includes(type));
375
+ if (!describesScanningHost) {
376
+ thoughtLog(
377
+ "Skipping the toolchain and OS crypto library probes for formulation, since they would describe the scanning host and not the image.",
378
+ );
379
+ }
380
+
288
381
  // Build environment details (Java, .NET, Python, Node, GCC, Rust, Go, Ruby,
289
- // PHP, Composer)
290
- const infoComponents = collectEnvInfo(projectPath, options);
382
+ // RubyGems, Bundler, PHP, Composer)
383
+ const infoComponents = describesScanningHost
384
+ ? collectEnvInfo(projectPath, options)
385
+ : [];
291
386
  if (infoComponents?.length) {
292
387
  components = components.concat(infoComponents);
293
388
  }
294
389
 
295
- // OS crypto libraries (cbom mode)
296
- if (options.includeCrypto) {
390
+ // OS crypto libraries (cbom mode). The queries also read the host's
391
+ // certificates, authorized keys and keychain items, so they run only when
392
+ // formulation was requested explicitly, not when CI, a profile, an OS type
393
+ // or the cbom command turned it on.
394
+ if (
395
+ options.includeCrypto &&
396
+ describesScanningHost &&
397
+ !options.formulationAutomatic
398
+ ) {
297
399
  const cryptoLibs = collectOSCryptoLibs(options, context?.executeOsQuery);
298
400
  if (cryptoLibs?.length) {
299
401
  components = components.concat(cryptoLibs);
@@ -307,8 +409,13 @@ export function addFormulationSection(filePath, options, context = {}) {
307
409
  const ciProperties = [];
308
410
 
309
411
  const discoveryPath = projectPath || ".";
412
+ if (describesLiveHost) {
413
+ thoughtLog(
414
+ "Skipping build-input, CI and README discovery for formulation, since an OS inventory describes the host and not a project tree.",
415
+ );
416
+ }
310
417
 
311
- for (const parser of _parsers) {
418
+ for (const parser of describesLiveHost ? [] : _parsers) {
312
419
  const matchedFiles = [];
313
420
  for (const pattern of parser.patterns) {
314
421
  const found = getAllFiles(discoveryPath, pattern, options);
@@ -323,7 +430,9 @@ export function addFormulationSection(filePath, options, context = {}) {
323
430
 
324
431
  let result;
325
432
  try {
326
- result = parser.parse(uniqueMatchedFiles, options);
433
+ result = parser.parse(uniqueMatchedFiles, options, {
434
+ rootDir: discoveryPath,
435
+ });
327
436
  } catch (err) {
328
437
  // A broken parser must not kill SBOM generation
329
438
  console.warn(
@@ -355,10 +464,9 @@ export function addFormulationSection(filePath, options, context = {}) {
355
464
  components = components.concat(ciComponents);
356
465
  }
357
466
 
358
- const readmeSecurityComponents = buildReadmeSecurityComponents(
359
- discoveryPath,
360
- options,
361
- );
467
+ const readmeSecurityComponents = describesLiveHost
468
+ ? []
469
+ : buildReadmeSecurityComponents(discoveryPath, options);
362
470
  if (readmeSecurityComponents.length) {
363
471
  components = components.concat(readmeSecurityComponents);
364
472
  }
@@ -377,7 +485,9 @@ export function addFormulationSection(filePath, options, context = {}) {
377
485
  !ENV_BLOCKLIST.some((b) => value.toLowerCase().includes(b)) &&
378
486
  value.length
379
487
  ) {
380
- environmentVars.push({ name: aevar, value });
488
+ // A registry or mirror URL can carry a password before `@` or a
489
+ // token in its query string, so only the scheme, host and path stay.
490
+ environmentVars.push({ name: aevar, value: stripUrlCredentials(value) });
381
491
  }
382
492
  }
383
493
 
@@ -17,6 +17,15 @@ const MATCH_FIELDS = [
17
17
  "executable",
18
18
  "description",
19
19
  ];
20
+ // Rows of these categories name an account, not a program: a user called
21
+ // `node` or `git` is no use of the binary of that name, and the GECOS comment
22
+ // in the description is free text about the account.
23
+ const ACCOUNT_CATEGORIES = new Set([
24
+ "authorized_keys_snapshot",
25
+ "logged_in_users_snapshot",
26
+ "users_snapshot",
27
+ ]);
28
+ const ACCOUNT_SKIPPED_FIELDS = new Set(["name", "description"]);
20
29
  const UNIX_PATH_PATTERN = /(?:^|[\s'"`=;|&(])((?:\.{0,2}\/|\/)[^\s'"`|;&()]+)/g;
21
30
  const STANDALONE_COMMAND_PATTERN =
22
31
  /(?:^|[\s;|&(])([a-z_][a-z0-9+._-]{1,})(?=$|[\s'"`|;&()])/gi;
@@ -250,7 +259,11 @@ export function createGtfoBinsProperties(name, linkedName) {
250
259
  */
251
260
  export function createGtfoBinsPropertiesFromRow(queryCategory, row) {
252
261
  const matches = new Map();
262
+ const isAccount = ACCOUNT_CATEGORIES.has(queryCategory);
253
263
  for (const field of MATCH_FIELDS) {
264
+ if (isAccount && ACCOUNT_SKIPPED_FIELDS.has(field)) {
265
+ continue;
266
+ }
254
267
  const fieldValue = row?.[field];
255
268
  if (!fieldValue) {
256
269
  continue;
@@ -8,6 +8,9 @@ import {
8
8
  readEnvironmentVariable,
9
9
  recordActivity,
10
10
  } from "../core/activity.js";
11
+ import { thoughtLog } from "../core/logger.js";
12
+ import { isCycloneDx20SpecVersion } from "./bomUtils.js";
13
+ import { readCycloneDx20 } from "./cyclonedx20.js";
11
14
  import { getHbomCommandDiagnosticSummary } from "./hbomAnalysis.js";
12
15
  import { importHbomModule } from "./hbomLoader.js";
13
16
  import { resolveCdxgenPlugins, resolvePluginBinary } from "./plugins.js";
@@ -411,8 +414,9 @@ export function ensureSupportedHbomSpecVersion(specVersion) {
411
414
  if (specVersion === undefined || specVersion === null || specVersion === "") {
412
415
  return;
413
416
  }
414
- if (Number(specVersion) !== 1.7) {
415
- throw new Error("HBOM generation currently supports only CycloneDX 1.7.");
417
+ // cdxgen renders 2.0 itself from the 1.7 collector output.
418
+ if (Number(specVersion) !== 1.7 && Number(specVersion) !== 2) {
419
+ throw new Error("HBOM generation supports only CycloneDX 1.7 and 2.0.");
416
420
  }
417
421
  }
418
422
 
@@ -474,6 +478,10 @@ export function normalizeHbomOptions(options = {}) {
474
478
  includeSensitiveIdentifiers:
475
479
  options.includeSensitiveIdentifiers ?? options.sensitive ?? false,
476
480
  platform: options.platform,
481
+ // Always 1.7: cdxgen's own pipeline (postProcess and its 2.0 renderer)
482
+ // turns the 1.7 collector output into 2.0, the way it renders every
483
+ // other BOM. A cdx-hbom that can emit 2.0 itself is never asked to.
484
+ specVersion: "1.7",
477
485
  timeoutMs:
478
486
  Number.isNaN(timeoutMs) || timeoutMs <= 0 ? undefined : timeoutMs,
479
487
  };
@@ -722,6 +730,38 @@ export function addHbomAnalysisProperties(bomJson) {
722
730
  return bomJson;
723
731
  }
724
732
 
733
+ /**
734
+ * The 1.x-shaped model of a collector result. cdxgen asks the collector for
735
+ * 1.7; a collector that still answers with a CycloneDX 2.0 document (a
736
+ * `specFormat`, or specVersion 2.0) is read with readCycloneDx20, so the
737
+ * 1.x pipeline that follows never mistakes it for a 1.x BOM and loses its
738
+ * inventories.
739
+ *
740
+ * @param {object} collected What collectHardware returned
741
+ * @returns {object} The 1.x-shaped HBOM
742
+ */
743
+ export function toInternalHbom(collected) {
744
+ if (
745
+ !collected ||
746
+ typeof collected !== "object" ||
747
+ !(collected.specFormat || isCycloneDx20SpecVersion(collected.specVersion))
748
+ ) {
749
+ return collected;
750
+ }
751
+ const { internalBom, losses } = readCycloneDx20(collected);
752
+ if (losses.length) {
753
+ thoughtLog(
754
+ `The HBOM collector returned CycloneDX 2.0; reading it into the 1.x model reported ${losses.length} loss(es).`,
755
+ losses,
756
+ );
757
+ }
758
+ // The pipeline renders 2.0 again from the 1.x model, so the model says
759
+ // what the collector was asked for.
760
+ internalBom.specVersion = "1.7";
761
+ delete internalBom.specFormat;
762
+ return internalBom;
763
+ }
764
+
725
765
  /**
726
766
  * Generate an HBOM using the optional cdx-hbom package.
727
767
  *
@@ -750,12 +790,11 @@ export async function createHbomDocument(options = {}) {
750
790
  });
751
791
  return createDryRunHbomDocument(options);
752
792
  }
753
- const bomJson = addHbomAnalysisProperties(
754
- await hbomModule.collectHardware(normalizedOptions),
755
- );
793
+ const collected = await hbomModule.collectHardware(normalizedOptions);
794
+ const bomJson = addHbomAnalysisProperties(toInternalHbom(collected));
756
795
  if (isDryRun) {
757
796
  recordHbomCollectorTrace(
758
- hbomModule.getCollectorTrace?.(bomJson) ?? normalizedOptions.trace,
797
+ hbomModule.getCollectorTrace?.(collected) ?? normalizedOptions.trace,
759
798
  );
760
799
  }
761
800
  return bomJson;
@@ -7,6 +7,21 @@ const LOCAL_HBOM_MODULE_URL = new URL(
7
7
  import.meta.url,
8
8
  );
9
9
  const LOCAL_HBOM_MODULE_PATH = fileURLToPath(LOCAL_HBOM_MODULE_URL);
10
+ // The ./cdx20 export of @cdxgen/cdx-hbom (cdx-hbom#26), at the same place
11
+ // relative to a local checkout as the package's exports map gives it.
12
+ const LOCAL_HBOM_MAPPER_PATH = fileURLToPath(
13
+ new URL("../../cdx-hbom/src/common/cdx20.js", import.meta.url),
14
+ );
15
+
16
+ /**
17
+ * The places cdx-hbom is loaded from, in order: a local checkout next to
18
+ * cdxgen, else the installed package.
19
+ */
20
+ export const HBOM_MODULE_LOCATIONS = Object.freeze({
21
+ localModulePath: LOCAL_HBOM_MODULE_PATH,
22
+ localMapperPath: LOCAL_HBOM_MAPPER_PATH,
23
+ packageMapperSpecifier: "@cdxgen/cdx-hbom/cdx20",
24
+ });
10
25
 
11
26
  /**
12
27
  * Resolve the optional cdx-hbom module.
@@ -33,3 +48,154 @@ export async function importHbomModule() {
33
48
  }
34
49
  return hbomModule;
35
50
  }
51
+
52
+ /**
53
+ * Load the optional CycloneDX 2.0 hardware mapper,
54
+ * `toCdx20HardwareComponent`, from the same cdx-hbom that importHbomModule
55
+ * loads: the local checkout when it exists (its src/common/cdx20.js), else
56
+ * the installed package's `./cdx20` export. A cdx-hbom without the mapper
57
+ * (the published 0.7.0 has no `./cdx20` export) or one that fails to load
58
+ * gives null, and the 2.0 render then keeps its generic conversion.
59
+ *
60
+ * @param {object} [locations] Where to look; defaults to
61
+ * HBOM_MODULE_LOCATIONS
62
+ * @returns {Promise<Function|null>} The mapper, or null
63
+ */
64
+ export async function loadHardwareMapper(locations = HBOM_MODULE_LOCATIONS) {
65
+ let specifier = locations.packageMapperSpecifier;
66
+ if (safeExistsSync(locations.localModulePath)) {
67
+ if (!safeExistsSync(locations.localMapperPath)) {
68
+ return null;
69
+ }
70
+ specifier = pathToFileURL(locations.localMapperPath).href;
71
+ }
72
+ let mapperModule;
73
+ try {
74
+ mapperModule = await import(specifier);
75
+ } catch (err) {
76
+ // cdx-hbom not installed, or a release without the ./cdx20 export: the
77
+ // generic conversion applies and nothing is wrong. Any other failure is
78
+ // a broken cdx-hbom and is reported before falling back.
79
+ if (!isMissingMapperError(err, specifier)) {
80
+ console.warn(
81
+ `The cdx-hbom CycloneDX 2.0 hardware mapper could not be loaded, so hardware components use the generic 2.0 conversion: ${err?.message || err}`,
82
+ );
83
+ }
84
+ return null;
85
+ }
86
+ return typeof mapperModule.toCdx20HardwareComponent === "function"
87
+ ? mapperModule.toCdx20HardwareComponent
88
+ : null;
89
+ }
90
+
91
+ /**
92
+ * Whether an import error means the mapper is simply absent: the package is
93
+ * not installed, or its exports map has no `./cdx20`. A missing module
94
+ * imported from inside the mapper is a fault, not an absence.
95
+ *
96
+ * @param {Error} err Import error
97
+ * @param {string} specifier The specifier that was imported
98
+ * @returns {boolean} true when the mapper is absent
99
+ */
100
+ function isMissingMapperError(err, specifier) {
101
+ if (err?.code === "ERR_PACKAGE_PATH_NOT_EXPORTED") {
102
+ return true;
103
+ }
104
+ const message = String(err?.message || "");
105
+ const packageName = specifier.startsWith("@")
106
+ ? specifier.split("/").slice(0, 2).join("/")
107
+ : undefined;
108
+ if (packageName) {
109
+ // Node.js and Bun name the missing package; Deno reports an import that
110
+ // its import map or node_modules cannot resolve.
111
+ return (
112
+ message.includes(`'${packageName}'`) ||
113
+ message.includes(`"${packageName}"`) ||
114
+ message.includes(`${packageName}/cdx20`) ||
115
+ (err?.code === "ERR_MODULE_NOT_FOUND" && message.includes(packageName))
116
+ );
117
+ }
118
+ return false;
119
+ }
120
+
121
+ /** The identifier policy cdx-hbom records when raw identifiers were kept. */
122
+ export const RAW_IDENTIFIER_POLICY = "raw-identifiers-enabled";
123
+
124
+ /**
125
+ * The identifier policy the collector recorded for the run, on the HBOM
126
+ * subject (`metadata.component`) or in `metadata.properties`.
127
+ *
128
+ * @param {object} internalBom The 1.x-shaped internal model
129
+ * @returns {string|undefined} The `cdx:hbom:identifierPolicy` value
130
+ */
131
+ export function recordedIdentifierPolicy(internalBom) {
132
+ for (const properties of [
133
+ internalBom?.metadata?.component?.properties,
134
+ internalBom?.metadata?.properties,
135
+ ]) {
136
+ const value = (Array.isArray(properties) ? properties : []).find(
137
+ (property) => property?.name === "cdx:hbom:identifierPolicy",
138
+ )?.value;
139
+ if (typeof value === "string") {
140
+ return value;
141
+ }
142
+ }
143
+ return undefined;
144
+ }
145
+
146
+ /**
147
+ * The renderCycloneDx20 context for an internal model: the hardware mapper
148
+ * and its identifier options when the model has a component with a
149
+ * cdx:hbom:* property and a mapper is available, otherwise an empty context,
150
+ * so a BOM without hardware never loads cdx-hbom. Hardware identifiers are
151
+ * asserted only when the collector recorded `raw-identifiers-enabled` for the
152
+ * run (it does when HBOM generation was asked to include sensitive
153
+ * identifiers); a redacted run never yields identifiers, whichever command
154
+ * renders it.
155
+ *
156
+ * @param {object} internalBom The 1.x-shaped internal model
157
+ * @param {object} [locations] Passed to loadHardwareMapper
158
+ * @returns {Promise<{hardwareMapper?: Function, hardwareOptions?: {includeSensitiveIdentifiers: boolean, identifierPolicy?: string}}>}
159
+ * The render context
160
+ */
161
+ export async function hardwareRenderContext(internalBom, locations) {
162
+ if (!hasHardwareComponent(internalBom)) {
163
+ return {};
164
+ }
165
+ const hardwareMapper = await loadHardwareMapper(locations);
166
+ if (!hardwareMapper) {
167
+ return {};
168
+ }
169
+ const identifierPolicy = recordedIdentifierPolicy(internalBom);
170
+ const hardwareOptions = {
171
+ includeSensitiveIdentifiers: identifierPolicy === RAW_IDENTIFIER_POLICY,
172
+ };
173
+ if (identifierPolicy !== undefined) {
174
+ hardwareOptions.identifierPolicy = identifierPolicy;
175
+ }
176
+ return { hardwareMapper, hardwareOptions };
177
+ }
178
+
179
+ function hasHardwareComponent(internalBom) {
180
+ const pending = [
181
+ internalBom?.metadata?.component,
182
+ ...(Array.isArray(internalBom?.components) ? internalBom.components : []),
183
+ ];
184
+ while (pending.length) {
185
+ const component = pending.pop();
186
+ if (!component || typeof component !== "object") {
187
+ continue;
188
+ }
189
+ if (
190
+ (component.properties || []).some((property) =>
191
+ `${property?.name ?? ""}`.startsWith("cdx:hbom:"),
192
+ )
193
+ ) {
194
+ return true;
195
+ }
196
+ if (Array.isArray(component.components)) {
197
+ pending.push(...component.components);
198
+ }
199
+ }
200
+ return false;
201
+ }