@cdxgen/cdxgen 13.3.0 → 13.5.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 (372) hide show
  1. package/README.md +12 -9
  2. package/bin/cdxgen-secure.js +15 -0
  3. package/bin/cdxgen.js +115 -31
  4. package/bin/evinse.js +23 -1
  5. package/bin/hbom.js +15 -7
  6. package/bin/repl.js +115 -0
  7. package/bin/sign.js +45 -15
  8. package/bin/tracebom.js +5 -1
  9. package/bin/validate.js +84 -18
  10. package/bin/verify.js +155 -86
  11. package/data/cbomosdb-queries.json +13 -1
  12. package/data/component-tags.json +8 -0
  13. package/data/helpers/init.gradle +46 -21
  14. package/data/queries-darwin.json +25 -1
  15. package/data/queries-win.json +19 -1
  16. package/data/queries.json +41 -0
  17. package/data/remediations.json +344 -1
  18. package/data/rules/cbom-security.yaml +235 -4
  19. package/data/rules/dependency-sources.yaml +67 -0
  20. package/data/rules/go-binaries.yaml +46 -0
  21. package/data/rules/golem-go.yaml +80 -0
  22. package/data/rules/hbom-compliance.yaml +40 -22
  23. package/data/rules/hbom-performance.yaml +7 -1
  24. package/data/rules/host-topology.yaml +1 -1
  25. package/data/rules/obom-runtime.yaml +31 -2
  26. package/data/rules/php-runtime.yaml +143 -0
  27. package/data/rules/scala-evidence.yaml +115 -0
  28. package/lib/audit/index.js +52 -6
  29. package/lib/cli/bomAssembly.js +28 -3
  30. package/lib/cli/cliOptions.js +110 -3
  31. package/lib/cli/index.js +186 -58
  32. package/lib/cli/jsBom.js +50 -29
  33. package/lib/cli/jvmBom.js +779 -146
  34. package/lib/cli/managedBom.js +1021 -261
  35. package/lib/cli/nativeBom.js +364 -58
  36. package/lib/core/activity.js +12 -0
  37. package/lib/core/env.js +47 -17
  38. package/lib/core/fs.js +31 -21
  39. package/lib/core/httpClient.js +233 -69
  40. package/lib/core/pluginRun.js +184 -0
  41. package/lib/core/processTree.js +100 -0
  42. package/lib/core/projectConfig.js +1 -0
  43. package/lib/core/projectTooling.js +97 -0
  44. package/lib/core/propertySanitizer.js +72 -0
  45. package/lib/core/runState.js +39 -0
  46. package/lib/core/toml.js +72 -0
  47. package/lib/ecosystems/asarutils.js +4 -2
  48. package/lib/ecosystems/bunutils.js +5 -7
  49. package/lib/ecosystems/cargoCache.js +231 -8
  50. package/lib/ecosystems/cmakeResolver.js +10 -21
  51. package/lib/ecosystems/cppBuildContext.js +631 -0
  52. package/lib/ecosystems/cppEvidence.js +337 -22
  53. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  54. package/lib/ecosystems/denoutils.js +93 -66
  55. package/lib/ecosystems/ecosystems.js +1967 -788
  56. package/lib/ecosystems/golem.js +578 -18
  57. package/lib/ecosystems/gradleutils.js +340 -46
  58. package/lib/ecosystems/jsEvidence.js +85 -24
  59. package/lib/ecosystems/kosi.js +21 -4
  60. package/lib/ecosystems/npmutils.js +64 -0
  61. package/lib/ecosystems/parsers-dotnet.js +668 -153
  62. package/lib/ecosystems/parsers-elm.js +46 -28
  63. package/lib/ecosystems/parsers-gleam.js +3 -4
  64. package/lib/ecosystems/parsers-go.js +1283 -297
  65. package/lib/ecosystems/parsers-js.js +92 -26
  66. package/lib/ecosystems/parsers-julia.js +2 -3
  67. package/lib/ecosystems/parsers-jvm.js +463 -29
  68. package/lib/ecosystems/parsers-misc.js +405 -93
  69. package/lib/ecosystems/parsers-python.js +152 -20
  70. package/lib/ecosystems/parsers-rust.js +4 -4
  71. package/lib/ecosystems/parsers-terraform.js +1573 -19
  72. package/lib/ecosystems/pythonutils.js +102 -6
  73. package/lib/ecosystems/remote/huggingface.js +3 -0
  74. package/lib/ecosystems/rubyutils.js +73 -20
  75. package/lib/ecosystems/rusi.js +32 -9
  76. package/lib/ecosystems/sbtutils.js +243 -187
  77. package/lib/ecosystems/scalacliutils.js +343 -0
  78. package/lib/ecosystems/scalasem.js +2620 -0
  79. package/lib/ecosystems/terraformRegistry.js +288 -0
  80. package/lib/ecosystems/utils.js +2 -0
  81. package/lib/ecosystems/vendoredCode.js +156 -0
  82. package/lib/evinser/evinser.js +695 -78
  83. package/lib/evinser/swiftsem.js +68 -5
  84. package/lib/helpers/agenticToolutils.js +2 -2
  85. package/lib/helpers/bomSigner.js +576 -128
  86. package/lib/inventory/agentSkillSpec.js +17 -0
  87. package/lib/inventory/aiCollector.js +4 -1
  88. package/lib/inventory/analyzer.js +110 -127
  89. package/lib/inventory/apkInstalledDb.js +243 -0
  90. package/lib/inventory/atomUtils.js +204 -48
  91. package/lib/inventory/auditCategories.js +10 -3
  92. package/lib/inventory/buildToolRateLimit.js +144 -0
  93. package/lib/inventory/cacheCommands.js +2 -1
  94. package/lib/inventory/cacheDir.js +52 -9
  95. package/lib/inventory/certificates.js +2371 -0
  96. package/lib/inventory/cmakeBuildDirs.js +254 -0
  97. package/lib/inventory/communityAiConfigParser.js +4 -9
  98. package/lib/inventory/deps.js +507 -128
  99. package/lib/inventory/display.js +200 -3
  100. package/lib/inventory/dosai.js +747 -101
  101. package/lib/inventory/dosaiParsers.js +310 -15
  102. package/lib/inventory/dynamic.js +96 -8
  103. package/lib/inventory/envcontext.js +168 -5
  104. package/lib/inventory/evidenceUtils.js +590 -132
  105. package/lib/inventory/fetchBatch.js +574 -38
  106. package/lib/inventory/fetchDiskCache.js +597 -0
  107. package/lib/inventory/forgeEnricher.js +14 -5
  108. package/lib/inventory/formulationParsers.js +3 -2
  109. package/lib/inventory/goBuildInfo.js +140 -0
  110. package/lib/inventory/hbom.js +67 -7
  111. package/lib/inventory/hbomAnalysis.js +16 -0
  112. package/lib/inventory/hostTopology.js +21 -0
  113. package/lib/inventory/jvmLocalRepos.js +917 -0
  114. package/lib/inventory/mcpConfigParser.js +2 -3
  115. package/lib/inventory/osPackagePurl.js +244 -0
  116. package/lib/inventory/osPackageResolver.js +89 -87
  117. package/lib/inventory/osinfo.js +79 -31
  118. package/lib/inventory/osqueryTransform.js +114 -5
  119. package/lib/inventory/parentSelection.js +146 -0
  120. package/lib/inventory/phpBuildInfo.js +487 -0
  121. package/lib/inventory/phpHostProbe.js +641 -0
  122. package/lib/inventory/rootfsCerts.js +426 -0
  123. package/lib/inventory/rustFormulationParser.js +3 -4
  124. package/lib/inventory/scalaCoords.js +153 -0
  125. package/lib/inventory/source.js +215 -11
  126. package/lib/inventory/spdx.js +245 -0
  127. package/lib/inventory/traceRunner.js +93 -8
  128. package/lib/inventory/tracedCrypto.js +423 -0
  129. package/lib/managers/binary.js +529 -183
  130. package/lib/managers/docker.js +199 -44
  131. package/lib/managers/oci.js +24 -2
  132. package/lib/parsers/cmakeCache.js +69 -0
  133. package/lib/parsers/cmakeLists.js +16 -2
  134. package/lib/parsers/cmakePresets.js +400 -0
  135. package/lib/parsers/compileCommands.js +557 -0
  136. package/lib/parsers/email.js +277 -0
  137. package/lib/parsers/largeJson.js +34 -2
  138. package/lib/parsers/strictJson.js +322 -0
  139. package/lib/parsers/terraformConfig.js +1073 -0
  140. package/lib/parsers/terraformSource.js +652 -0
  141. package/lib/server/server.js +209 -1
  142. package/lib/stages/postgen/annotator.js +17 -1
  143. package/lib/stages/postgen/introspection/markers.js +1 -1
  144. package/lib/stages/postgen/introspection/reflect.js +4 -1
  145. package/lib/stages/postgen/postgen.js +20 -6
  146. package/lib/stages/postgen/ruleEngine.js +58 -1
  147. package/lib/stages/postgen/specVersionCompat.js +62 -0
  148. package/lib/stages/pregen/pregen.js +117 -5
  149. package/lib/validator/bomValidator.js +26 -5
  150. package/lib/validator/generated/runtime.mjs +13 -0
  151. package/lib/validator/generated/validate-1.6.mjs +26 -7
  152. package/lib/validator/generated/validate-1.7.mjs +31 -12
  153. package/lib/validator/generated/validate-2.0.mjs +30 -11
  154. package/lib/validator/index.js +25 -10
  155. package/lib/validator/reporters/json.js +1 -0
  156. package/package.json +23 -23
  157. package/types/bin/cdxgen-secure.d.ts +3 -0
  158. package/types/bin/cdxgen-secure.d.ts.map +1 -0
  159. package/types/bin/repl.d.ts.map +1 -1
  160. package/types/lib/audit/index.d.ts.map +1 -1
  161. package/types/lib/cli/bomAssembly.d.ts +9 -2
  162. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  163. package/types/lib/cli/cliOptions.d.ts +43 -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.map +1 -1
  167. package/types/lib/cli/jvmBom.d.ts +44 -1
  168. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  169. package/types/lib/cli/managedBom.d.ts +7 -4
  170. package/types/lib/cli/managedBom.d.ts.map +1 -1
  171. package/types/lib/cli/nativeBom.d.ts +3 -1
  172. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  173. package/types/lib/core/activity.d.ts.map +1 -1
  174. package/types/lib/core/env.d.ts +27 -2
  175. package/types/lib/core/env.d.ts.map +1 -1
  176. package/types/lib/core/fs.d.ts.map +1 -1
  177. package/types/lib/core/httpClient.d.ts +13 -8
  178. package/types/lib/core/httpClient.d.ts.map +1 -1
  179. package/types/lib/core/pluginRun.d.ts +97 -0
  180. package/types/lib/core/pluginRun.d.ts.map +1 -0
  181. package/types/lib/core/processTree.d.ts +36 -0
  182. package/types/lib/core/processTree.d.ts.map +1 -0
  183. package/types/lib/core/projectConfig.d.ts.map +1 -1
  184. package/types/lib/core/projectTooling.d.ts +41 -0
  185. package/types/lib/core/projectTooling.d.ts.map +1 -0
  186. package/types/lib/core/propertySanitizer.d.ts +26 -0
  187. package/types/lib/core/propertySanitizer.d.ts.map +1 -1
  188. package/types/lib/core/runState.d.ts +29 -0
  189. package/types/lib/core/runState.d.ts.map +1 -0
  190. package/types/lib/core/toml.d.ts +20 -0
  191. package/types/lib/core/toml.d.ts.map +1 -0
  192. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  193. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  194. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  195. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  196. package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
  197. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  198. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  199. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  200. package/types/lib/ecosystems/cppEvidence.d.ts +19 -3
  201. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  202. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  203. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  204. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  205. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  206. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  207. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  208. package/types/lib/ecosystems/golem.d.ts +81 -1
  209. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  210. package/types/lib/ecosystems/gradleutils.d.ts +59 -7
  211. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  212. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  213. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  214. package/types/lib/ecosystems/npmutils.d.ts +15 -0
  215. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  216. package/types/lib/ecosystems/parsers-dotnet.d.ts +155 -5
  217. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  218. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  219. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  220. package/types/lib/ecosystems/parsers-go.d.ts +119 -5
  221. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  222. package/types/lib/ecosystems/parsers-js.d.ts +6 -1
  223. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  224. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  225. package/types/lib/ecosystems/parsers-jvm.d.ts +74 -3
  226. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  227. package/types/lib/ecosystems/parsers-misc.d.ts +21 -59
  228. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  229. package/types/lib/ecosystems/parsers-python.d.ts +57 -6
  230. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  231. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  232. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  233. package/types/lib/ecosystems/pythonutils.d.ts +26 -1
  234. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  235. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  236. package/types/lib/ecosystems/rubyutils.d.ts +21 -1
  237. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  238. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  239. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  240. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  241. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  242. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  243. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  244. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  245. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  246. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  247. package/types/lib/ecosystems/utils.d.ts +2 -2
  248. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  249. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  250. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  251. package/types/lib/evinser/evinser.d.ts +100 -8
  252. package/types/lib/evinser/evinser.d.ts.map +1 -1
  253. package/types/lib/evinser/swiftsem.d.ts +10 -0
  254. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  255. package/types/lib/helpers/bomSigner.d.ts +80 -4
  256. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  257. package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
  258. package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
  259. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  260. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  261. package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
  262. package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
  263. package/types/lib/inventory/atomUtils.d.ts +96 -10
  264. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  265. package/types/lib/inventory/auditCategories.d.ts +3 -0
  266. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  267. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  268. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  269. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  270. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  271. package/types/lib/inventory/cacheDir.d.ts +12 -4
  272. package/types/lib/inventory/cacheDir.d.ts.map +1 -1
  273. package/types/lib/inventory/certificates.d.ts +205 -0
  274. package/types/lib/inventory/certificates.d.ts.map +1 -0
  275. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  276. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  277. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  278. package/types/lib/inventory/deps.d.ts +61 -2
  279. package/types/lib/inventory/deps.d.ts.map +1 -1
  280. package/types/lib/inventory/display.d.ts +18 -0
  281. package/types/lib/inventory/display.d.ts.map +1 -1
  282. package/types/lib/inventory/dosai.d.ts +202 -14
  283. package/types/lib/inventory/dosai.d.ts.map +1 -1
  284. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  285. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  286. package/types/lib/inventory/dynamic.d.ts +16 -1
  287. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  288. package/types/lib/inventory/envcontext.d.ts +40 -2
  289. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  290. package/types/lib/inventory/evidenceUtils.d.ts +48 -14
  291. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  292. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  293. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  294. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  295. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  296. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  297. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  298. package/types/lib/inventory/goBuildInfo.d.ts +10 -0
  299. package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
  300. package/types/lib/inventory/hbom.d.ts.map +1 -1
  301. package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
  302. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
  303. package/types/lib/inventory/hostTopology.d.ts.map +1 -1
  304. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  305. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  306. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  307. package/types/lib/inventory/osPackagePurl.d.ts +99 -0
  308. package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
  309. package/types/lib/inventory/osPackageResolver.d.ts +6 -2
  310. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  311. package/types/lib/inventory/osinfo.d.ts +31 -6
  312. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  313. package/types/lib/inventory/osqueryTransform.d.ts +51 -0
  314. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
  315. package/types/lib/inventory/parentSelection.d.ts +34 -0
  316. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  317. package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
  318. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
  319. package/types/lib/inventory/phpHostProbe.d.ts +35 -0
  320. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
  321. package/types/lib/inventory/rootfsCerts.d.ts +27 -0
  322. package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
  323. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  324. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  325. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  326. package/types/lib/inventory/source.d.ts +28 -0
  327. package/types/lib/inventory/source.d.ts.map +1 -1
  328. package/types/lib/inventory/spdx.d.ts +24 -0
  329. package/types/lib/inventory/spdx.d.ts.map +1 -1
  330. package/types/lib/inventory/traceRunner.d.ts +16 -1
  331. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  332. package/types/lib/inventory/tracedCrypto.d.ts +64 -0
  333. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  334. package/types/lib/managers/binary.d.ts +10 -0
  335. package/types/lib/managers/binary.d.ts.map +1 -1
  336. package/types/lib/managers/docker.d.ts +12 -0
  337. package/types/lib/managers/docker.d.ts.map +1 -1
  338. package/types/lib/managers/oci.d.ts +3 -1
  339. package/types/lib/managers/oci.d.ts.map +1 -1
  340. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  341. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  342. package/types/lib/parsers/cmakeLists.d.ts +2 -1
  343. package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
  344. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  345. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  346. package/types/lib/parsers/compileCommands.d.ts +115 -0
  347. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  348. package/types/lib/parsers/email.d.ts +19 -0
  349. package/types/lib/parsers/email.d.ts.map +1 -0
  350. package/types/lib/parsers/largeJson.d.ts +13 -0
  351. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  352. package/types/lib/parsers/strictJson.d.ts +46 -0
  353. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  354. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  355. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  356. package/types/lib/parsers/terraformSource.d.ts +66 -0
  357. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  358. package/types/lib/server/server.d.ts +8 -0
  359. package/types/lib/server/server.d.ts.map +1 -1
  360. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  361. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
  362. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  363. package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
  364. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  365. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  366. package/types/lib/stages/pregen/pregen.d.ts +14 -1
  367. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  368. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  369. package/types/lib/validator/index.d.ts +8 -0
  370. package/types/lib/validator/index.d.ts.map +1 -1
  371. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  372. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
@@ -77,6 +77,7 @@ export function parseCommand(cmdStr) {
77
77
  export function resolveSaferExecBinary() {
78
78
  const currentPlatform = platform();
79
79
  const currentArch = arch();
80
+ const isWindows = currentPlatform === "win32";
80
81
  let pkgName = "";
81
82
 
82
83
  if (currentPlatform === "darwin") {
@@ -91,6 +92,14 @@ export function resolveSaferExecBinary() {
91
92
  } else if (currentArch === "arm64") {
92
93
  pkgName = "@cdxgen/safer-exec-linux-arm64";
93
94
  }
95
+ } else if (isWindows) {
96
+ // The Windows packages ship the MXC executor (wxc-exec.exe) next to the
97
+ // engine, which is where the engine looks for it.
98
+ if (currentArch === "x64") {
99
+ pkgName = "@cdxgen/safer-exec-windows-amd64";
100
+ } else if (currentArch === "arm64") {
101
+ pkgName = "@cdxgen/safer-exec-windows-arm64";
102
+ }
94
103
  }
95
104
 
96
105
  if (!pkgName) {
@@ -117,9 +126,14 @@ export function resolveSaferExecBinary() {
117
126
 
118
127
  for (const modulesDir of searchDirs) {
119
128
  // Direct structure under node_modules. The Go runtime inside the
120
- // platform package is bin/safer-exec-rt; "safer-exec" is the JS
129
+ // platform package is bin/safer-exec-rt(.exe); "safer-exec" is the JS
121
130
  // dispatcher's npm bin name, not a file on disk.
122
- const directPath = join(modulesDir, pkgName, "bin", "safer-exec-rt");
131
+ const directPath = join(
132
+ modulesDir,
133
+ pkgName,
134
+ "bin",
135
+ isWindows ? "safer-exec-rt.exe" : "safer-exec-rt",
136
+ );
123
137
  let realDirectPath;
124
138
  try {
125
139
  realDirectPath = realpathSync(directPath);
@@ -127,10 +141,12 @@ export function resolveSaferExecBinary() {
127
141
  realDirectPath = directPath;
128
142
  }
129
143
  if (existsSync(realDirectPath)) {
130
- try {
131
- chmodSync(realDirectPath, 0o755);
132
- } catch (_err) {
133
- // ignore
144
+ if (!isWindows) {
145
+ try {
146
+ chmodSync(realDirectPath, 0o755);
147
+ } catch (_err) {
148
+ // ignore
149
+ }
134
150
  }
135
151
  return realDirectPath;
136
152
  }
@@ -179,7 +195,7 @@ export function resolveSaferExecBinary() {
179
195
  * @param {boolean} [options.denyPersistenceWrites] - Deny writes to auto-execution and persistence locations
180
196
  * @param {boolean} [options.privateTmp] - Private tmpfs /tmp and /var/tmp (Linux)
181
197
  * @param {string} [options.protectHome] - Home isolation: read-only, tmpfs, or off (Linux)
182
- * @returns {Promise<{libPaths: string[], httpAccessEntries: Object[], egressEntries?: Object[], dryRunSummary?: Object}>} Collected libraries, HTTP URLs, proxy egress decisions, and the dry-run summary
198
+ * @returns {Promise<{libPaths: string[], httpAccessEntries: Object[], cryptoEntries?: Object[], cryptoComponents?: Object[], cryptoResult?: Object, egressEntries?: Object[], dryRunSummary?: Object}>} Collected libraries, HTTP URLs, crypto observations, proxy egress decisions, and the dry-run summary
183
199
  * @throws {Error} When the @cdxgen/safer-exec tracing runtime is not installed
184
200
  * (for example after pruning it from a standalone payload), since the traced
185
201
  * command could never run and the BOM would silently be empty
@@ -252,6 +268,14 @@ export async function executeAndTrace(commandStr, workingDir, options = {}) {
252
268
  if (options.dryRun) {
253
269
  exec.enableDryRun();
254
270
  }
271
+ if (platform() === "win32" && options.blockInterpreters) {
272
+ // safer-exec reports --private-tmp and --protect-home on Windows when a
273
+ // grant defeats them, but --block-interpreters is macOS-only and stays
274
+ // silent (cdxgen/safer-exec#4).
275
+ console.warn(
276
+ "[cdxgen trace] --block-interpreters has no effect on Windows.",
277
+ );
278
+ }
255
279
  if (options.blockInterpreters) {
256
280
  exec.blockInterpreters();
257
281
  }
@@ -376,8 +400,41 @@ export async function executeAndTrace(commandStr, workingDir, options = {}) {
376
400
 
377
401
  const result = await exec.run(cmd, args);
378
402
  const stderr = result?.stderr || "";
403
+ const { warnings, error: engineError } = engineMessages(stderr);
404
+ // Windows records DLL loads through an ETW session, which only an
405
+ // elevated process (or a Performance Log Users member) may start.
406
+ const etwDenied =
407
+ platform() === "win32" &&
408
+ warnings.some(
409
+ (line) =>
410
+ line.includes("DLL tracing:") &&
411
+ line.includes("administrator rights"),
412
+ );
413
+ // run() captures the engine's stderr, so a control the sandbox could not
414
+ // apply would otherwise read like one it did.
415
+ for (const line of warnings) {
416
+ if (etwDenied && line.includes("DLL tracing:")) {
417
+ continue;
418
+ }
419
+ console.warn(line);
420
+ }
421
+ if (etwDenied) {
422
+ console.warn(
423
+ "[cdxgen trace] library tracing on Windows needs an elevated prompt (or membership in the Performance Log Users group); the BOM has no library components. Egress services from --proxy-egress are still recorded.",
424
+ );
425
+ }
379
426
  if (result && result.exitCode !== 0) {
380
- if (stderr.includes("[safer-exec] Error:")) {
427
+ if (engineError) {
428
+ console.error(engineError);
429
+ if (options.strict) {
430
+ // Under --strict the sandbox refused the run; an empty BOM would
431
+ // read as a command that loaded nothing.
432
+ throw Object.assign(
433
+ new Error(`Tracing refused under --strict: ${engineError}`),
434
+ { strictRefusal: true },
435
+ );
436
+ }
437
+ } else if (stderr.includes("[safer-exec] Error:")) {
381
438
  console.error("Tracing launcher execution failed:", stderr.trim());
382
439
  } else {
383
440
  // A failing command usually means a partial trace (for example a
@@ -474,6 +531,9 @@ export async function executeAndTrace(commandStr, workingDir, options = {}) {
474
531
  httpAccessEntries: [...collectedUrls, ...urls],
475
532
  cryptoEntries: [...collectedCrypto, ...cryptoLogs],
476
533
  cryptoComponents,
534
+ // The CRYPTO result from the report channel: the cipher entries carry
535
+ // the certificate chains servers presented (safer-exec ≥ 1.2.0).
536
+ cryptoResult: result?.crypto,
477
537
  egressEntries: parseEgressEntries(result.auditLog),
478
538
  dryRunSummary,
479
539
  };
@@ -484,15 +544,40 @@ export async function executeAndTrace(commandStr, workingDir, options = {}) {
484
544
  httpAccessEntries: collectedUrls,
485
545
  cryptoEntries: collectedCrypto,
486
546
  cryptoComponents,
547
+ cryptoResult: result?.crypto,
487
548
  egressEntries: [],
488
549
  dryRunSummary,
489
550
  };
490
551
  } catch (err) {
552
+ if (err?.strictRefusal) {
553
+ throw err;
554
+ }
491
555
  console.error("Tracing command execution failed:", err);
492
556
  }
493
557
  return emptyResult;
494
558
  }
495
559
 
560
+ /**
561
+ * Picks the safer-exec engine's own lines out of the stderr it shares with the
562
+ * traced command. Warnings start with "safer-exec: warning:"; when the engine
563
+ * stops a run, its error is the last other "safer-exec: " line it prints.
564
+ *
565
+ * @param {string} stderr Captured stderr of a safer-exec run
566
+ * @returns {{warnings: string[], error: string|undefined}} Unique engine warnings, and the engine's error if any
567
+ */
568
+ export function engineMessages(stderr) {
569
+ const lines = String(stderr || "")
570
+ .split(/\r?\n/)
571
+ .filter((line) => line.startsWith("safer-exec: "));
572
+ const warnings = [
573
+ ...new Set(lines.filter((line) => line.startsWith("safer-exec: warning:"))),
574
+ ];
575
+ const others = lines.filter(
576
+ (line) => !line.startsWith("safer-exec: warning:"),
577
+ );
578
+ return { warnings, error: others.at(-1) };
579
+ }
580
+
496
581
  /**
497
582
  * Returns the sorted, de-duplicated parameter names of a query string.
498
583
  *
@@ -0,0 +1,423 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import { thoughtLog } from "../core/logger.js";
5
+ import { dirNameStr } from "../core/paths.js";
6
+ import {
7
+ CertificateCollector,
8
+ certRecordFromObservedChainEntry,
9
+ toWellFormedText,
10
+ } from "./certificates.js";
11
+ import { applyAlgorithmProperties } from "./cryptoAlgorithmFamily.js";
12
+
13
+ const cryptoOids = JSON.parse(
14
+ readFileSync(join(dirNameStr, "data", "crypto-oid.json"), "utf-8"),
15
+ );
16
+
17
+ // TLS key-exchange groups by their IANA registry name. A hybrid group pairs a
18
+ // classical curve with ML-KEM: each half has an OID, the pair has none, so the
19
+ // group is recorded as both algorithms.
20
+ const GROUP_ALGORITHMS = {
21
+ X25519MLKEM768: ["x25519", "ml-kem-768"],
22
+ SecP256r1MLKEM768: ["secp256r1", "ml-kem-768"],
23
+ SecP384r1MLKEM1024: ["secp384r1", "ml-kem-1024"],
24
+ X25519Kyber768Draft00: ["x25519"],
25
+ MLKEM512: ["ml-kem-512"],
26
+ MLKEM768: ["ml-kem-768"],
27
+ MLKEM1024: ["ml-kem-1024"],
28
+ x448: ["curveX448"],
29
+ };
30
+
31
+ const MLKEM_SECURITY_LEVELS = {
32
+ "ml-kem-512": 1,
33
+ "ml-kem-768": 3,
34
+ "ml-kem-1024": 5,
35
+ };
36
+
37
+ // The curve each classical key-exchange algorithm is defined over.
38
+ const KEY_AGREEMENT_CURVES = {
39
+ x25519: "x25519",
40
+ curveX448: "x448",
41
+ };
42
+
43
+ // Closed enums of cryptoProperties.algorithmProperties in bom-1.7.schema.json.
44
+ const MODES = new Set(["cbc", "ecb", "ccm", "gcm", "cfb", "ofb", "ctr"]);
45
+ const CRYPTO_FUNCTIONS = new Set([
46
+ "generate",
47
+ "keygen",
48
+ "encrypt",
49
+ "decrypt",
50
+ "digest",
51
+ "tag",
52
+ "keyderive",
53
+ "sign",
54
+ "verify",
55
+ "encapsulate",
56
+ "decapsulate",
57
+ ]);
58
+
59
+ /**
60
+ * Names the bulk cipher of a TLS cipher suite as a crypto-oid.json key, from
61
+ * either its IANA name (TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) or its OpenSSL
62
+ * name (ECDHE-RSA-AES128-GCM-SHA256).
63
+ *
64
+ * @param {string} suiteName Cipher suite name
65
+ * @returns {{name: string, bits: number, mode: string|undefined}|undefined} The cipher, when it has an OID
66
+ */
67
+ export function suiteCipher(suiteName) {
68
+ const tokens = String(suiteName || "")
69
+ .toUpperCase()
70
+ .split(/[-_]/);
71
+ let cipher;
72
+ if (tokens.includes("CHACHA20")) {
73
+ cipher = { name: "chaCha20Poly1305", bits: 256, mode: undefined };
74
+ } else if (tokens.includes("3DES") || tokens.includes("CBC3")) {
75
+ cipher = { name: "des-EDE3-CBC", bits: 112, mode: "cbc" };
76
+ } else {
77
+ const aesIdx = tokens.findIndex((t) => t.startsWith("AES"));
78
+ if (aesIdx < 0) {
79
+ return undefined;
80
+ }
81
+ const bits =
82
+ Number.parseInt(tokens[aesIdx].substring(3), 10) ||
83
+ Number.parseInt(tokens[aesIdx + 1], 10);
84
+ const mode = ["GCM", "CCM"].find((m) => tokens.includes(m)) || "CBC";
85
+ cipher = { name: `aes${bits}-${mode}`, bits, mode: mode.toLowerCase() };
86
+ }
87
+ return cryptoOids[cipher.name]?.oid ? cipher : undefined;
88
+ }
89
+
90
+ function oidKey(name) {
91
+ if (!name) {
92
+ return undefined;
93
+ }
94
+ if (cryptoOids[name]?.oid) {
95
+ return name;
96
+ }
97
+ const lower = String(name).toLowerCase();
98
+ return cryptoOids[lower]?.oid ? lower : undefined;
99
+ }
100
+
101
+ function cipherProperties(cipher) {
102
+ const props = {
103
+ primitive: cipher.mode === "cbc" ? "block-cipher" : "ae",
104
+ parameterSetIdentifier: String(cipher.bits),
105
+ cryptoFunctions: ["encrypt", "decrypt"],
106
+ classicalSecurityLevel: cipher.bits,
107
+ };
108
+ if (cipher.mode) {
109
+ props.mode = cipher.mode;
110
+ }
111
+ return props;
112
+ }
113
+
114
+ function keyExchangeProperties(name) {
115
+ const level = MLKEM_SECURITY_LEVELS[name];
116
+ if (level) {
117
+ return {
118
+ primitive: "kem",
119
+ cryptoFunctions: ["keygen", "encapsulate", "decapsulate"],
120
+ nistQuantumSecurityLevel: level,
121
+ };
122
+ }
123
+ return { primitive: "key-agree", cryptoFunctions: ["keygen"] };
124
+ }
125
+
126
+ // Keeps the values of an algorithm the traced CBOM described that fit the
127
+ // schema; the primitive itself is checked by applyAlgorithmProperties.
128
+ function knownAlgorithmProperties(props) {
129
+ const known = {};
130
+ if (typeof props?.primitive === "string") {
131
+ known.primitive = props.primitive === "aead" ? "ae" : props.primitive;
132
+ }
133
+ const mode = String(props?.mode || "").toLowerCase();
134
+ if (MODES.has(mode)) {
135
+ known.mode = mode;
136
+ }
137
+ const functions = (props?.cryptoFunctions || []).filter((f) =>
138
+ CRYPTO_FUNCTIONS.has(f),
139
+ );
140
+ if (functions.length) {
141
+ known.cryptoFunctions = functions;
142
+ }
143
+ for (const level of ["classicalSecurityLevel", "nistQuantumSecurityLevel"]) {
144
+ if (Number.isInteger(props?.[level])) {
145
+ known[level] = props[level];
146
+ }
147
+ }
148
+ return known;
149
+ }
150
+
151
+ /**
152
+ * Turns the CBOM safer-exec writes for a traced command into components that
153
+ * cdxgen can merge and validate:
154
+ *
155
+ * - every asset gets a bom-ref, without which the assets merge into one
156
+ * - algorithms carry their OID, and those without one are left out
157
+ * - hybrid key-exchange groups become their classical and ML-KEM halves
158
+ * - cipher suites become their bulk cipher, which the protocol's suites refer
159
+ * to by bom-ref
160
+ * - algorithm properties keep only values the schema allows
161
+ * - crypto libraries are left out: they are the traced library components,
162
+ * marked with cdx:dynamic:cryptoLibrary
163
+ *
164
+ * @param {Object[]} cbomComponents Components of the safer-exec CBOM
165
+ * @returns {Object[]} Normalized cryptographic-asset components
166
+ */
167
+ export function normalizeTracedCryptoComponents(cbomComponents) {
168
+ const byRef = new Map();
169
+ const addAlgorithm = (name, algorithmProperties, curve) => {
170
+ const oid = cryptoOids[name].oid;
171
+ const ref = `crypto/algorithm/${encodeURIComponent(name)}@${oid}`;
172
+ if (!byRef.has(ref)) {
173
+ const component = {
174
+ type: "cryptographic-asset",
175
+ name,
176
+ "bom-ref": ref,
177
+ description: cryptoOids[name].description,
178
+ cryptoProperties: {
179
+ assetType: "algorithm",
180
+ algorithmProperties,
181
+ oid,
182
+ },
183
+ };
184
+ applyAlgorithmProperties(component, { name, curve });
185
+ byRef.set(ref, component);
186
+ }
187
+ return ref;
188
+ };
189
+ const protocols = [];
190
+ for (const comp of cbomComponents || []) {
191
+ const props = comp?.cryptoProperties;
192
+ if (comp?.type !== "cryptographic-asset" || !props) {
193
+ continue;
194
+ }
195
+ if (props.assetType === "protocol") {
196
+ protocols.push(comp);
197
+ continue;
198
+ }
199
+ if (props.assetType !== "algorithm") {
200
+ continue;
201
+ }
202
+ const groupHalves = GROUP_ALGORITHMS[comp.name];
203
+ if (groupHalves) {
204
+ for (const half of groupHalves) {
205
+ addAlgorithm(
206
+ half,
207
+ keyExchangeProperties(half),
208
+ KEY_AGREEMENT_CURVES[half] ||
209
+ (MLKEM_SECURITY_LEVELS[half] ? "" : half),
210
+ );
211
+ }
212
+ continue;
213
+ }
214
+ const name = oidKey(comp.name);
215
+ if (name) {
216
+ addAlgorithm(
217
+ name,
218
+ MLKEM_SECURITY_LEVELS[name] || KEY_AGREEMENT_CURVES[name]
219
+ ? keyExchangeProperties(name)
220
+ : knownAlgorithmProperties(props.algorithmProperties),
221
+ KEY_AGREEMENT_CURVES[name] ||
222
+ (/^(secp|brainpool)/.test(name) ? name : ""),
223
+ );
224
+ continue;
225
+ }
226
+ const cipher = suiteCipher(comp.name);
227
+ if (cipher) {
228
+ addAlgorithm(cipher.name, cipherProperties(cipher));
229
+ continue;
230
+ }
231
+ thoughtLog(`Skipping traced algorithm '${comp.name}' without an OID.`);
232
+ }
233
+ for (const comp of protocols) {
234
+ const ref = `crypto/protocol/${encodeURIComponent(comp.name)}@${encodeURIComponent(comp.version || "unknown")}`;
235
+ const protocolProperties = {
236
+ ...comp.cryptoProperties.protocolProperties,
237
+ };
238
+ // The traced suites list algorithm names; the schema wants bom-refs.
239
+ const cipherSuites = (protocolProperties.cipherSuites || []).map(
240
+ ({ algorithms: _names, ...suite }) => {
241
+ const cipher = suiteCipher(suite.name);
242
+ return cipher
243
+ ? {
244
+ ...suite,
245
+ algorithms: [addAlgorithm(cipher.name, cipherProperties(cipher))],
246
+ }
247
+ : suite;
248
+ },
249
+ );
250
+ const existing = byRef.get(ref);
251
+ if (existing) {
252
+ const suites = existing.cryptoProperties.protocolProperties.cipherSuites;
253
+ for (const suite of cipherSuites) {
254
+ if (!suites.some((s) => s.name === suite.name)) {
255
+ suites.push(suite);
256
+ }
257
+ }
258
+ continue;
259
+ }
260
+ protocolProperties.cipherSuites = cipherSuites;
261
+ const component = {
262
+ type: "cryptographic-asset",
263
+ name: comp.name,
264
+ "bom-ref": ref,
265
+ cryptoProperties: {
266
+ assetType: "protocol",
267
+ protocolProperties,
268
+ },
269
+ };
270
+ if (comp.version) {
271
+ component.version = comp.version;
272
+ }
273
+ byRef.set(ref, component);
274
+ }
275
+ return Array.from(byRef.values());
276
+ }
277
+
278
+ // safer-exec walks at most this many certificates of one server's chain
279
+ // (tlsobserve maxChainCerts); a longer array did not come from it, so only the
280
+ // first entries are read.
281
+ const MAX_CHAIN_CERTS = 10;
282
+
283
+ /**
284
+ * The bom-ref normalizeTracedCryptoComponents gives the protocol component of
285
+ * a negotiated TLS version: safer-exec's CBOM names it "TLSv1.2" with the
286
+ * version the prefix leaves ("1.2").
287
+ *
288
+ * @param {string} protocol negotiated protocol name from the crypto result
289
+ * @returns {string} protocol component bom-ref
290
+ */
291
+ function tracedProtocolBomRef(protocol) {
292
+ const name = String(protocol || "");
293
+ const version = name.startsWith("TLSv") ? name.slice("TLSv".length) : name;
294
+ return `crypto/protocol/${encodeURIComponent(name)}@${encodeURIComponent(version || "unknown")}`;
295
+ }
296
+
297
+ /**
298
+ * Builds the shared certificate model for the chains servers presented in TLS
299
+ * ≤ 1.2 handshakes, from the `chain` arrays of safer-exec's crypto result
300
+ * (report channel, safer-exec ≥ 1.2.0; earlier releases and TLS 1.3 entries
301
+ * carry no chain and yield nothing). The records go through the shared
302
+ * certificate collector, so bom-refs, properties and dedup match certificates
303
+ * found on disk. A presented chain is untrusted server input: no issuer edges
304
+ * are inferred from it, and the certificate and public-key components link
305
+ * through the collector as every producer's do. Each TLS protocol component
306
+ * provides the certificates and keys presented under it — the edge safer-
307
+ * exec's own CBOM records — which answers which servers presented a key.
308
+ *
309
+ * @param {Object} cryptoResult safer-exec CRYPTO result (`result.crypto`)
310
+ * @param {Set<string>} protocolRefs bom-refs of the protocol components in the BOM;
311
+ * a provides edge is only emitted for a protocol that is actually present
312
+ * @returns {{components: Object[], dependencies: Object[]}} certificate,
313
+ * public-key and algorithm components, and the protocol provides edges
314
+ */
315
+ export function observedChainComponents(
316
+ cryptoResult,
317
+ protocolRefs = new Set(),
318
+ ) {
319
+ const collector = new CertificateCollector();
320
+ // Protocol name -> {certSha256, spkiSha256} pairs in presentation order,
321
+ // leaf first; sorted to refs at the end so the output never depends on the
322
+ // connection order the engine happened to report.
323
+ const chainEntriesByProtocol = new Map();
324
+ // The collector keeps the first record's fields for a certSha256, so two
325
+ // servers presenting one hash with conflicting text would otherwise make
326
+ // the name (and bom-ref) depend on connection order; sort first.
327
+ const ciphers = (
328
+ Array.isArray(cryptoResult?.ciphers) ? cryptoResult.ciphers : []
329
+ )
330
+ .map((cipher) => [JSON.stringify(cipher) ?? "", cipher])
331
+ .sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
332
+ .map(([, cipher]) => cipher);
333
+ for (const cipher of ciphers) {
334
+ const chain = cipher?.chain;
335
+ if (!Array.isArray(chain) || chain.length === 0) {
336
+ // safer-exec ≤ 1.1.2, or a TLS 1.3 handshake (encrypted Certificate
337
+ // message): no chain was observed.
338
+ continue;
339
+ }
340
+ const host = typeof cipher.host === "string" ? cipher.host : "";
341
+ for (const entry of chain.slice(0, MAX_CHAIN_CERTS)) {
342
+ const record = certRecordFromObservedChainEntry(entry, { host });
343
+ if (record) {
344
+ collector.addRecord(record);
345
+ }
346
+ }
347
+ // A protocol name is free text from the report channel; it only reaches a
348
+ // bom-ref through encodeURIComponent, so it must be well-formed.
349
+ const protocol = toWellFormedText(cipher.protocol);
350
+ if (protocol) {
351
+ let entries = chainEntriesByProtocol.get(protocol);
352
+ if (!entries) {
353
+ entries = [];
354
+ chainEntriesByProtocol.set(protocol, entries);
355
+ }
356
+ for (const entry of chain.slice(0, MAX_CHAIN_CERTS)) {
357
+ const certSha256 = String(entry?.certSha256 || "").toLowerCase();
358
+ const spkiSha256 = String(entry?.spkiSha256 || "").toLowerCase();
359
+ if (certSha256.length === 64) {
360
+ entries.push({ certSha256, spkiSha256 });
361
+ }
362
+ }
363
+ }
364
+ }
365
+ const { components, dependencies } = collector.build();
366
+ // The collector's components identify their asset by the hash in `version`
367
+ // (certSha256 for certificates, spkiSha256 for public keys).
368
+ const certRefBySha = new Map();
369
+ const keyRefBySpki = new Map();
370
+ for (const component of components) {
371
+ const assetType = component.cryptoProperties?.assetType;
372
+ if (assetType === "certificate") {
373
+ certRefBySha.set(component.version, component["bom-ref"]);
374
+ } else if (assetType === "related-crypto-material") {
375
+ keyRefBySpki.set(component.version, component["bom-ref"]);
376
+ }
377
+ }
378
+ const outputDependencies = [...dependencies];
379
+ for (const [protocol, entries] of chainEntriesByProtocol) {
380
+ const protocolRef = tracedProtocolBomRef(protocol);
381
+ if (!protocolRefs.has(protocolRef)) {
382
+ continue;
383
+ }
384
+ const provides = [
385
+ ...new Set(
386
+ entries.flatMap(({ certSha256, spkiSha256 }) => [
387
+ certRefBySha.get(certSha256),
388
+ keyRefBySpki.get(spkiSha256),
389
+ ]),
390
+ ),
391
+ ]
392
+ .filter((ref) => ref)
393
+ .sort();
394
+ if (provides.length) {
395
+ outputDependencies.push({ ref: protocolRef, provides });
396
+ }
397
+ }
398
+ outputDependencies.sort((left, right) => left.ref.localeCompare(right.ref));
399
+ return { components, dependencies: outputDependencies };
400
+ }
401
+
402
+ /**
403
+ * Maps the crypto libraries safer-exec reported to the paths they were loaded
404
+ * from.
405
+ *
406
+ * @param {Object[]} cryptoEntries safer-exec crypto-library and crypto-cipher audit entries
407
+ * @returns {Map<string, {name: string, version: string}>} Crypto libraries by path
408
+ */
409
+ export function cryptoLibrariesByPath(cryptoEntries) {
410
+ const libraries = new Map();
411
+ for (const entry of cryptoEntries || []) {
412
+ if (entry?.type !== "crypto-library" || !entry.path || !entry.name) {
413
+ continue;
414
+ }
415
+ if (!libraries.has(entry.path)) {
416
+ libraries.set(entry.path, {
417
+ name: String(entry.name),
418
+ version: entry.version ? String(entry.version) : "",
419
+ });
420
+ }
421
+ }
422
+ return libraries;
423
+ }