@cdxgen/cdxgen 13.1.0 → 13.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/README.md +32 -15
  2. package/bin/cdxgen.js +243 -122
  3. package/bin/evinse.js +7 -0
  4. package/bin/sign.js +88 -14
  5. package/bin/tracebom.js +64 -4
  6. package/bin/validate.js +29 -7
  7. package/bin/verify.js +79 -39
  8. package/data/crypto-oid.json +88 -12
  9. package/data/remediations.json +47 -0
  10. package/data/rules/ai-bom.yaml +78 -0
  11. package/data/rules/cargo-rust.yaml +132 -0
  12. package/data/rules/mcp-servers.yaml +122 -0
  13. package/lib/audit/targets.js +6 -1
  14. package/lib/cli/bomAssembly.js +117 -15
  15. package/lib/cli/cliOptions.js +93 -1
  16. package/lib/cli/index.js +235 -5
  17. package/lib/cli/jsBom.js +108 -26
  18. package/lib/cli/jvmBom.js +68 -12
  19. package/lib/cli/managedBom.js +264 -41
  20. package/lib/cli/nativeBom.js +923 -15
  21. package/lib/core/activity.js +120 -2
  22. package/lib/core/env.js +44 -7
  23. package/lib/core/fs.js +174 -6
  24. package/lib/core/metadataProperties.js +97 -0
  25. package/lib/core/projectConfig.js +31 -4
  26. package/lib/core/userAgent.js +151 -0
  27. package/lib/ecosystems/bunutils.js +269 -101
  28. package/lib/ecosystems/cargoCache.js +364 -0
  29. package/lib/ecosystems/caxa.js +447 -7
  30. package/lib/ecosystems/cmakeResolver.js +66 -0
  31. package/lib/ecosystems/cppEvidence.js +104 -33
  32. package/lib/ecosystems/ecosystems.js +260 -99
  33. package/lib/ecosystems/golem.js +11 -1
  34. package/lib/ecosystems/gradleutils.js +185 -9
  35. package/lib/ecosystems/jsEvidence.js +19 -20
  36. package/lib/ecosystems/kosi.js +1263 -0
  37. package/lib/ecosystems/npmutils.js +110 -9
  38. package/lib/ecosystems/parsers-crystal.js +179 -0
  39. package/lib/ecosystems/parsers-dotnet.js +120 -49
  40. package/lib/ecosystems/parsers-elm.js +816 -0
  41. package/lib/ecosystems/parsers-gleam.js +1 -1
  42. package/lib/ecosystems/parsers-go.js +47 -9
  43. package/lib/ecosystems/parsers-js.js +171 -50
  44. package/lib/ecosystems/parsers-julia.js +268 -0
  45. package/lib/ecosystems/parsers-lua.js +218 -0
  46. package/lib/ecosystems/parsers-misc.js +919 -161
  47. package/lib/ecosystems/parsers-nim.js +221 -0
  48. package/lib/ecosystems/parsers-opam.js +180 -0
  49. package/lib/ecosystems/parsers-perl.js +277 -0
  50. package/lib/ecosystems/parsers-python.js +27 -15
  51. package/lib/ecosystems/parsers-r.js +148 -0
  52. package/lib/ecosystems/parsers-rebar.js +411 -0
  53. package/lib/ecosystems/parsers-rust.js +954 -33
  54. package/lib/ecosystems/parsers-spack.js +204 -0
  55. package/lib/ecosystems/parsers-stack.js +264 -0
  56. package/lib/ecosystems/parsers-terraform.js +194 -0
  57. package/lib/ecosystems/rubyutils.js +1426 -166
  58. package/lib/ecosystems/rusi.js +158 -7
  59. package/lib/evinser/evinser.js +547 -129
  60. package/lib/evinser/swiftsem.js +1914 -302
  61. package/lib/helpers/agenticToolutils.js +925 -0
  62. package/lib/helpers/bomSigner.js +652 -221
  63. package/lib/inventory/agentFormulationParser.js +57 -4
  64. package/lib/inventory/aiCollector.js +504 -10
  65. package/lib/inventory/aiInventory.js +12 -0
  66. package/lib/inventory/analyzer.js +488 -30
  67. package/lib/inventory/astWorker.js +6 -0
  68. package/lib/inventory/atomUtils.js +568 -31
  69. package/lib/inventory/auditCategories.js +8 -0
  70. package/lib/inventory/cbomutils.js +30 -0
  71. package/lib/inventory/cdxrs.js +110 -14
  72. package/lib/inventory/communityAiConfigParser.js +215 -2
  73. package/lib/inventory/depsUtils.js +183 -11
  74. package/lib/inventory/display.js +4 -5
  75. package/lib/inventory/dosai.js +627 -13
  76. package/lib/inventory/dynamic.js +39 -1
  77. package/lib/inventory/envcontext.js +9 -6
  78. package/lib/inventory/evidenceUtils.js +26 -5
  79. package/lib/inventory/fetchBatch.js +190 -19
  80. package/lib/inventory/fetchRate.js +74 -11
  81. package/lib/inventory/forgeEnricher.js +3 -2
  82. package/lib/inventory/jvmToolEnv.js +6 -5
  83. package/lib/inventory/mcpConfigParser.js +166 -2
  84. package/lib/inventory/mcpDiscovery.js +11 -0
  85. package/lib/inventory/mcpServerManifest.js +389 -0
  86. package/lib/inventory/mcpbBundle.js +419 -0
  87. package/lib/inventory/plugins.js +3 -0
  88. package/lib/inventory/protobom.js +62 -89
  89. package/lib/inventory/protobomLoader.js +4 -0
  90. package/lib/inventory/purl.js +152 -3
  91. package/lib/inventory/rustFormulationParser.js +1 -1
  92. package/lib/inventory/sbomDocument.js +2 -70
  93. package/lib/inventory/source.js +47 -23
  94. package/lib/inventory/stylePackageRefs.js +41 -0
  95. package/lib/inventory/svelteUtils.js +524 -0
  96. package/lib/inventory/traceRunner.js +210 -21
  97. package/lib/managers/binary.js +115 -24
  98. package/lib/managers/docker.js +440 -20
  99. package/lib/managers/oci.js +39 -1
  100. package/lib/parsers/cmakeLists.js +452 -0
  101. package/lib/parsers/huggingfaceUtils.js +15 -7
  102. package/lib/parsers/largeJson.js +454 -0
  103. package/lib/parsers/npmrc.js +19 -1
  104. package/lib/parsers/onnx.js +294 -0
  105. package/lib/parsers/safetensors.js +114 -0
  106. package/lib/stages/postgen/introspection/markers.js +29 -6
  107. package/lib/stages/postgen/postgen.js +237 -16
  108. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
  109. package/lib/validator/bomValidator.js +6 -16
  110. package/lib/validator/index.js +48 -10
  111. package/lib/validator/reporters/console.js +3 -0
  112. package/lib/validator/reporters/json.js +1 -0
  113. package/lib/validator/reporters/sarif.js +7 -0
  114. package/package.json +27 -27
  115. package/types/lib/audit/targets.d.ts.map +1 -1
  116. package/types/lib/cli/bomAssembly.d.ts +2 -1
  117. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  118. package/types/lib/cli/cliOptions.d.ts +33 -1
  119. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  120. package/types/lib/cli/index.d.ts +2 -2
  121. package/types/lib/cli/index.d.ts.map +1 -1
  122. package/types/lib/cli/jsBom.d.ts +23 -0
  123. package/types/lib/cli/jsBom.d.ts.map +1 -1
  124. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  125. package/types/lib/cli/managedBom.d.ts +33 -0
  126. package/types/lib/cli/managedBom.d.ts.map +1 -1
  127. package/types/lib/cli/nativeBom.d.ts +88 -8
  128. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  129. package/types/lib/core/activity.d.ts.map +1 -1
  130. package/types/lib/core/env.d.ts +21 -0
  131. package/types/lib/core/env.d.ts.map +1 -1
  132. package/types/lib/core/fs.d.ts +55 -0
  133. package/types/lib/core/fs.d.ts.map +1 -1
  134. package/types/lib/core/metadataProperties.d.ts +39 -0
  135. package/types/lib/core/metadataProperties.d.ts.map +1 -0
  136. package/types/lib/core/projectConfig.d.ts +6 -2
  137. package/types/lib/core/projectConfig.d.ts.map +1 -1
  138. package/types/lib/core/userAgent.d.ts +56 -0
  139. package/types/lib/core/userAgent.d.ts.map +1 -0
  140. package/types/lib/ecosystems/bunutils.d.ts +12 -5
  141. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  142. package/types/lib/ecosystems/cargoCache.d.ts +75 -0
  143. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
  144. package/types/lib/ecosystems/caxa.d.ts +14 -0
  145. package/types/lib/ecosystems/caxa.d.ts.map +1 -1
  146. package/types/lib/ecosystems/cmakeResolver.d.ts +23 -0
  147. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  148. package/types/lib/ecosystems/cppEvidence.d.ts +2 -2
  149. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  150. package/types/lib/ecosystems/ecosystems.d.ts +15 -1
  151. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  152. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  153. package/types/lib/ecosystems/gradleutils.d.ts +40 -4
  154. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  155. package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
  156. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  157. package/types/lib/ecosystems/kosi.d.ts +86 -0
  158. package/types/lib/ecosystems/kosi.d.ts.map +1 -0
  159. package/types/lib/ecosystems/npmutils.d.ts +42 -0
  160. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  161. package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
  162. package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
  163. package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
  164. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  165. package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
  166. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
  167. package/types/lib/ecosystems/parsers-go.d.ts +10 -0
  168. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  169. package/types/lib/ecosystems/parsers-js.d.ts +10 -4
  170. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  171. package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
  172. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
  173. package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
  174. package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
  175. package/types/lib/ecosystems/parsers-misc.d.ts +90 -3
  176. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  177. package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
  178. package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
  179. package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
  180. package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
  181. package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
  182. package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
  183. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  184. package/types/lib/ecosystems/parsers-r.d.ts +35 -0
  185. package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
  186. package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
  187. package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
  188. package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
  189. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  190. package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
  191. package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
  192. package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
  193. package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
  194. package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
  195. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
  196. package/types/lib/ecosystems/pythonutils.d.ts +1 -1
  197. package/types/lib/ecosystems/rubyutils.d.ts +143 -2
  198. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  199. package/types/lib/ecosystems/rusi.d.ts +13 -0
  200. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  201. package/types/lib/evinser/evinser.d.ts +81 -2
  202. package/types/lib/evinser/evinser.d.ts.map +1 -1
  203. package/types/lib/evinser/swiftsem.d.ts +280 -15
  204. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  205. package/types/lib/helpers/agenticToolutils.d.ts +87 -0
  206. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
  207. package/types/lib/helpers/bomSigner.d.ts +106 -13
  208. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  209. package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
  210. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  211. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  212. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  213. package/types/lib/inventory/atomUtils.d.ts +145 -3
  214. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  215. package/types/lib/inventory/auditCategories.d.ts +4 -0
  216. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  217. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  218. package/types/lib/inventory/cdxrs.d.ts +8 -3
  219. package/types/lib/inventory/cdxrs.d.ts.map +1 -1
  220. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  221. package/types/lib/inventory/depsUtils.d.ts +15 -0
  222. package/types/lib/inventory/depsUtils.d.ts.map +1 -1
  223. package/types/lib/inventory/display.d.ts.map +1 -1
  224. package/types/lib/inventory/dosai.d.ts +87 -1
  225. package/types/lib/inventory/dosai.d.ts.map +1 -1
  226. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  227. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  228. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  229. package/types/lib/inventory/fetchBatch.d.ts +34 -0
  230. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  231. package/types/lib/inventory/fetchRate.d.ts +9 -6
  232. package/types/lib/inventory/fetchRate.d.ts.map +1 -1
  233. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  234. package/types/lib/inventory/jvmToolEnv.d.ts +5 -4
  235. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  236. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  237. package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
  238. package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
  239. package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
  240. package/types/lib/inventory/mcpbBundle.d.ts +19 -0
  241. package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
  242. package/types/lib/inventory/plugins.d.ts.map +1 -1
  243. package/types/lib/inventory/protobom.d.ts +18 -13
  244. package/types/lib/inventory/protobom.d.ts.map +1 -1
  245. package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
  246. package/types/lib/inventory/purl.d.ts +71 -2
  247. package/types/lib/inventory/purl.d.ts.map +1 -1
  248. package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
  249. package/types/lib/inventory/source.d.ts.map +1 -1
  250. package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
  251. package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
  252. package/types/lib/inventory/svelteUtils.d.ts +124 -0
  253. package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
  254. package/types/lib/inventory/traceRunner.d.ts +47 -1
  255. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  256. package/types/lib/managers/binary.d.ts +30 -1
  257. package/types/lib/managers/binary.d.ts.map +1 -1
  258. package/types/lib/managers/docker.d.ts +29 -0
  259. package/types/lib/managers/docker.d.ts.map +1 -1
  260. package/types/lib/managers/oci.d.ts +14 -0
  261. package/types/lib/managers/oci.d.ts.map +1 -1
  262. package/types/lib/parsers/cmakeLists.d.ts +76 -0
  263. package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
  264. package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
  265. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  266. package/types/lib/parsers/largeJson.d.ts +52 -0
  267. package/types/lib/parsers/largeJson.d.ts.map +1 -0
  268. package/types/lib/parsers/npmrc.d.ts +5 -0
  269. package/types/lib/parsers/npmrc.d.ts.map +1 -1
  270. package/types/lib/parsers/onnx.d.ts +28 -0
  271. package/types/lib/parsers/onnx.d.ts.map +1 -0
  272. package/types/lib/parsers/safetensors.d.ts +22 -0
  273. package/types/lib/parsers/safetensors.d.ts.map +1 -0
  274. package/types/lib/stages/postgen/introspection/markers.d.ts +3 -2
  275. package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -1
  276. package/types/lib/stages/postgen/postgen.d.ts +33 -0
  277. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  278. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  279. package/types/lib/validator/index.d.ts +10 -1
  280. package/types/lib/validator/index.d.ts.map +1 -1
  281. package/types/lib/validator/reporters/console.d.ts.map +1 -1
  282. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  283. package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
@@ -1,9 +1,10 @@
1
1
  import { Buffer } from "node:buffer";
2
- import { chmodSync } from "node:fs";
2
+ import { chmodSync, readFileSync } from "node:fs";
3
3
  import { homedir, platform } from "node:os";
4
4
  import { basename, join, resolve } from "node:path";
5
5
 
6
6
  import { build } from "@cdxgen/cdx-purl";
7
+ import * as toml from "smol-toml";
7
8
 
8
9
  import {
9
10
  DEBUG_MODE,
@@ -23,7 +24,7 @@ import { thoughtLog } from "../core/logger.js";
23
24
  import { isWin } from "../core/paths.js";
24
25
  import { collectJarNS } from "../inventory/deps.js";
25
26
  import { minimumGradleVersionForJava } from "../inventory/jvmToolEnv.js";
26
- import { applyPurl } from "../inventory/purl.js";
27
+ import { applyPurl, tryBuildPurl } from "../inventory/purl.js";
27
28
  import { parsePkgJson } from "./parsers-js.js";
28
29
 
29
30
  /**
@@ -40,19 +41,33 @@ function isJvmToolPinned() {
40
41
  /**
41
42
  * Method to return the gradle command to use.
42
43
  *
44
+ * Project-local wrapper scripts (`gradlew`) are attacker-controlled files in
45
+ * the scanned project, so they are only used when dependency tooling is
46
+ * allowed (`options.installDeps !== false`); otherwise the system Gradle is
47
+ * used.
48
+ *
43
49
  * @param {string} srcPath Path to look for gradlew wrapper
44
50
  * @param {string|null} rootPath Root directory to look for gradlew wrapper
51
+ * @param {Object} [options] CLI options (`installDeps` gates wrapper use)
45
52
  */
46
- export function getGradleCommand(srcPath, rootPath) {
53
+ export function getGradleCommand(srcPath, rootPath, options = {}) {
47
54
  let gradleCmd = "gradle";
48
55
  const pinnedToolSelected = isJvmToolPinned();
56
+ // Introspection mode exists to observe the project's own toolchain, so it
57
+ // keeps using wrappers even when dependency installation is disabled.
58
+ const allowProjectWrappers =
59
+ options.installDeps !== false || options.introspect === true;
49
60
 
50
61
  let findGradleFile = "gradlew";
51
62
  if (platform() === "win32") {
52
63
  findGradleFile = "gradlew.bat";
53
64
  }
54
65
 
55
- if (!pinnedToolSelected && safeExistsSync(join(srcPath, findGradleFile))) {
66
+ if (
67
+ allowProjectWrappers &&
68
+ !pinnedToolSelected &&
69
+ safeExistsSync(join(srcPath, findGradleFile))
70
+ ) {
56
71
  // Use local gradle wrapper if available
57
72
  // Enable execute permission
58
73
  try {
@@ -71,6 +86,7 @@ export function getGradleCommand(srcPath, rootPath) {
71
86
  reason: `Selected project-local Gradle wrapper ${gradleCmd}.`,
72
87
  });
73
88
  } else if (
89
+ allowProjectWrappers &&
74
90
  !pinnedToolSelected &&
75
91
  rootPath &&
76
92
  safeExistsSync(join(rootPath, findGradleFile))
@@ -379,8 +395,9 @@ export function executeParallelGradleProperties(
379
395
  dir,
380
396
  allProjectsStr,
381
397
  extraArgs = [],
398
+ options = {},
382
399
  ) {
383
- const gradleCmd = getGradleCommand(dir, null);
400
+ const gradleCmd = getGradleCommand(dir, null, options);
384
401
  const gradleArgs = buildGradleCommandArguments(
385
402
  extraArgs.concat(
386
403
  readEnvironmentVariable("GRADLE_ARGS")
@@ -1204,11 +1221,16 @@ export async function collectGradleDependencies(
1204
1221
  /**
1205
1222
  * Method to return the mill command to use.
1206
1223
  *
1224
+ * The project-local `mill` script is an attacker-controlled file in the
1225
+ * scanned project, so it is only used when dependency tooling is allowed
1226
+ * (`options.installDeps !== false`).
1227
+ *
1207
1228
  * @param {string} srcPath Path to look for mill wrapper
1229
+ * @param {Object} [options] CLI options (`installDeps` gates wrapper use)
1208
1230
  */
1209
- export function getMillCommand(srcPath) {
1231
+ export function getMillCommand(srcPath, options = {}) {
1210
1232
  let millCmd = `mill${platform() === "win32" ? ".bat" : ""}`;
1211
- if (safeExistsSync(join(srcPath, millCmd))) {
1233
+ if (options.installDeps !== false && safeExistsSync(join(srcPath, millCmd))) {
1212
1234
  // Use local mill wrapper if available
1213
1235
  // Enable execute permission
1214
1236
  try {
@@ -1225,15 +1247,24 @@ export function getMillCommand(srcPath) {
1225
1247
  /**
1226
1248
  * Method to return the maven command to use.
1227
1249
  *
1250
+ * Project-local wrapper scripts (`mvnw`) are attacker-controlled files in the
1251
+ * scanned project, so they are only used when dependency tooling is allowed
1252
+ * (`options.installDeps !== false`); otherwise the installed Maven is used.
1253
+ *
1228
1254
  * @param {string} srcPath Path to look for maven wrapper
1229
1255
  * @param {string} rootPath Root directory to look for maven wrapper
1256
+ * @param {Object} [options] CLI options (`installDeps` gates wrapper use)
1230
1257
  */
1231
- export function getMavenCommand(srcPath, rootPath) {
1258
+ export function getMavenCommand(srcPath, rootPath, options = {}) {
1232
1259
  let mavenCmd = "mvn";
1233
1260
  // Check if the wrapper script is both available and functional
1234
1261
  let isWrapperReady = false;
1235
1262
  let isWrapperFound = false;
1236
1263
  const pinnedToolSelected = isJvmToolPinned();
1264
+ // Introspection mode exists to observe the project's own toolchain, so it
1265
+ // keeps using wrappers even when dependency installation is disabled.
1266
+ const allowProjectWrappers =
1267
+ options.installDeps !== false || options.introspect === true;
1237
1268
  let findMavenFile = "mvnw";
1238
1269
  let mavenWrapperCmd = null;
1239
1270
  if (platform() === "win32") {
@@ -1246,7 +1277,11 @@ export function getMavenCommand(srcPath, rootPath) {
1246
1277
  }
1247
1278
  }
1248
1279
 
1249
- if (!pinnedToolSelected && safeExistsSync(join(srcPath, findMavenFile))) {
1280
+ if (
1281
+ allowProjectWrappers &&
1282
+ !pinnedToolSelected &&
1283
+ safeExistsSync(join(srcPath, findMavenFile))
1284
+ ) {
1250
1285
  // Use local maven wrapper if available
1251
1286
  // Enable execute permission
1252
1287
  try {
@@ -1266,6 +1301,7 @@ export function getMavenCommand(srcPath, rootPath) {
1266
1301
  reason: `Found Maven wrapper candidate ${mavenWrapperCmd}.`,
1267
1302
  });
1268
1303
  } else if (
1304
+ allowProjectWrappers &&
1269
1305
  !pinnedToolSelected &&
1270
1306
  rootPath &&
1271
1307
  safeExistsSync(join(rootPath, findMavenFile))
@@ -1386,3 +1422,143 @@ export function getMavenCommand(srcPath, rootPath) {
1386
1422
  }
1387
1423
  return mavenCmd;
1388
1424
  }
1425
+
1426
+ /**
1427
+ * Parse a Gradle version catalog (`gradle/libs.versions.toml`).
1428
+ *
1429
+ * A catalog declares coordinates the build scripts then reference by alias.
1430
+ * It is a declaration, not a resolution: entries may go unused, and their
1431
+ * versions may be references into the `[versions]` table. Because of that,
1432
+ * catalog components are emitted only as a fallback when Gradle itself
1433
+ * produced no dependency information, and every component carries a
1434
+ * `cdx:gradle:catalog=true` property so consumers can tell declared catalog
1435
+ * entries from a resolved graph.
1436
+ *
1437
+ * Only libraries whose version resolves to a literal (inline or via
1438
+ * `version.ref`) are emitted; rich or unresolved versions would produce purls
1439
+ * that identify no artifact. Plugins are skipped because they are build-time
1440
+ * only.
1441
+ *
1442
+ * @param {string} catalogFile Path to `libs.versions.toml`
1443
+ * @returns {Object[]} Package records for resolvable library entries
1444
+ */
1445
+ export function parseGradleVersionCatalog(catalogFile) {
1446
+ let catalog;
1447
+ try {
1448
+ catalog = toml.parse(readFileSync(catalogFile, "utf-8"));
1449
+ } catch (error) {
1450
+ console.warn(`Failed to parse ${catalogFile}: ${error.message}`);
1451
+ return [];
1452
+ }
1453
+ const versions =
1454
+ catalog?.versions && typeof catalog.versions === "object"
1455
+ ? catalog.versions
1456
+ : {};
1457
+ const libraries =
1458
+ catalog?.libraries && typeof catalog.libraries === "object"
1459
+ ? catalog.libraries
1460
+ : {};
1461
+ const pkgList = [];
1462
+ for (const entry of Object.values(libraries)) {
1463
+ const coordinate = catalogCoordinate(entry);
1464
+ if (!coordinate) {
1465
+ continue;
1466
+ }
1467
+ const version = resolveCatalogVersion(entry, versions);
1468
+ if (!version) {
1469
+ continue;
1470
+ }
1471
+ const { group, name } = coordinate;
1472
+ // A catalog is authored input, but a malformed coordinate must cost the
1473
+ // one entry, not the whole scan.
1474
+ const purl = tryBuildPurl({
1475
+ type: "maven",
1476
+ namespace: group,
1477
+ name,
1478
+ version,
1479
+ });
1480
+ if (!purl) {
1481
+ continue;
1482
+ }
1483
+ pkgList.push({
1484
+ group,
1485
+ name,
1486
+ version,
1487
+ type: "library",
1488
+ scope: "required",
1489
+ purl,
1490
+ "bom-ref": decodeURIComponent(purl),
1491
+ properties: [
1492
+ { name: "internal:SrcFile", value: catalogFile },
1493
+ { name: "cdx:gradle:catalog", value: "true" },
1494
+ ],
1495
+ evidence: {
1496
+ identity: {
1497
+ field: "purl",
1498
+ confidence: 0.5,
1499
+ methods: [
1500
+ {
1501
+ technique: "source-code-analysis",
1502
+ confidence: 0.5,
1503
+ value: `Filename ${catalogFile}`,
1504
+ },
1505
+ ],
1506
+ },
1507
+ },
1508
+ });
1509
+ }
1510
+ return pkgList;
1511
+ }
1512
+
1513
+ /**
1514
+ * Read the Maven coordinate of one catalog entry.
1515
+ *
1516
+ * A catalog writes the coordinate either as `module = "group:name"` or as a
1517
+ * separate `group` and `name` pair; both forms are equivalent and both appear
1518
+ * in real catalogs.
1519
+ *
1520
+ * @param {object} entry Catalog library entry
1521
+ * @returns {{group: string, name: string}|undefined} Coordinate, or undefined
1522
+ */
1523
+ function catalogCoordinate(entry) {
1524
+ const module = typeof entry?.module === "string" ? entry.module : undefined;
1525
+ if (module?.includes(":")) {
1526
+ const separator = module.indexOf(":");
1527
+ return {
1528
+ group: module.slice(0, separator),
1529
+ name: module.slice(separator + 1),
1530
+ };
1531
+ }
1532
+ if (typeof entry?.group === "string" && typeof entry?.name === "string") {
1533
+ return { group: entry.group, name: entry.name };
1534
+ }
1535
+ return undefined;
1536
+ }
1537
+
1538
+ /**
1539
+ * Resolve the version of one catalog entry.
1540
+ *
1541
+ * An entry may pin `version = "1.2.3"` directly or reference the versions
1542
+ * table with `version.ref = "alias"`. Version references that are missing, or
1543
+ * that carry Gradle rich-version syntax (ranges, `!!`, separators), resolve
1544
+ * to nothing and the entry is skipped.
1545
+ *
1546
+ * @param {object} entry Catalog library entry
1547
+ * @param {object} versions Parsed `[versions]` table
1548
+ * @returns {string|undefined} Concrete version, or undefined
1549
+ */
1550
+ function resolveCatalogVersion(entry, versions) {
1551
+ const raw =
1552
+ typeof entry?.version === "string"
1553
+ ? entry.version
1554
+ : typeof entry?.version?.ref === "string"
1555
+ ? versions[entry.version.ref]
1556
+ : undefined;
1557
+ if (typeof raw !== "string" || !raw) {
1558
+ return undefined;
1559
+ }
1560
+ if (/[[\](),!]|->|\.\./u.test(raw)) {
1561
+ return undefined;
1562
+ }
1563
+ return raw;
1564
+ }
@@ -8,9 +8,9 @@
8
8
 
9
9
  import { join, resolve } from "node:path";
10
10
 
11
- import { DEBUG_MODE } from "../core/activity.js";
12
11
  import { safeExistsSync } from "../core/fs.js";
13
12
  import { createOccurrenceEvidence } from "../inventory/evidenceUtils.js";
13
+ import { isPkgTypeOnlyImport, setNpmTypeOnlyProperty } from "./npmutils.js";
14
14
  import { parsePkgJson } from "./parsers-js.js";
15
15
 
16
16
  const NPM_BIN_IMPORT_PREFIX = "cdx:npm:bin/";
@@ -109,8 +109,11 @@ function applyNpmBinCommandEvidence(pkg, allImports) {
109
109
  *
110
110
  * For each component, resolves its module aliases (including the deno jsr
111
111
  * specifier), records imported/exported modules as properties, attaches
112
- * occurrence evidence, and adjusts the component scope to "required" when the
113
- * package is used or "optional" when it is not.
112
+ * occurrence evidence, and promotes the component scope to "required" when the
113
+ * package is used. A package with no observed usage keeps the scope its
114
+ * manifest parser assigned: static import analysis cannot see packages loaded
115
+ * from configuration, bundler entrypoints, stylesheets, or plugins, so a
116
+ * missing import is not evidence that a package is optional.
114
117
  *
115
118
  * @param {Array<object>} pkgList Package components to enrich.
116
119
  * @param {object} allImports Map of import specifier to usage evidence objects.
@@ -128,10 +131,6 @@ export async function addEvidenceForImports(
128
131
  const impPkgs = Object.keys(allImports);
129
132
  const exportedPkgs = Object.keys(allExports);
130
133
  for (const pkg of pkgList) {
131
- if (impPkgs?.length) {
132
- // Assume that all packages are optional until we see an evidence
133
- pkg.scope = "optional";
134
- }
135
134
  const { group, name } = pkg;
136
135
  // Evidence belonging to a type must be associated with the package
137
136
  if (group === "@types") {
@@ -155,7 +154,6 @@ export async function addEvidenceForImports(
155
154
  aliases.push(jsrSpecifier);
156
155
  }
157
156
  }
158
- let isImported = false;
159
157
  for (const alias of aliases) {
160
158
  const isWasmAlias = /\.wasm([?#].*)?$/i.test(alias);
161
159
  const all_includes = impPkgs.filter(
@@ -213,11 +211,22 @@ export async function addEvidenceForImports(
213
211
  }
214
212
  // Identify all the imported modules of a component
215
213
  if (impPkgs.includes(alias) || all_includes.length) {
216
- isImported = true;
217
214
  let importedModules = new Set();
218
215
  let wasmExportedModules = new Set();
219
216
  const seenOccurrenceLocations = new Set();
220
- pkg.scope = "required";
217
+ // A package imported only for its types is erased at compile time.
218
+ // Scoping it excluded here, rather than when the BOM is assembled,
219
+ // keeps propagateRequiredScopeFromDependencies from promoting what it
220
+ // depends on. A package whose bin command is used is still required.
221
+ if (
222
+ isPkgTypeOnlyImport(allImports, group, name) &&
223
+ !getNpmBinCommandEvidenceKeys(allImports, pkg).length
224
+ ) {
225
+ pkg.scope = "excluded";
226
+ setNpmTypeOnlyProperty(pkg);
227
+ } else {
228
+ pkg.scope = "required";
229
+ }
221
230
  for (const subevidence of all_includes) {
222
231
  const evidences = allImports[subevidence];
223
232
  for (const evidence of evidences) {
@@ -273,16 +282,6 @@ export async function addEvidenceForImports(
273
282
  }
274
283
  break;
275
284
  }
276
- if (
277
- impPkgs?.length > 0 &&
278
- !isImported &&
279
- DEBUG_MODE &&
280
- pkg?.scope !== "optional"
281
- ) {
282
- console.debug(
283
- `\x1b[1;35mNotice: Package ${pkg.name} has no usage in code. Check if it is needed.\x1b[0m`,
284
- );
285
- }
286
285
  // Capture metadata such as description from local node_modules in deep mode
287
286
  if (deep && !pkg.description && pkg.properties) {
288
287
  let localNodeModulesPath;