@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
package/bin/sign.js CHANGED
@@ -20,6 +20,7 @@ import {
20
20
  getNonCycloneDxErrorMessage,
21
21
  isCycloneDxBom,
22
22
  } from "../lib/inventory/bomUtils.js";
23
+ import { parseStrictJson } from "../lib/parsers/strictJson.js";
23
24
 
24
25
  const _yargs = yargs(hideBin(process.argv));
25
26
 
@@ -55,13 +56,19 @@ const args = _yargs
55
56
  type: "array",
56
57
  string: true,
57
58
  description:
58
- "Public key (PEM) of an earlier signer. With --mode chain, every existing chain entry must verify with one of these keys before a new entry is appended.",
59
+ "Public key (PEM) of an earlier signer. Before a new entry is appended, every existing chain entry (--mode chain), or at least one existing entry (--mode signers), must verify with one of these keys.",
59
60
  })
60
61
  .option("allow-unverified-history", {
61
62
  type: "boolean",
62
63
  default: false,
63
64
  description:
64
- "With --mode chain, append even when existing chain entries cannot be verified with --verify-existing-with.",
65
+ "With --mode chain or signers, append even when the existing signature entries cannot be verified with --verify-existing-with.",
66
+ })
67
+ .option("embed-public-key", {
68
+ type: "boolean",
69
+ default: true,
70
+ description:
71
+ "Embed the signer's public key in the root signature, which binds the signature to that key. Not used for HMAC (HS*) algorithms. Pass --no-embed-public-key to leave it out.",
65
72
  })
66
73
  .option("key-id", {
67
74
  description:
@@ -173,16 +180,19 @@ function describeEntry(result) {
173
180
  : `entry ${result.index}`;
174
181
  }
175
182
 
176
- // A new chain entry covers every earlier entry, so the signer vouches for
177
- // that history. Check it with the earlier signers' keys before appending.
178
- function checkChainHistory(bomJson) {
183
+ // A new chain entry covers every earlier entry, so the signer vouches for that
184
+ // history, and every entry must verify. A new signers entry covers the same
185
+ // content as the existing entries, so at least one must verify to show the
186
+ // content is still what an earlier signer signed.
187
+ function checkExistingSignatures(bomJson) {
179
188
  const existing = bomJson.signature;
189
+ const otherForm = args.mode === "chain" ? "signers" : "chain";
180
190
  if (
181
- args.mode !== "chain" ||
191
+ args.mode === "replace" ||
182
192
  existing === undefined ||
183
193
  (existing !== null &&
184
194
  typeof existing === "object" &&
185
- Object.hasOwn(existing, "signers"))
195
+ Object.hasOwn(existing, otherForm))
186
196
  ) {
187
197
  return;
188
198
  }
@@ -192,10 +202,10 @@ function checkChainHistory(bomJson) {
192
202
  }
193
203
  return fs.readFileSync(keyFile, "utf8");
194
204
  });
195
- const unverified = checkSignatureEntries(bomJson, keys).filter(
196
- (result) => !result.verified,
197
- );
198
- if (!unverified.length) {
205
+ const results = checkSignatureEntries(bomJson, keys);
206
+ const unverified = results.filter((result) => !result.verified);
207
+ const chain = args.mode === "chain";
208
+ if (chain ? !unverified.length : unverified.length < results.length) {
199
209
  return;
200
210
  }
201
211
  const details = unverified.map(
@@ -205,7 +215,9 @@ function checkChainHistory(bomJson) {
205
215
  if (args.allowUnverifiedHistory) {
206
216
  console.warn(
207
217
  [
208
- `Warning: appending to a chain with ${unverified.length} unverified entry(s):`,
218
+ chain
219
+ ? `Warning: appending to a chain with ${unverified.length} unverified entry(s):`
220
+ : `Warning: appending a signers entry although none of the ${unverified.length} existing entry(s) verified:`,
209
221
  ...details,
210
222
  ].join("\n"),
211
223
  );
@@ -213,7 +225,9 @@ function checkChainHistory(bomJson) {
213
225
  }
214
226
  throw new Error(
215
227
  [
216
- `the new chain entry would vouch for ${unverified.length} existing entry(s) that could not be verified:`,
228
+ chain
229
+ ? `the new chain entry would vouch for ${unverified.length} existing entry(s) that could not be verified:`
230
+ : `none of the ${unverified.length} existing signature entry(s) could be verified, so the content may have changed since it was signed:`,
217
231
  ...details,
218
232
  "Pass --verify-existing-with <public key> for each earlier signer, or --allow-unverified-history to sign anyway.",
219
233
  ].join("\n"),
@@ -221,7 +235,7 @@ function checkChainHistory(bomJson) {
221
235
  }
222
236
 
223
237
  try {
224
- const bomJson = JSON.parse(fs.readFileSync(args.input, "utf8"));
238
+ const bomJson = parseStrictJson(fs.readFileSync(args.input));
225
239
  if (!isCycloneDxBom(bomJson)) {
226
240
  console.error(getNonCycloneDxErrorMessage(bomJson, "cdx-sign"));
227
241
  process.exit(1);
@@ -229,10 +243,12 @@ try {
229
243
 
230
244
  let signedBom = bomJson;
231
245
  if (hasPrivateKey && privateKeyContent) {
232
- checkChainHistory(bomJson);
246
+ checkExistingSignatures(bomJson);
233
247
  signedBom = signBom(bomJson, {
234
248
  privateKey: privateKeyContent,
235
249
  algorithm: args.algorithm,
250
+ embedPublicKey: args.embedPublicKey,
251
+ embedPublicKeyNested: false,
236
252
  keyId: args.keyId,
237
253
  mode: args.mode,
238
254
  signComponents: args.signComponents,
@@ -247,6 +263,20 @@ try {
247
263
  console.log(
248
264
  `Mode: ${args.mode} | Algorithm: ${args.algorithm}${args.keyId ? ` | KeyId: ${args.keyId}` : ""}`,
249
265
  );
266
+ const rootSignature = signedBom.signature;
267
+ const newEntry =
268
+ rootSignature?.signers?.at(-1) ??
269
+ rootSignature?.chain?.at(-1) ??
270
+ rootSignature;
271
+ if (
272
+ args.embedPublicKey &&
273
+ !String(args.algorithm).startsWith("HS") &&
274
+ newEntry?.publicKey === undefined
275
+ ) {
276
+ console.warn(
277
+ "Warning: this key type has no JWK form, so the public key was not embedded in the signature.",
278
+ );
279
+ }
250
280
  } else {
251
281
  if (!hasAnySignature(bomJson)) {
252
282
  console.error(
package/bin/tracebom.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  safeWriteSync,
15
15
  } from "../lib/ecosystems/utils.js";
16
16
  import { DEFAULT_CDX_SPEC_VERSION } from "../lib/inventory/bomUtils.js";
17
+ import { applySpecVersionCompatibility } from "../lib/stages/postgen/specVersionCompat.js";
17
18
 
18
19
  const _yargs = yargs(hideBin(process.argv));
19
20
 
@@ -166,7 +167,7 @@ const args = _yargs
166
167
  })
167
168
  .option("trace-crypto", {
168
169
  description:
169
- "Enable eBPF-based cryptographic library and cipher suite tracing (Linux only).",
170
+ "Trace cryptographic libraries and TLS parameters: eBPF on Linux, crypto DLLs on Windows (elevated prompt), and TLS handshakes through --proxy-egress on every platform.",
170
171
  default: true,
171
172
  type: "boolean",
172
173
  })
@@ -337,6 +338,9 @@ const options = {
337
338
  console.error("Dynamic SBOM generation failed: no output was produced.");
338
339
  process.exit(1);
339
340
  }
341
+ // tracebom skips postProcess, so the downgrade to an older --spec-version
342
+ // (for example dropping the 1.7 algorithmFamily of traced crypto) runs here.
343
+ applySpecVersionCompatibility(bomJson, options);
340
344
 
341
345
  const jsonPayload = JSON.stringify(bomJson, null, DEBUG_MODE ? 2 : null);
342
346
 
package/bin/validate.js CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  safeMkdirSync,
27
27
  safeWriteSync,
28
28
  } from "../lib/ecosystems/utils.js";
29
+ import { displayValue } from "../lib/helpers/bomSigner.js";
29
30
  import {
30
31
  getNonCycloneDxErrorMessage,
31
32
  isCycloneDxBom,
@@ -35,6 +36,7 @@ import {
35
36
  isProtoBomPath,
36
37
  } from "../lib/inventory/protobomLoader.js";
37
38
  import { getBomWithOras } from "../lib/managers/oci.js";
39
+ import { parseStrictJson } from "../lib/parsers/strictJson.js";
38
40
  import { shouldFail, validateBomAdvanced } from "../lib/validator/index.js";
39
41
  import { render as renderReport } from "../lib/validator/reporters/index.js";
40
42
 
@@ -126,9 +128,8 @@ const args = _yargs
126
128
  })
127
129
  .option("require-signature", {
128
130
  type: "boolean",
129
- default: false,
130
131
  description:
131
- "Exit non-zero (4) when --public-key or --secret-key is provided but signature verification fails.",
132
+ "Exit non-zero (4) when signature verification fails. On by default when --public-key or --secret-key is given; pass --no-require-signature to only report the result.",
132
133
  })
133
134
  .option("strict", {
134
135
  type: "boolean",
@@ -143,7 +144,11 @@ const args = _yargs
143
144
  .help()
144
145
  .wrap(Math.min(120, yargs().terminalWidth())).argv;
145
146
 
146
- async function loadBom(input, platform) {
147
+ /**
148
+ * Loads the BOM. When signatures are verified, the text must also pass
149
+ * parseStrictJson(), and `strictError` explains why it did not.
150
+ */
151
+ async function loadBom(input, platform, strict, requireSignature) {
147
152
  if (safeExistsSync(input)) {
148
153
  try {
149
154
  if (isProtoBomPath(input)) {
@@ -151,9 +156,18 @@ async function loadBom(input, platform) {
151
156
  "cdx-validate",
152
157
  "protobuf BOM input",
153
158
  );
154
- return readBinary(input, true);
159
+ return { bomJson: readBinary(input, true) };
155
160
  }
156
- return JSON.parse(fs.readFileSync(input, "utf8"));
161
+ const bytes = fs.readFileSync(input);
162
+ const bomJson = JSON.parse(bytes.toString("utf8"));
163
+ if (strict) {
164
+ try {
165
+ parseStrictJson(bytes);
166
+ } catch (err) {
167
+ return { bomJson, strictError: err.message };
168
+ }
169
+ }
170
+ return { bomJson };
157
171
  } catch (err) {
158
172
  console.error(`Failed to parse ${input}: ${err.message}`);
159
173
  process.exit(1);
@@ -164,8 +178,27 @@ async function loadBom(input, platform) {
164
178
  input.includes("docker") ||
165
179
  input.includes("ghcr")
166
180
  ) {
167
- const bom = await getBomWithOras(input, platform);
168
- if (bom) return bom;
181
+ let strictError;
182
+ try {
183
+ const bom = await getBomWithOras(
184
+ input,
185
+ platform,
186
+ strict ? { checkBytes: parseStrictJson } : {},
187
+ );
188
+ if (bom) return { bomJson: bom };
189
+ } catch (err) {
190
+ if (requireSignature) {
191
+ console.error(
192
+ `cdx-validate: signature verification failed — the attached BOM cannot be verified: ${err.message}`,
193
+ );
194
+ process.exit(4);
195
+ }
196
+ strictError = err.message;
197
+ }
198
+ if (strictError) {
199
+ const bom = await getBomWithOras(input, platform);
200
+ if (bom) return { bomJson: bom, strictError };
201
+ }
169
202
  }
170
203
  console.error(`Input '${input}' is not a readable SBOM.`);
171
204
  process.exit(1);
@@ -173,12 +206,21 @@ async function loadBom(input, platform) {
173
206
  }
174
207
 
175
208
  function loadKeyFile(path, label, encoding) {
176
- if (!path) return null;
209
+ if (path === undefined) return null;
210
+ if (typeof path !== "string" || !path.trim()) {
211
+ console.error(`cdx-validate: ${label} needs exactly one file path.`);
212
+ process.exit(1);
213
+ }
177
214
  if (!safeExistsSync(path)) {
178
215
  console.error(`${label} '${path}' not found.`);
179
216
  process.exit(1);
180
217
  }
181
- return fs.readFileSync(path, encoding);
218
+ const content = fs.readFileSync(path, encoding);
219
+ if (!content.toString().trim()) {
220
+ console.error(`${label} '${path}' is empty.`);
221
+ process.exit(1);
222
+ }
223
+ return content;
182
224
  }
183
225
 
184
226
  function splitCsv(value) {
@@ -208,14 +250,21 @@ function isLocalProtoBomInput(input) {
208
250
  return isProtoBomPath(input);
209
251
  }
210
252
 
211
- const bomJson = await loadBom(args.input, args.platform);
212
- if (args.publicKey && args.secretKey) {
253
+ const keyGiven = args.publicKey !== undefined || args.secretKey !== undefined;
254
+ const requireSignature = args.requireSignature ?? keyGiven;
255
+ const { bomJson, strictError } = await loadBom(
256
+ args.input,
257
+ args.platform,
258
+ keyGiven,
259
+ requireSignature,
260
+ );
261
+ if (args.publicKey !== undefined && args.secretKey !== undefined) {
213
262
  console.error("cdx-validate: use either --public-key or --secret-key.");
214
263
  process.exit(1);
215
264
  }
216
265
  const publicKeyStr = loadKeyFile(args.publicKey, "Public key", "utf8");
217
266
  const secretKey = loadKeyFile(args.secretKey, "Shared secret");
218
- const verificationRequested = Boolean(publicKeyStr || secretKey);
267
+ const verificationRequested = keyGiven;
219
268
  const inputIsLocalProtoBom = isLocalProtoBomInput(args.input);
220
269
  if (!isCycloneDxBom(bomJson)) {
221
270
  console.error(getNonCycloneDxErrorMessage(bomJson, "cdx-validate"));
@@ -226,7 +275,7 @@ if (inputIsLocalProtoBom && verificationRequested) {
226
275
  console.error(
227
276
  "cdx-validate: protobuf BOM input does not currently preserve JSF signature blocks. Verify signatures against the source JSON BOM instead.",
228
277
  );
229
- process.exit(args.requireSignature ? 4 : 1);
278
+ process.exit(requireSignature ? 4 : 1);
230
279
  }
231
280
 
232
281
  const report = await validateBomAdvanced(bomJson, {
@@ -237,10 +286,17 @@ const report = await validateBomAdvanced(bomJson, {
237
286
  minSeverity: args.minSeverity,
238
287
  includeManual: args.includeManual,
239
288
  includePass: args.includePass,
240
- publicKey: publicKeyStr || undefined,
241
- secretKey: secretKey || undefined,
289
+ publicKey: publicKeyStr ?? undefined,
290
+ secretKey: secretKey ?? undefined,
242
291
  nestedSignatures: args.nestedSignatures,
243
292
  });
293
+ if (strictError && verificationRequested) {
294
+ report.signatureVerified = false;
295
+ report.signatureEntries = null;
296
+ report.signatureDetails = {
297
+ error: `the BOM cannot be verified: ${strictError}`,
298
+ };
299
+ }
244
300
 
245
301
  let output;
246
302
  try {
@@ -267,14 +323,24 @@ for (const e of report.validationErrors || []) {
267
323
  const { shouldFail: fail, reason } = shouldFail(report, {
268
324
  failSeverity: args.failSeverity,
269
325
  strict: args.strict,
270
- requireSignature: Boolean(args.requireSignature && verificationRequested),
326
+ requireSignature: Boolean(requireSignature && verificationRequested),
271
327
  });
272
328
 
273
- if (report.signatureVerified === false && args.requireSignature) {
329
+ if (report.signatureVerified === false && verificationRequested) {
274
330
  console.error(
275
331
  `cdx-validate: signature verification failed — ${report.signatureDetails?.error || "no matching key"}.`,
276
332
  );
277
- process.exit(4);
333
+ if (requireSignature) {
334
+ process.exit(4);
335
+ }
336
+ }
337
+ const unverifiedEntries = (report.signatureEntries || []).filter(
338
+ (entry) => !entry.verified,
339
+ );
340
+ if (report.signatureVerified && unverifiedEntries.length) {
341
+ console.error(
342
+ `cdx-validate: warning — ${unverifiedEntries.length} of ${report.signatureEntries.length} signature entries did not verify with the given key: ${unverifiedEntries.map((entry) => `entry ${entry.index}${entry.keyId === undefined ? "" : ` (keyId '${displayValue(entry.keyId)}')`}`).join(", ")}.`,
343
+ );
278
344
  }
279
345
 
280
346
  if (args.strict && report.schemaValid === false) {
package/bin/verify.js CHANGED
@@ -17,7 +17,7 @@ import {
17
17
  displayValue,
18
18
  loadSharedSecret,
19
19
  loadVerificationKey,
20
- verifyNode,
20
+ verifyBomSignatures,
21
21
  } from "../lib/helpers/bomSigner.js";
22
22
  import {
23
23
  getNonCycloneDxErrorMessage,
@@ -25,6 +25,7 @@ import {
25
25
  } from "../lib/inventory/bomUtils.js";
26
26
  import { isProtoBomPath } from "../lib/inventory/protobomLoader.js";
27
27
  import { getBomWithOras } from "../lib/managers/oci.js";
28
+ import { parseStrictJson } from "../lib/parsers/strictJson.js";
28
29
 
29
30
  const dirName = dirNameStr;
30
31
 
@@ -41,10 +42,13 @@ const args = _yargs
41
42
  description: "The platform to validate. No default",
42
43
  })
43
44
  .option("public-key", {
45
+ type: "array",
46
+ string: true,
44
47
  description:
45
- "Public key in PEM format. Default public.key unless --secret-key is used",
48
+ "Trusted public key in PEM format. Repeat to trust several keys; a signature entry verifies when one of them verifies it.",
46
49
  })
47
50
  .option("secret-key", {
51
+ type: "string",
48
52
  description:
49
53
  "File holding the shared secret for HMAC (HS256, HS384, HS512) signatures. HMAC signatures are only accepted with this option.",
50
54
  })
@@ -52,7 +56,19 @@ const args = _yargs
52
56
  type: "boolean",
53
57
  default: true,
54
58
  description:
55
- "Strictly verify all nested component, service, and annotation signatures against the provided public key. Pass --no-deep to verify only the root signature.",
59
+ "Strictly verify all nested component, service, and annotation signatures against the provided keys. Pass --no-deep to verify only the root signature.",
60
+ })
61
+ .option("require-all", {
62
+ type: "boolean",
63
+ default: false,
64
+ description:
65
+ "Require every entry of a signers or chain signature to verify with one of the keys, instead of at least one.",
66
+ })
67
+ .option("allow-nested-only", {
68
+ type: "boolean",
69
+ default: false,
70
+ description:
71
+ "Accept a BOM without a root signature when every component, service, and annotation carries a signature that verifies. No signature then covers the other members of the BOM.",
56
72
  })
57
73
  .completion("completion", "Generate bash/zsh completion")
58
74
  .epilogue("for documentation, visit https://cdxgen.github.io/cdxgen")
@@ -96,7 +112,7 @@ async function getBom(args) {
96
112
  process.exit(1);
97
113
  }
98
114
  try {
99
- return JSON.parse(fs.readFileSync(args.input, "utf8"));
115
+ return parseStrictJson(fs.readFileSync(args.input));
100
116
  } catch (error) {
101
117
  console.log(`Failed to parse '${args.input}': ${error.message}`);
102
118
  process.exit(1);
@@ -107,11 +123,32 @@ async function getBom(args) {
107
123
  args.input.includes("docker") ||
108
124
  args.input.includes("ghcr")
109
125
  ) {
110
- return await getBomWithOras(args.input, args.platform);
126
+ try {
127
+ return await getBomWithOras(args.input, args.platform, {
128
+ checkBytes: parseStrictJson,
129
+ });
130
+ } catch (error) {
131
+ console.log(
132
+ `Failed to parse the BOM attached to '${args.input}': ${error.message}`,
133
+ );
134
+ process.exit(1);
135
+ }
111
136
  }
112
137
  return undefined;
113
138
  }
114
139
 
140
+ const publicKeyFiles = args.publicKey || [];
141
+ if (publicKeyFiles.length && args.secretKey) {
142
+ console.log("Use either --public-key or --secret-key, not both.");
143
+ process.exit(1);
144
+ }
145
+ if (!publicKeyFiles.length && !args.secretKey) {
146
+ console.log(
147
+ "Name the trusted key with --public-key <file>, or --secret-key <file> for HMAC signatures.",
148
+ );
149
+ process.exit(1);
150
+ }
151
+
115
152
  const bomJson = await getBom(args);
116
153
 
117
154
  if (!bomJson) {
@@ -123,107 +160,139 @@ if (!isCycloneDxBom(bomJson)) {
123
160
  process.exit(1);
124
161
  }
125
162
 
126
- if (args.publicKey && args.secretKey) {
127
- console.log("Use either --public-key or --secret-key, not both.");
128
- process.exit(1);
129
- }
130
- const keyFile = args.secretKey || args.publicKey || "public.key";
131
- if (!args.secretKey && !args.publicKey) {
132
- console.log(
133
- `No --public-key given; using '${keyFile}' from the current directory. Pass --public-key to name the trusted key explicitly.`,
134
- );
135
- }
136
- if (!safeExistsSync(keyFile)) {
137
- console.log(
138
- args.secretKey
139
- ? "Shared secret for signature verification is missing!"
140
- : "Public key for signature verification is missing!",
141
- );
142
- process.exit(1);
143
- }
144
-
145
- let verificationKey;
146
- try {
147
- verificationKey = args.secretKey
148
- ? loadSharedSecret(fs.readFileSync(keyFile))
149
- : loadVerificationKey(fs.readFileSync(keyFile, "utf8"));
150
- } catch (error) {
151
- console.log(`Unable to use '${keyFile}': ${error.message}`);
152
- process.exit(1);
163
+ const verificationKeys = [];
164
+ for (const keyFile of args.secretKey ? [args.secretKey] : publicKeyFiles) {
165
+ if (!safeExistsSync(keyFile)) {
166
+ console.log(
167
+ args.secretKey
168
+ ? `Shared secret '${keyFile}' for signature verification is missing!`
169
+ : `Public key '${keyFile}' for signature verification is missing!`,
170
+ );
171
+ process.exit(1);
172
+ }
173
+ try {
174
+ verificationKeys.push(
175
+ args.secretKey
176
+ ? loadSharedSecret(fs.readFileSync(keyFile))
177
+ : loadVerificationKey(fs.readFileSync(keyFile, "utf8")),
178
+ );
179
+ } catch (error) {
180
+ console.log(`Unable to use '${keyFile}': ${error.message}`);
181
+ process.exit(1);
182
+ }
153
183
  }
154
184
 
155
- function verify(node) {
156
- const reasons = [];
157
- const match = verifyNode(node, verificationKey, { reasons });
158
- return { match, reasons };
185
+ function describeEntry(node, entry) {
186
+ const parts = [displayValue(entry.algorithm)];
187
+ if (entry.keyId !== undefined) {
188
+ parts.push(`keyId '${displayValue(entry.keyId)}'`);
189
+ }
190
+ const position =
191
+ node.form === "single" ? "" : `${node.form}[${entry.index}] `;
192
+ return `${position}(${parts.join(", ")})`;
159
193
  }
160
194
 
161
- function printReasons(reasons) {
162
- for (const reason of new Set(reasons)) {
195
+ function printRejections(node) {
196
+ for (const reason of node.reasons) {
163
197
  console.log(` - ${reason}`);
164
198
  }
165
- }
166
-
167
- let rootResult = null;
168
- if (bomJson.signature) {
169
- rootResult = verify(bomJson);
170
- }
171
-
172
- const verifyNested = args.deep || !bomJson.signature;
173
- let hasInvalidNested = false;
174
- let checkedNested = 0;
175
-
176
- if (verifyNested) {
177
- const nestedTargets = [
178
- ["components", "Component", (c) => c["bom-ref"] || c.name],
179
- ["services", "Service", (s) => s["bom-ref"] || s.name],
180
- ["annotations", "Annotation", (a) => a["bom-ref"] || a.subject],
181
- ];
182
- for (const [field, label, nameOf] of nestedTargets) {
183
- for (const node of bomJson[field] || []) {
184
- if (node?.signature) {
185
- checkedNested++;
186
- const result = verify(node);
187
- if (!result.match) {
188
- console.log(
189
- `${label} '${displayValue(nameOf(node))}' signature is invalid!`,
190
- );
191
- printReasons(result.reasons);
192
- hasInvalidNested = true;
193
- }
194
- }
199
+ for (const entry of node.entries) {
200
+ for (const reason of entry.reasons) {
201
+ console.log(
202
+ ` - ${node.form === "single" ? "" : `${node.form}[${entry.index}]: `}${reason}`,
203
+ );
195
204
  }
196
205
  }
197
206
  }
198
207
 
199
- if (hasInvalidNested) {
208
+ const report = verifyBomSignatures(bomJson, verificationKeys, {
209
+ deep: args.deep,
210
+ allowNestedOnly: args.allowNestedOnly,
211
+ requireAll: args.requireAll,
212
+ });
213
+
214
+ for (const error of report.errors) {
215
+ console.log(error);
216
+ }
217
+ const keyFiles = args.secretKey ? [args.secretKey] : publicKeyFiles;
218
+ if (args.requireAll && report.root?.form) {
219
+ report.root.keysMatched.forEach((matched, index) => {
220
+ if (!matched) {
221
+ console.log(` - key #${index + 1}: ${keyFiles[index]}`);
222
+ }
223
+ });
224
+ }
225
+ const invalidNested = report.nested.filter((item) => !item.verified);
226
+ for (const item of invalidNested) {
227
+ console.log(`${item.label} signature is invalid!`);
228
+ printRejections(item);
229
+ }
230
+ if (invalidNested.length) {
200
231
  console.log("One or more nested signatures are invalid!");
201
- if (rootResult?.match) {
232
+ if (report.root?.verified) {
202
233
  console.log(
203
234
  "The root signature verifies with this key. Nested signatures belong to the party that created them, so pass --no-deep to verify only the root signature with this key.",
204
235
  );
205
236
  }
237
+ }
238
+ if (report.root && !report.root.verified) {
239
+ console.log("BOM signature is invalid!");
240
+ printRejections(report.root);
241
+ if (!report.root.reasons.length && !report.root.match) {
242
+ console.log(
243
+ "If this BOM was signed with cdxgen 13.2.0 or earlier, re-sign it with this version of cdx-sign.",
244
+ );
245
+ }
246
+ }
247
+ if (!report.root && !bomJson.signature && !args.allowNestedOnly) {
248
+ console.log(
249
+ "Pass --allow-nested-only to accept a BOM whose components, services, and annotations are all signed.",
250
+ );
251
+ }
252
+ for (const notice of report.notices) {
253
+ console.log(`Note: ${notice}`);
254
+ }
255
+ if (!report.verified) {
206
256
  process.exit(1);
207
257
  }
208
258
 
209
- if (bomJson.signature) {
210
- const rootMatch = rootResult?.match;
211
- if (rootMatch) {
212
- const identifier = rootMatch.keyId
213
- ? `KeyId: '${displayValue(rootMatch.keyId)}'`
214
- : `Algorithm: '${rootMatch.algorithm}'`;
215
- console.log(`✓ Signature is valid! (Matched ${identifier})`);
216
- } else {
217
- console.log("BOM signature is invalid!");
218
- printReasons(rootResult?.reasons || []);
259
+ if (report.root) {
260
+ const rootMatch = report.root.match;
261
+ const identifier = rootMatch.keyId
262
+ ? `KeyId: '${displayValue(rootMatch.keyId)}'`
263
+ : `Algorithm: '${rootMatch.algorithm}'`;
264
+ console.log(`✓ Signature is valid! (Matched ${identifier})`);
265
+ const unverified = report.root.entries.filter((entry) => !entry.verified);
266
+ if (unverified.length) {
219
267
  console.log(
220
- "If this BOM was signed with cdxgen 13.2.0 or earlier, re-sign it with this version of cdx-sign.",
268
+ `Warning: ${unverified.length} of ${report.root.entries.length} ${report.root.form} entries did not verify with the given key(s). Pass --require-all to fail in that case.`,
221
269
  );
222
- process.exit(1);
270
+ for (const entry of unverified) {
271
+ console.log(
272
+ ` - ${describeEntry(report.root, entry)}: ${entry.reasons[0] || "not verified"}`,
273
+ );
274
+ }
223
275
  }
224
- } else if (checkedNested > 0 && !hasInvalidNested) {
225
- console.log(`✓ ${checkedNested} nested signature(s) are valid!`);
226
276
  } else {
227
- console.log("No valid signatures found to verify!");
228
- process.exit(1);
277
+ console.log(`✓ ${report.nested.length} nested signature(s) are valid!`);
278
+ }
279
+ const MAX_LISTED_ENTRIES = 10;
280
+ const unverifiedNested = report.nested.flatMap((item) =>
281
+ item.entries
282
+ .filter((entry) => !entry.verified)
283
+ .map(
284
+ (entry) =>
285
+ `${item.label} ${describeEntry(item, entry)}: ${entry.reasons[0] || "not verified"}`,
286
+ ),
287
+ );
288
+ if (unverifiedNested.length) {
289
+ console.log(
290
+ `Warning: ${unverifiedNested.length} nested signature entries did not verify with the given key(s). Pass --require-all to fail in that case.`,
291
+ );
292
+ for (const line of unverifiedNested.slice(0, MAX_LISTED_ENTRIES)) {
293
+ console.log(` - ${line}`);
294
+ }
295
+ if (unverifiedNested.length > MAX_LISTED_ENTRIES) {
296
+ console.log(` - and ${unverifiedNested.length - MAX_LISTED_ENTRIES} more`);
297
+ }
229
298
  }