@cdxgen/cdxgen 13.3.0 → 13.4.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 (255) hide show
  1. package/README.md +9 -6
  2. package/bin/cdxgen.js +90 -26
  3. package/bin/evinse.js +18 -1
  4. package/bin/repl.js +86 -0
  5. package/bin/sign.js +45 -15
  6. package/bin/tracebom.js +5 -1
  7. package/bin/validate.js +84 -18
  8. package/bin/verify.js +155 -86
  9. package/data/helpers/init.gradle +46 -21
  10. package/data/remediations.json +61 -0
  11. package/data/rules/dependency-sources.yaml +67 -0
  12. package/data/rules/scala-evidence.yaml +115 -0
  13. package/lib/audit/index.js +38 -6
  14. package/lib/cli/index.js +113 -54
  15. package/lib/cli/jsBom.js +50 -29
  16. package/lib/cli/jvmBom.js +665 -138
  17. package/lib/cli/managedBom.js +320 -200
  18. package/lib/cli/nativeBom.js +139 -30
  19. package/lib/core/activity.js +12 -0
  20. package/lib/core/env.js +17 -9
  21. package/lib/core/fs.js +3 -2
  22. package/lib/core/httpClient.js +159 -24
  23. package/lib/core/pluginRun.js +184 -0
  24. package/lib/core/processTree.js +100 -0
  25. package/lib/core/runState.js +39 -0
  26. package/lib/core/toml.js +72 -0
  27. package/lib/ecosystems/asarutils.js +4 -2
  28. package/lib/ecosystems/bunutils.js +5 -7
  29. package/lib/ecosystems/cargoCache.js +231 -8
  30. package/lib/ecosystems/cmakeResolver.js +10 -21
  31. package/lib/ecosystems/cppBuildContext.js +631 -0
  32. package/lib/ecosystems/cppEvidence.js +337 -22
  33. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  34. package/lib/ecosystems/denoutils.js +93 -66
  35. package/lib/ecosystems/ecosystems.js +1967 -788
  36. package/lib/ecosystems/golem.js +28 -12
  37. package/lib/ecosystems/gradleutils.js +314 -30
  38. package/lib/ecosystems/jsEvidence.js +85 -24
  39. package/lib/ecosystems/kosi.js +21 -4
  40. package/lib/ecosystems/npmutils.js +64 -0
  41. package/lib/ecosystems/parsers-dotnet.js +567 -151
  42. package/lib/ecosystems/parsers-elm.js +46 -28
  43. package/lib/ecosystems/parsers-gleam.js +3 -4
  44. package/lib/ecosystems/parsers-go.js +324 -61
  45. package/lib/ecosystems/parsers-js.js +92 -26
  46. package/lib/ecosystems/parsers-julia.js +2 -3
  47. package/lib/ecosystems/parsers-jvm.js +204 -12
  48. package/lib/ecosystems/parsers-misc.js +182 -55
  49. package/lib/ecosystems/parsers-python.js +141 -10
  50. package/lib/ecosystems/parsers-rust.js +4 -4
  51. package/lib/ecosystems/parsers-terraform.js +1573 -19
  52. package/lib/ecosystems/pythonutils.js +102 -6
  53. package/lib/ecosystems/remote/huggingface.js +3 -0
  54. package/lib/ecosystems/rubyutils.js +67 -17
  55. package/lib/ecosystems/rusi.js +22 -9
  56. package/lib/ecosystems/sbtutils.js +243 -187
  57. package/lib/ecosystems/scalacliutils.js +342 -0
  58. package/lib/ecosystems/scalasem.js +2618 -0
  59. package/lib/ecosystems/terraformRegistry.js +288 -0
  60. package/lib/ecosystems/utils.js +2 -0
  61. package/lib/ecosystems/vendoredCode.js +156 -0
  62. package/lib/evinser/evinser.js +578 -69
  63. package/lib/helpers/agenticToolutils.js +2 -2
  64. package/lib/helpers/bomSigner.js +576 -128
  65. package/lib/inventory/aiCollector.js +4 -1
  66. package/lib/inventory/analyzer.js +110 -127
  67. package/lib/inventory/atomUtils.js +171 -48
  68. package/lib/inventory/auditCategories.js +7 -0
  69. package/lib/inventory/buildToolRateLimit.js +144 -0
  70. package/lib/inventory/cacheCommands.js +2 -1
  71. package/lib/inventory/cmakeBuildDirs.js +254 -0
  72. package/lib/inventory/deps.js +501 -124
  73. package/lib/inventory/dosai.js +248 -44
  74. package/lib/inventory/dosaiParsers.js +310 -15
  75. package/lib/inventory/dynamic.js +52 -6
  76. package/lib/inventory/evidenceUtils.js +154 -109
  77. package/lib/inventory/fetchBatch.js +574 -38
  78. package/lib/inventory/fetchDiskCache.js +597 -0
  79. package/lib/inventory/forgeEnricher.js +14 -5
  80. package/lib/inventory/jvmLocalRepos.js +917 -0
  81. package/lib/inventory/mcpConfigParser.js +2 -3
  82. package/lib/inventory/parentSelection.js +146 -0
  83. package/lib/inventory/rustFormulationParser.js +3 -4
  84. package/lib/inventory/scalaCoords.js +153 -0
  85. package/lib/inventory/source.js +8 -8
  86. package/lib/inventory/spdx.js +101 -0
  87. package/lib/inventory/traceRunner.js +88 -7
  88. package/lib/inventory/tracedCrypto.js +294 -0
  89. package/lib/managers/docker.js +33 -32
  90. package/lib/managers/oci.js +24 -2
  91. package/lib/parsers/cmakeCache.js +69 -0
  92. package/lib/parsers/cmakeLists.js +16 -2
  93. package/lib/parsers/cmakePresets.js +400 -0
  94. package/lib/parsers/compileCommands.js +557 -0
  95. package/lib/parsers/strictJson.js +322 -0
  96. package/lib/parsers/terraformConfig.js +1073 -0
  97. package/lib/parsers/terraformSource.js +652 -0
  98. package/lib/server/server.js +23 -0
  99. package/lib/stages/postgen/introspection/markers.js +1 -1
  100. package/lib/stages/postgen/postgen.js +15 -1
  101. package/lib/stages/postgen/specVersionCompat.js +32 -0
  102. package/lib/validator/index.js +25 -10
  103. package/lib/validator/reporters/json.js +1 -0
  104. package/package.json +21 -21
  105. package/types/lib/audit/index.d.ts.map +1 -1
  106. package/types/lib/cli/index.d.ts.map +1 -1
  107. package/types/lib/cli/jsBom.d.ts.map +1 -1
  108. package/types/lib/cli/jvmBom.d.ts +24 -1
  109. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  110. package/types/lib/cli/managedBom.d.ts +7 -4
  111. package/types/lib/cli/managedBom.d.ts.map +1 -1
  112. package/types/lib/cli/nativeBom.d.ts +3 -1
  113. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  114. package/types/lib/core/activity.d.ts.map +1 -1
  115. package/types/lib/core/env.d.ts +9 -0
  116. package/types/lib/core/env.d.ts.map +1 -1
  117. package/types/lib/core/fs.d.ts.map +1 -1
  118. package/types/lib/core/httpClient.d.ts +13 -8
  119. package/types/lib/core/httpClient.d.ts.map +1 -1
  120. package/types/lib/core/pluginRun.d.ts +97 -0
  121. package/types/lib/core/pluginRun.d.ts.map +1 -0
  122. package/types/lib/core/processTree.d.ts +36 -0
  123. package/types/lib/core/processTree.d.ts.map +1 -0
  124. package/types/lib/core/runState.d.ts +29 -0
  125. package/types/lib/core/runState.d.ts.map +1 -0
  126. package/types/lib/core/toml.d.ts +20 -0
  127. package/types/lib/core/toml.d.ts.map +1 -0
  128. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  129. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  130. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  131. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  132. package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
  133. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  134. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  135. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  136. package/types/lib/ecosystems/cppEvidence.d.ts +18 -2
  137. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  138. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  139. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  140. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  141. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  142. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  143. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  144. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  145. package/types/lib/ecosystems/gradleutils.d.ts +50 -0
  146. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  147. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  148. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  149. package/types/lib/ecosystems/npmutils.d.ts +15 -0
  150. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  151. package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
  152. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  153. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  154. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  155. package/types/lib/ecosystems/parsers-go.d.ts +44 -2
  156. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  157. package/types/lib/ecosystems/parsers-js.d.ts +6 -1
  158. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  159. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  160. package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
  161. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  162. package/types/lib/ecosystems/parsers-misc.d.ts +3 -1
  163. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  164. package/types/lib/ecosystems/parsers-python.d.ts +53 -3
  165. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  166. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  167. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  168. package/types/lib/ecosystems/pythonutils.d.ts +27 -2
  169. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  170. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  171. package/types/lib/ecosystems/rubyutils.d.ts +21 -1
  172. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  173. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  174. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  175. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  176. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  177. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  178. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  179. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  180. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  181. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  182. package/types/lib/ecosystems/utils.d.ts +2 -2
  183. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  184. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  185. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  186. package/types/lib/evinser/evinser.d.ts +75 -8
  187. package/types/lib/evinser/evinser.d.ts.map +1 -1
  188. package/types/lib/helpers/bomSigner.d.ts +80 -4
  189. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  190. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  191. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  192. package/types/lib/inventory/atomUtils.d.ts +82 -10
  193. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  194. package/types/lib/inventory/auditCategories.d.ts +3 -0
  195. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  196. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  197. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  198. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  199. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  200. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  201. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  202. package/types/lib/inventory/deps.d.ts +59 -1
  203. package/types/lib/inventory/deps.d.ts.map +1 -1
  204. package/types/lib/inventory/dosai.d.ts +57 -8
  205. package/types/lib/inventory/dosai.d.ts.map +1 -1
  206. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  207. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  208. package/types/lib/inventory/dynamic.d.ts +10 -0
  209. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  210. package/types/lib/inventory/evidenceUtils.d.ts +19 -4
  211. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  212. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  213. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  214. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  215. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  216. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  217. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  218. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  219. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  220. package/types/lib/inventory/parentSelection.d.ts +34 -0
  221. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  222. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  223. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  224. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  225. package/types/lib/inventory/source.d.ts.map +1 -1
  226. package/types/lib/inventory/spdx.d.ts +9 -0
  227. package/types/lib/inventory/spdx.d.ts.map +1 -1
  228. package/types/lib/inventory/traceRunner.d.ts +12 -0
  229. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  230. package/types/lib/inventory/tracedCrypto.d.ts +42 -0
  231. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  232. package/types/lib/managers/docker.d.ts.map +1 -1
  233. package/types/lib/managers/oci.d.ts +3 -1
  234. package/types/lib/managers/oci.d.ts.map +1 -1
  235. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  236. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  237. package/types/lib/parsers/cmakeLists.d.ts +2 -1
  238. package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
  239. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  240. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  241. package/types/lib/parsers/compileCommands.d.ts +115 -0
  242. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  243. package/types/lib/parsers/strictJson.d.ts +46 -0
  244. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  245. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  246. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  247. package/types/lib/parsers/terraformSource.d.ts +66 -0
  248. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  249. package/types/lib/server/server.d.ts.map +1 -1
  250. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  251. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  252. package/types/lib/validator/index.d.ts +8 -0
  253. package/types/lib/validator/index.d.ts.map +1 -1
  254. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  255. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
package/README.md CHANGED
@@ -216,7 +216,7 @@ Common asset names:
216
216
  #### Linux
217
217
 
218
218
  ```bash
219
- VERSION="v13.3.0"
219
+ VERSION="v13.4.0"
220
220
  ASSET="cdx-audit-linux-amd64"
221
221
  BASE_URL="https://github.com/cdxgen/cdxgen/releases/download/${VERSION}"
222
222
 
@@ -230,7 +230,7 @@ chmod +x "${ASSET}"
230
230
  #### macOS
231
231
 
232
232
  ```bash
233
- VERSION="v13.3.0"
233
+ VERSION="v13.4.0"
234
234
  ASSET="cdx-audit-darwin-arm64"
235
235
  BASE_URL="https://github.com/cdxgen/cdxgen/releases/download/${VERSION}"
236
236
 
@@ -244,7 +244,7 @@ chmod +x "${ASSET}"
244
244
  #### Windows (PowerShell)
245
245
 
246
246
  ```powershell
247
- $Version = "v13.3.0"
247
+ $Version = "v13.4.0"
248
248
  $Asset = "cdx-audit-windows-amd64.exe"
249
249
  $BaseUrl = "https://github.com/cdxgen/cdxgen/releases/download/$Version"
250
250
 
@@ -269,7 +269,7 @@ steps:
269
269
  env:
270
270
  GH_TOKEN: ${{ github.token }}
271
271
  run: |
272
- gh release download v13.3.0 \
272
+ gh release download v13.4.0 \
273
273
  --repo cdxgen/cdxgen \
274
274
  --pattern 'cdx-audit-linux-amd64' \
275
275
  --pattern 'cdx-audit-linux-amd64.sha256'
@@ -759,9 +759,9 @@ BOMs signed with cdxgen 13.2.0 or earlier must be re-signed with the current `cd
759
759
  For complex supply chain orchestration, use the bundled `cdx-sign` CLI. This tool allows multiple entities (e.g., a Builder and an Auditor) to co-sign an existing BOM without modifying its original data.
760
760
 
761
761
  ```shell
762
- # Append a parallel multi-signature (Auditor co-signing)
762
+ # Append a parallel multi-signature (Auditor co-signing) after checking the Builder's signature
763
763
  # Note: Granular component signing is disabled to preserve the Builder's original signature payload.
764
- cdx-sign -i bom.json -k auditor_private.pem -a ES256 --key-id "auditor-qa" --mode signers --no-sign-components
764
+ cdx-sign -i bom.json -k auditor_private.pem -a ES256 --key-id "auditor-qa" --mode signers --no-sign-components --verify-existing-with builder_public.pem
765
765
  ```
766
766
 
767
767
  ### Validating CycloneDX BOMs
@@ -780,6 +780,9 @@ cdx-verify -i bom.json --public-key public.key
780
780
 
781
781
  # Verify ONLY the top-level root signature (useful for verifying a multi-signer who didn't sign nested components)
782
782
  cdx-verify -i bom.json --public-key auditor_public.key --no-deep
783
+
784
+ # Require every co-signer
785
+ cdx-verify -i bom.json --public-key builder_public.key --public-key auditor_public.key --require-all
783
786
  ```
784
787
 
785
788
  ### Verifying the signature (pnpm)
package/bin/cdxgen.js CHANGED
@@ -34,6 +34,7 @@ import {
34
34
  isDeferredFailOnError,
35
35
  } from "../lib/core/deferredExit.js";
36
36
  import { TRACE_MODE, thoughtEnd, thoughtLog } from "../lib/core/logger.js";
37
+ import { resolveSourcePathArgument } from "../lib/core/pluginRun.js";
37
38
  import {
38
39
  PROJECT_CONFIG_FILENAMES,
39
40
  sanitizeProjectConfig,
@@ -263,12 +264,12 @@ const args = _yargs
263
264
  .option("cmake-cache", {
264
265
  type: "string",
265
266
  description:
266
- "Path to the CMakeCache.txt of a configured C/C++ build. Overrides the lookup under build/, out/ and cmake-build-*/.",
267
+ "Path to the CMakeCache.txt of a configured C/C++ build. Overrides the lookup in the build directories of the CMake presets, build*/, out/, builddir/ and cmake-build-*/.",
267
268
  })
268
269
  .option("compile-commands", {
269
270
  type: "string",
270
271
  description:
271
- "Path to the compile_commands.json of a C/C++ build, or a directory holding one. atom then parses each file with its build's include paths, macros and language. Overrides the lookup in the project root, build/, out/, builddir/ and cmake-build-*/, which is skipped in secure mode.",
272
+ "Path to the compile_commands.json of a C/C++ build, or a directory holding one. atom then parses each file with its build's include paths, macros and language. Overrides the lookup in the project root and its build directories (those of the CMake presets, build*/, out/, builddir/, cmake-build-*/), which is skipped in secure mode.",
272
273
  })
273
274
  .option("git-branch", {
274
275
  description: "Git branch to clone when the source is a git URL or purl",
@@ -456,6 +457,23 @@ const args = _yargs
456
457
  default: false,
457
458
  description: "Generate SBOM with evidence for supported languages.",
458
459
  })
460
+ .option("scalasem", {
461
+ type: "boolean",
462
+ default: true,
463
+ description:
464
+ "Run the Scala semantic analyzer for the evidence of Scala projects. Use --no-scalasem to skip it.",
465
+ })
466
+ .option("scalasem-command", {
467
+ type: "string",
468
+ description:
469
+ "Use a specific scalasem script for the Scala evidence. Defaults to SCALASEM_CMD.",
470
+ })
471
+ .option("scalasem-include-tests", {
472
+ type: "boolean",
473
+ default: false,
474
+ description:
475
+ "Include test sources in the Scala evidence, tagged with their usage scope.",
476
+ })
459
477
  .option("deps-slices-file", {
460
478
  description: "Path for the parsedeps slice file created by atom.",
461
479
  default: "deps.slices.json",
@@ -893,7 +911,7 @@ const args = _yargs
893
911
  type: "boolean",
894
912
  default: true,
895
913
  description:
896
- "Use the on-disk metadata cache for registry lookups. Pass --no-cache to bypass it for this run.",
914
+ "Use the on-disk metadata cache for registry lookups. Pass --no-cache to bypass it for this run; answers are still remembered within the run.",
897
915
  })
898
916
  .option("cache-ttl", {
899
917
  type: "number",
@@ -1013,9 +1031,24 @@ if (!readEnvironmentVariable("NODE_USE_SYSTEM_CA")) {
1013
1031
  process.env.NODE_USE_SYSTEM_CA = "1";
1014
1032
  }
1015
1033
 
1016
- const filePath = args._[0] || process.cwd();
1034
+ let filePath = args._[0] || process.cwd();
1017
1035
  const sourceInputIsRemoteOrPurl =
1018
1036
  maybeRemotePath(filePath) || maybePurlSource(filePath);
1037
+ // Resolve the source argument once, before anything uses it. With the working
1038
+ // directory already set to the project and the argument relative to a
1039
+ // repository root (depscan's launch shape), every later resolve would produce
1040
+ // <project>/<argument>, a directory that does not exist: the BOM came out
1041
+ // empty and the plugins analyzed nothing, while the run still exited 0.
1042
+ if (!sourceInputIsRemoteOrPurl && args._?.[0]) {
1043
+ const sourceResolution = resolveSourcePathArgument(filePath);
1044
+ if (sourceResolution.rewritten) {
1045
+ console.warn(
1046
+ `Source path '${filePath}' does not exist relative to the working directory, which is itself the directory it names; using '${sourceResolution.path}'.`,
1047
+ );
1048
+ filePath = sourceResolution.path;
1049
+ args._[0] = filePath;
1050
+ }
1051
+ }
1019
1052
  if (!args.projectName) {
1020
1053
  if (filePath !== ".") {
1021
1054
  args.projectName = basename(filePath);
@@ -2137,30 +2170,61 @@ const writeCycloneDxOutput = (jsonFile, bomJson, options) => {
2137
2170
  sourcePath: filePath,
2138
2171
  });
2139
2172
  const evinserModule = await import("../lib/evinser/evinser.js");
2140
- options.projectType = options.projectType || ["java"];
2141
- const evinseOptions = evinserModule.buildEvinseOptions(
2142
- options,
2143
- args,
2144
- internalCycloneDxInputPath || options.output,
2145
- );
2146
- const dbObjMap = await evinserModule.prepareDB(evinseOptions);
2147
- if (dbObjMap) {
2148
- const sliceArtefacts = await evinserModule.analyzeProject(
2149
- dbObjMap,
2150
- evinseOptions,
2173
+ if (!options.projectType) {
2174
+ const evidenceLanguage = evinserModule.defaultEvidenceLanguage(
2175
+ filePath,
2176
+ bomNSData?.bomJson,
2177
+ options,
2178
+ );
2179
+ if (evidenceLanguage) {
2180
+ options.projectType = [evidenceLanguage];
2181
+ }
2182
+ }
2183
+ if (!options.projectType) {
2184
+ console.log(
2185
+ "Skipping the evidence analysis since the BOM lists no package in a language it supports. Pass -t to choose the language.",
2151
2186
  );
2152
- const evinseJson = await evinserModule.createEvinseFile(
2153
- sliceArtefacts,
2154
- evinseOptions,
2187
+ if (bomNSData) {
2188
+ bomNSData.bomJson = applyEvidenceBasedFilter(
2189
+ bomNSData.bomJson,
2190
+ options,
2191
+ );
2192
+ }
2193
+ } else {
2194
+ const evinseOptions = evinserModule.buildEvinseOptions(
2195
+ options,
2196
+ args,
2197
+ internalCycloneDxInputPath || options.output,
2155
2198
  );
2156
- // The scope filter is deferred until here so the analyzers get to
2157
- // prove that an optional dependency is actually used.
2158
- bomNSData.bomJson = applyEvidenceBasedFilter(evinseJson, options);
2159
- if (options.print && evinseJson) {
2160
- printOccurrences(evinseJson);
2161
- printCallStack(evinseJson);
2162
- printReachables(sliceArtefacts);
2163
- printServices(evinseJson);
2199
+ try {
2200
+ const dbObjMap = await evinserModule.prepareDB(evinseOptions);
2201
+ if (dbObjMap) {
2202
+ const sliceArtefacts = await evinserModule.analyzeProject(
2203
+ dbObjMap,
2204
+ evinseOptions,
2205
+ );
2206
+ const evinseJson = await evinserModule.createEvinseFile(
2207
+ sliceArtefacts,
2208
+ evinseOptions,
2209
+ );
2210
+ // The scope filter is deferred until here so the analyzers get to
2211
+ // prove that an optional dependency is actually used.
2212
+ bomNSData.bomJson = applyEvidenceBasedFilter(evinseJson, options);
2213
+ if (options.print && evinseJson) {
2214
+ printOccurrences(evinseJson);
2215
+ printCallStack(evinseJson);
2216
+ printReachables(sliceArtefacts);
2217
+ printServices(evinseJson);
2218
+ }
2219
+ }
2220
+ } catch (err) {
2221
+ // A rusi/golem analysis failure under --fail-on-error on an
2222
+ // introspected run defers its exit so the BOM and the reports are
2223
+ // still written; the run continues here without the reachability
2224
+ // evidence, exactly as the deferral contract describes.
2225
+ if (!isDeferredFailOnError(err)) {
2226
+ throw err;
2227
+ }
2164
2228
  }
2165
2229
  }
2166
2230
  }
package/bin/evinse.js CHANGED
@@ -91,7 +91,7 @@ const args = yargs(hideBin(process.argv))
91
91
  })
92
92
  .option("compile-commands", {
93
93
  description:
94
- "Path to the compile_commands.json of a C/C++ build, or a directory holding one, for atom to parse each file as its build does. Overrides the lookup in the project root, build/, out/, builddir/ and cmake-build-*/, which is skipped in secure mode.",
94
+ "Path to the compile_commands.json of a C/C++ build, or a directory holding one, for atom to parse each file as its build does. Overrides the lookup in the project root and its build directories (those of the CMake presets, build*/, out/, builddir/, cmake-build-*/), which is skipped in secure mode.",
95
95
  type: "string",
96
96
  })
97
97
  .option("golem-command", {
@@ -264,6 +264,23 @@ const args = yargs(hideBin(process.argv))
264
264
  description: "Use an existing usages slices file.",
265
265
  default: "usages.slices.json",
266
266
  })
267
+ .option("scalasem-command", {
268
+ description:
269
+ "Use a specific scalasem script for Scala evidence analysis. Defaults to SCALASEM_CMD.",
270
+ default: readEnvironmentVariable("SCALASEM_CMD"),
271
+ })
272
+ .option("scalasem-include-tests", {
273
+ description:
274
+ "Include test sources in the Scala semantic evidence, tagged with their usage scope.",
275
+ default: false,
276
+ type: "boolean",
277
+ })
278
+ .option("scalasem", {
279
+ description:
280
+ "Run the Scala semantic analyzer for Scala projects. Use --no-scalasem to skip it and keep the plain BOM.",
281
+ default: true,
282
+ type: "boolean",
283
+ })
267
284
  .option("data-flow-slices-file", {
268
285
  description: "Use an existing data-flow slices file.",
269
286
  default: "data-flow.slices.json",
package/bin/repl.js CHANGED
@@ -1741,6 +1741,92 @@ cdxgenRepl.defineCommand("golemcoverage", {
1741
1741
  this.displayPrompt();
1742
1742
  },
1743
1743
  });
1744
+ function hasScalasemProperties(component) {
1745
+ return Boolean(
1746
+ component?.properties?.some((property) =>
1747
+ property?.name?.startsWith("cdx:scalasem:"),
1748
+ ),
1749
+ );
1750
+ }
1751
+
1752
+ cdxgenRepl.defineCommand("scalasemsummary", {
1753
+ help: "summarize Scala Evinse/scalasem metadata and evidence coverage",
1754
+ action() {
1755
+ const interactiveBom = getInteractiveBom();
1756
+ if (!interactiveBom) {
1757
+ console.log("⚠ No BOM is loaded. Use .import command to import an SBOM");
1758
+ this.displayPrompt();
1759
+ return;
1760
+ }
1761
+ if (!hasScalasemProperties(interactiveBom.metadata?.component)) {
1762
+ console.log(
1763
+ "No scalasem properties found. Generate an evidence BOM with evinse -l scala or cdxgen --evidence to use this view.",
1764
+ );
1765
+ this.displayPrompt();
1766
+ return;
1767
+ }
1768
+ const rootComponent = interactiveBom.metadata?.component || {};
1769
+ const components = interactiveBom.components || [];
1770
+ const occurrences = components.reduce(
1771
+ (total, component) =>
1772
+ total + (component?.evidence?.occurrences?.length || 0),
1773
+ 0,
1774
+ );
1775
+ const occurrencesWithLine = components.reduce(
1776
+ (total, component) =>
1777
+ total +
1778
+ (component?.evidence?.occurrences || []).filter(
1779
+ (occurrence) => occurrence?.line,
1780
+ ).length,
1781
+ 0,
1782
+ );
1783
+ const callstacks = components.filter(
1784
+ (component) => component?.evidence?.callstack?.frames?.length,
1785
+ ).length;
1786
+ const cryptoAssets = components.filter(
1787
+ (component) => component?.type === "cryptographic-asset",
1788
+ ).length;
1789
+ printKeyValueTable("Scala Evinse / scalasem summary", [
1790
+ [
1791
+ "Schema version",
1792
+ getPropertyValue(rootComponent, "cdx:scalasem:schemaVersion"),
1793
+ ],
1794
+ [
1795
+ "Facts source",
1796
+ getPropertyValue(rootComponent, "cdx:scalasem:factsSource"),
1797
+ ],
1798
+ [
1799
+ "Scala versions",
1800
+ getPropertyValue(rootComponent, "cdx:scalasem:scalaVersions"),
1801
+ ],
1802
+ ["Platforms", getPropertyValue(rootComponent, "cdx:scalasem:platforms")],
1803
+ [
1804
+ "Files analyzed",
1805
+ getPropertyValue(rootComponent, "cdx:scalasem:filesAnalyzed"),
1806
+ ],
1807
+ ["Degraded", getPropertyValue(rootComponent, "cdx:scalasem:degraded")],
1808
+ ...(rootComponent.properties || [])
1809
+ .filter((property) =>
1810
+ property?.name?.startsWith("cdx:scalasem:diagnostic:"),
1811
+ )
1812
+ .map((property) => [
1813
+ `Diagnostic ${property.name.slice("cdx:scalasem:diagnostic:".length)}`,
1814
+ property.value,
1815
+ ]),
1816
+ [
1817
+ "Components with occurrences",
1818
+ components.filter((c) => c?.evidence?.occurrences?.length).length,
1819
+ ],
1820
+ ["Occurrences", occurrences],
1821
+ ["Occurrences with line", occurrencesWithLine],
1822
+ ["Components with call stacks", callstacks],
1823
+ ["Crypto assets", cryptoAssets],
1824
+ ["Services", (interactiveBom.services || []).length],
1825
+ ]);
1826
+ this.displayPrompt();
1827
+ },
1828
+ });
1829
+
1744
1830
  cdxgenRepl.defineCommand("auditfindings", {
1745
1831
  help: "summarize cdx-audit and bom-audit annotations from the loaded BOM",
1746
1832
  action() {
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