@cdxgen/cdxgen 13.2.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 (311) hide show
  1. package/README.md +33 -14
  2. package/bin/cdxgen.js +310 -138
  3. package/bin/evinse.js +22 -0
  4. package/bin/repl.js +86 -0
  5. package/bin/sign.js +119 -15
  6. package/bin/tracebom.js +69 -5
  7. package/bin/validate.js +107 -19
  8. package/bin/verify.js +172 -63
  9. package/data/helpers/init.gradle +46 -21
  10. package/data/remediations.json +85 -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/bomAssembly.js +105 -15
  15. package/lib/cli/cliOptions.js +93 -1
  16. package/lib/cli/index.js +156 -58
  17. package/lib/cli/jsBom.js +99 -53
  18. package/lib/cli/jvmBom.js +708 -147
  19. package/lib/cli/managedBom.js +415 -224
  20. package/lib/cli/nativeBom.js +264 -44
  21. package/lib/core/activity.js +12 -0
  22. package/lib/core/env.js +22 -15
  23. package/lib/core/fs.js +176 -7
  24. package/lib/core/httpClient.js +159 -24
  25. package/lib/core/metadataProperties.js +97 -0
  26. package/lib/core/pluginRun.js +184 -0
  27. package/lib/core/processTree.js +100 -0
  28. package/lib/core/projectConfig.js +31 -4
  29. package/lib/core/runState.js +39 -0
  30. package/lib/core/toml.js +72 -0
  31. package/lib/ecosystems/asarutils.js +4 -2
  32. package/lib/ecosystems/bunutils.js +274 -108
  33. package/lib/ecosystems/cargoCache.js +231 -8
  34. package/lib/ecosystems/caxa.js +434 -2
  35. package/lib/ecosystems/cmakeResolver.js +76 -21
  36. package/lib/ecosystems/cppBuildContext.js +631 -0
  37. package/lib/ecosystems/cppEvidence.js +399 -51
  38. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  39. package/lib/ecosystems/denoutils.js +93 -66
  40. package/lib/ecosystems/ecosystems.js +1983 -793
  41. package/lib/ecosystems/golem.js +28 -12
  42. package/lib/ecosystems/gradleutils.js +356 -37
  43. package/lib/ecosystems/jsEvidence.js +104 -44
  44. package/lib/ecosystems/kosi.js +31 -7
  45. package/lib/ecosystems/npmutils.js +174 -9
  46. package/lib/ecosystems/parsers-dotnet.js +616 -159
  47. package/lib/ecosystems/parsers-elm.js +46 -28
  48. package/lib/ecosystems/parsers-gleam.js +3 -4
  49. package/lib/ecosystems/parsers-go.js +371 -70
  50. package/lib/ecosystems/parsers-js.js +263 -76
  51. package/lib/ecosystems/parsers-julia.js +2 -3
  52. package/lib/ecosystems/parsers-jvm.js +204 -12
  53. package/lib/ecosystems/parsers-misc.js +929 -216
  54. package/lib/ecosystems/parsers-python.js +167 -24
  55. package/lib/ecosystems/parsers-rust.js +28 -22
  56. package/lib/ecosystems/parsers-terraform.js +1573 -19
  57. package/lib/ecosystems/pythonutils.js +102 -6
  58. package/lib/ecosystems/remote/huggingface.js +3 -0
  59. package/lib/ecosystems/rubyutils.js +1492 -182
  60. package/lib/ecosystems/rusi.js +54 -10
  61. package/lib/ecosystems/sbtutils.js +243 -187
  62. package/lib/ecosystems/scalacliutils.js +342 -0
  63. package/lib/ecosystems/scalasem.js +2618 -0
  64. package/lib/ecosystems/terraformRegistry.js +288 -0
  65. package/lib/ecosystems/utils.js +2 -0
  66. package/lib/ecosystems/vendoredCode.js +156 -0
  67. package/lib/evinser/evinser.js +1008 -191
  68. package/lib/evinser/swiftsem.js +1914 -302
  69. package/lib/helpers/agenticToolutils.js +2 -2
  70. package/lib/helpers/bomSigner.js +1101 -222
  71. package/lib/inventory/aiCollector.js +4 -1
  72. package/lib/inventory/analyzer.js +110 -127
  73. package/lib/inventory/atomUtils.js +703 -43
  74. package/lib/inventory/auditCategories.js +7 -0
  75. package/lib/inventory/buildToolRateLimit.js +144 -0
  76. package/lib/inventory/cacheCommands.js +2 -1
  77. package/lib/inventory/cdxrs.js +103 -12
  78. package/lib/inventory/cmakeBuildDirs.js +254 -0
  79. package/lib/inventory/deps.js +501 -124
  80. package/lib/inventory/depsUtils.js +183 -11
  81. package/lib/inventory/display.js +4 -5
  82. package/lib/inventory/dosai.js +798 -57
  83. package/lib/inventory/dosaiParsers.js +310 -15
  84. package/lib/inventory/dynamic.js +90 -6
  85. package/lib/inventory/evidenceUtils.js +166 -113
  86. package/lib/inventory/fetchBatch.js +690 -55
  87. package/lib/inventory/fetchDiskCache.js +597 -0
  88. package/lib/inventory/forgeEnricher.js +14 -5
  89. package/lib/inventory/jvmLocalRepos.js +917 -0
  90. package/lib/inventory/mcpConfigParser.js +2 -3
  91. package/lib/inventory/parentSelection.js +146 -0
  92. package/lib/inventory/protobom.js +8 -1
  93. package/lib/inventory/purl.js +152 -3
  94. package/lib/inventory/rustFormulationParser.js +3 -4
  95. package/lib/inventory/sbomDocument.js +2 -70
  96. package/lib/inventory/scalaCoords.js +153 -0
  97. package/lib/inventory/source.js +45 -27
  98. package/lib/inventory/spdx.js +101 -0
  99. package/lib/inventory/traceRunner.js +295 -25
  100. package/lib/inventory/tracedCrypto.js +294 -0
  101. package/lib/managers/binary.js +115 -24
  102. package/lib/managers/docker.js +467 -46
  103. package/lib/managers/oci.js +63 -3
  104. package/lib/parsers/cmakeCache.js +69 -0
  105. package/lib/parsers/cmakeLists.js +466 -0
  106. package/lib/parsers/cmakePresets.js +400 -0
  107. package/lib/parsers/compileCommands.js +557 -0
  108. package/lib/parsers/largeJson.js +454 -0
  109. package/lib/parsers/npmrc.js +19 -1
  110. package/lib/parsers/strictJson.js +322 -0
  111. package/lib/parsers/terraformConfig.js +1073 -0
  112. package/lib/parsers/terraformSource.js +652 -0
  113. package/lib/server/server.js +23 -0
  114. package/lib/stages/postgen/introspection/markers.js +1 -1
  115. package/lib/stages/postgen/postgen.js +83 -16
  116. package/lib/stages/postgen/specVersionCompat.js +32 -0
  117. package/lib/validator/bomValidator.js +6 -16
  118. package/lib/validator/index.js +64 -11
  119. package/lib/validator/reporters/console.js +3 -0
  120. package/lib/validator/reporters/json.js +2 -0
  121. package/lib/validator/reporters/sarif.js +7 -0
  122. package/package.json +23 -23
  123. package/types/lib/audit/index.d.ts.map +1 -1
  124. package/types/lib/cli/bomAssembly.d.ts +2 -1
  125. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  126. package/types/lib/cli/cliOptions.d.ts +33 -1
  127. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  128. package/types/lib/cli/index.d.ts.map +1 -1
  129. package/types/lib/cli/jsBom.d.ts.map +1 -1
  130. package/types/lib/cli/jvmBom.d.ts +24 -1
  131. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  132. package/types/lib/cli/managedBom.d.ts +7 -4
  133. package/types/lib/cli/managedBom.d.ts.map +1 -1
  134. package/types/lib/cli/nativeBom.d.ts +3 -1
  135. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  136. package/types/lib/core/activity.d.ts.map +1 -1
  137. package/types/lib/core/env.d.ts +9 -0
  138. package/types/lib/core/env.d.ts.map +1 -1
  139. package/types/lib/core/fs.d.ts +55 -0
  140. package/types/lib/core/fs.d.ts.map +1 -1
  141. package/types/lib/core/httpClient.d.ts +13 -8
  142. package/types/lib/core/httpClient.d.ts.map +1 -1
  143. package/types/lib/core/metadataProperties.d.ts +39 -0
  144. package/types/lib/core/metadataProperties.d.ts.map +1 -0
  145. package/types/lib/core/pluginRun.d.ts +97 -0
  146. package/types/lib/core/pluginRun.d.ts.map +1 -0
  147. package/types/lib/core/processTree.d.ts +36 -0
  148. package/types/lib/core/processTree.d.ts.map +1 -0
  149. package/types/lib/core/projectConfig.d.ts +6 -2
  150. package/types/lib/core/projectConfig.d.ts.map +1 -1
  151. package/types/lib/core/runState.d.ts +29 -0
  152. package/types/lib/core/runState.d.ts.map +1 -0
  153. package/types/lib/core/toml.d.ts +20 -0
  154. package/types/lib/core/toml.d.ts.map +1 -0
  155. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  156. package/types/lib/ecosystems/bunutils.d.ts +12 -5
  157. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  158. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  159. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  160. package/types/lib/ecosystems/caxa.d.ts +14 -0
  161. package/types/lib/ecosystems/caxa.d.ts.map +1 -1
  162. package/types/lib/ecosystems/cmakeResolver.d.ts +28 -3
  163. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  164. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  165. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  166. package/types/lib/ecosystems/cppEvidence.d.ts +17 -1
  167. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  168. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  169. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  170. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  171. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  172. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  173. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  174. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  175. package/types/lib/ecosystems/gradleutils.d.ts +70 -4
  176. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  177. package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
  178. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  179. package/types/lib/ecosystems/kosi.d.ts +13 -0
  180. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  181. package/types/lib/ecosystems/npmutils.d.ts +57 -0
  182. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  183. package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
  184. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  185. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  186. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  187. package/types/lib/ecosystems/parsers-go.d.ts +54 -2
  188. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  189. package/types/lib/ecosystems/parsers-js.d.ts +16 -5
  190. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  191. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  192. package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
  193. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  194. package/types/lib/ecosystems/parsers-misc.d.ts +70 -4
  195. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  196. package/types/lib/ecosystems/parsers-python.d.ts +53 -3
  197. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  198. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  199. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  200. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  201. package/types/lib/ecosystems/pythonutils.d.ts +27 -2
  202. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  203. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  204. package/types/lib/ecosystems/rubyutils.d.ts +164 -3
  205. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  206. package/types/lib/ecosystems/rusi.d.ts +13 -0
  207. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  208. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  209. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  210. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  211. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  212. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  213. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  214. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  215. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  216. package/types/lib/ecosystems/utils.d.ts +2 -2
  217. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  218. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  219. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  220. package/types/lib/evinser/evinser.d.ts +132 -15
  221. package/types/lib/evinser/evinser.d.ts.map +1 -1
  222. package/types/lib/evinser/swiftsem.d.ts +280 -15
  223. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  224. package/types/lib/helpers/bomSigner.d.ts +182 -13
  225. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  226. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  227. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  228. package/types/lib/inventory/atomUtils.d.ts +219 -5
  229. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  230. package/types/lib/inventory/auditCategories.d.ts +3 -0
  231. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  232. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  233. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  234. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  235. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  236. package/types/lib/inventory/cdxrs.d.ts +8 -3
  237. package/types/lib/inventory/cdxrs.d.ts.map +1 -1
  238. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  239. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  240. package/types/lib/inventory/deps.d.ts +59 -1
  241. package/types/lib/inventory/deps.d.ts.map +1 -1
  242. package/types/lib/inventory/depsUtils.d.ts +15 -0
  243. package/types/lib/inventory/depsUtils.d.ts.map +1 -1
  244. package/types/lib/inventory/display.d.ts.map +1 -1
  245. package/types/lib/inventory/dosai.d.ts +121 -9
  246. package/types/lib/inventory/dosai.d.ts.map +1 -1
  247. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  248. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  249. package/types/lib/inventory/dynamic.d.ts +10 -0
  250. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  251. package/types/lib/inventory/evidenceUtils.d.ts +19 -4
  252. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  253. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  254. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  255. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  256. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  257. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  258. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  259. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  260. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  261. package/types/lib/inventory/parentSelection.d.ts +34 -0
  262. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  263. package/types/lib/inventory/plugins.d.ts.map +1 -1
  264. package/types/lib/inventory/protobom.d.ts.map +1 -1
  265. package/types/lib/inventory/purl.d.ts +71 -2
  266. package/types/lib/inventory/purl.d.ts.map +1 -1
  267. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  268. package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
  269. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  270. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  271. package/types/lib/inventory/source.d.ts.map +1 -1
  272. package/types/lib/inventory/spdx.d.ts +9 -0
  273. package/types/lib/inventory/spdx.d.ts.map +1 -1
  274. package/types/lib/inventory/traceRunner.d.ts +59 -1
  275. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  276. package/types/lib/inventory/tracedCrypto.d.ts +42 -0
  277. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  278. package/types/lib/managers/binary.d.ts +30 -1
  279. package/types/lib/managers/binary.d.ts.map +1 -1
  280. package/types/lib/managers/docker.d.ts +29 -0
  281. package/types/lib/managers/docker.d.ts.map +1 -1
  282. package/types/lib/managers/oci.d.ts +17 -1
  283. package/types/lib/managers/oci.d.ts.map +1 -1
  284. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  285. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  286. package/types/lib/parsers/cmakeLists.d.ts +77 -0
  287. package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
  288. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  289. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  290. package/types/lib/parsers/compileCommands.d.ts +115 -0
  291. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  292. package/types/lib/parsers/largeJson.d.ts +52 -0
  293. package/types/lib/parsers/largeJson.d.ts.map +1 -0
  294. package/types/lib/parsers/npmrc.d.ts +5 -0
  295. package/types/lib/parsers/npmrc.d.ts.map +1 -1
  296. package/types/lib/parsers/strictJson.d.ts +46 -0
  297. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  298. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  299. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  300. package/types/lib/parsers/terraformSource.d.ts +66 -0
  301. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  302. package/types/lib/server/server.d.ts.map +1 -1
  303. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  304. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  305. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  306. package/types/lib/validator/index.d.ts +18 -1
  307. package/types/lib/validator/index.d.ts.map +1 -1
  308. package/types/lib/validator/reporters/console.d.ts.map +1 -1
  309. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  310. package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
  311. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
@@ -1,7 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
 
3
- import * as toml from "smol-toml";
4
-
3
+ import { parseToml } from "../core/toml.js";
5
4
  import { tryBuildPurl } from "../inventory/purl.js";
6
5
 
7
6
  /**
@@ -260,7 +259,7 @@ const JULIA_STDLIBS = new Set([
260
259
  */
261
260
  function readTomlFile(filePath) {
262
261
  try {
263
- return toml.parse(readFileSync(filePath, "utf-8"));
262
+ return parseToml(readFileSync(filePath, "utf-8"));
264
263
  } catch (error) {
265
264
  console.warn(`Failed to parse ${filePath}: ${error.message}`);
266
265
  return undefined;
@@ -1,14 +1,23 @@
1
- import { readFileSync } from "node:fs";
2
- import { basename, dirname, resolve } from "node:path";
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import {
3
+ basename,
4
+ dirname,
5
+ isAbsolute,
6
+ join,
7
+ relative,
8
+ resolve,
9
+ } from "node:path";
3
10
 
4
11
  import { build } from "@cdxgen/cdx-purl";
5
12
  import { parseEDNString } from "edn-data";
6
13
 
7
- import { readEnvironmentVariable } from "../core/activity.js";
14
+ import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
8
15
  import { recordDegradation } from "../core/buildLedger.js";
9
16
  import { includeMavenTestScope } from "../core/env.js";
17
+ import { safeExistsSync } from "../core/fs.js";
10
18
  import { thoughtLog } from "../core/logger.js";
11
19
  import { applyPurl } from "../inventory/purl.js";
20
+ import { parseScalaArtifact } from "../inventory/scalaCoords.js";
12
21
  import { xml2js } from "../parsers/xml.js";
13
22
 
14
23
  /**
@@ -173,6 +182,158 @@ export function parsePom(pomFile) {
173
182
  return { isQuarkus, pomPurl, modules, properties, dependencies: deps };
174
183
  }
175
184
 
185
+ /** Bound on the reactor walk. A pom's module list is untrusted input, so the
186
+ * walk cannot trust it to stay finite. */
187
+ const REACTOR_MODULE_LIMIT = 500;
188
+
189
+ /**
190
+ * Every pom file of the reactor an aggregator pom builds: its own modules,
191
+ * recursively through nested aggregators. A recursive dependency:tree run
192
+ * from the aggregator resolves all of them, so their own runs repeat it.
193
+ *
194
+ * A module entry names a directory relative to its pom, sometimes a direct
195
+ * path to a pom file. Entries that are absolute, or that climb out of the
196
+ * aggregator's directory, are ignored: the scan never listed them, and the
197
+ * walk must not follow a pom's pointers outside its own tree.
198
+ *
199
+ * @param {string} rootPom Path of the aggregator pom.
200
+ * @returns {Set<string>} Pom files of the reactor, excluding the aggregator.
201
+ */
202
+ export function resolveReactorPomFiles(rootPom) {
203
+ const pomFiles = new Set();
204
+ const visited = new Set();
205
+ const rootDir = dirname(resolve(rootPom));
206
+ const queue = [resolve(rootPom)];
207
+ while (queue.length > 0 && pomFiles.size < REACTOR_MODULE_LIMIT) {
208
+ const pom = queue.shift();
209
+ if (visited.has(pom)) {
210
+ continue;
211
+ }
212
+ visited.add(pom);
213
+ let modules;
214
+ try {
215
+ modules = parsePom(pom)?.modules;
216
+ } catch (_err) {
217
+ // An unreadable module pom ends this branch of the walk.
218
+ continue;
219
+ }
220
+ for (const module of modules || []) {
221
+ if (typeof module !== "string") {
222
+ continue;
223
+ }
224
+ const cleaned = module.trim().replaceAll("\\", "/");
225
+ if (
226
+ !cleaned ||
227
+ cleaned.includes("\0") ||
228
+ cleaned.startsWith("/") ||
229
+ /^[A-Za-z]:/.test(cleaned)
230
+ ) {
231
+ continue;
232
+ }
233
+ const modulePom = resolve(
234
+ dirname(pom),
235
+ cleaned.endsWith(".xml") ? cleaned : join(cleaned, "pom.xml"),
236
+ );
237
+ const withinRoot = relative(rootDir, modulePom);
238
+ if (
239
+ !withinRoot ||
240
+ withinRoot.startsWith("..") ||
241
+ isAbsolute(withinRoot) ||
242
+ !safeExistsSync(modulePom)
243
+ ) {
244
+ continue;
245
+ }
246
+ if (visited.has(modulePom)) {
247
+ continue;
248
+ }
249
+ pomFiles.add(modulePom);
250
+ queue.push(modulePom);
251
+ }
252
+ }
253
+ return pomFiles;
254
+ }
255
+
256
+ /**
257
+ * File name stem for the trees of one recursive dependency:tree run. Maven
258
+ * expands the project expressions for each reactor module, so every module
259
+ * writes its own file; a fixed name would hold only the last module's tree.
260
+ */
261
+ export const MAVEN_MODULE_TREE_STEM =
262
+ // biome-ignore lint/suspicious/noTemplateCurlyInString: Maven expands these project expressions itself
263
+ "${project.groupId}-${project.artifactId}";
264
+
265
+ /**
266
+ * The groupId-artifactId key a module's tree file is named by.
267
+ *
268
+ * @param {string} pomFile Path of the module pom.
269
+ * @returns {string|undefined} The key, or undefined when the pom cannot be read.
270
+ */
271
+ function moduleTreeKey(pomFile) {
272
+ try {
273
+ const { groupId, artifactId } = parsePom(pomFile)?.properties || {};
274
+ return groupId && artifactId ? `${groupId}-${artifactId}` : undefined;
275
+ } catch (_err) {
276
+ return undefined;
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Read the trees one dependency:tree run wrote, one file per reactor module
282
+ * (see MAVEN_MODULE_TREE_STEM), and tell which of the reactor's pom files
283
+ * they cover. The pom the run started from comes first, then its modules in
284
+ * reactor order. A module pom counts as covered only when its own tree is
285
+ * among the files, so a module the run left out still runs on its own.
286
+ *
287
+ * @param {string} treeDir Directory the run wrote its trees into.
288
+ * @param {string} rootPom The pom file the run started from.
289
+ * @returns {{trees: Array<{pomFile: string, text: string}>, covered: Set<string>}}
290
+ */
291
+ export function readMavenModuleTrees(treeDir, rootPom) {
292
+ const trees = [];
293
+ const covered = new Set();
294
+ let names;
295
+ try {
296
+ names = readdirSync(treeDir).sort();
297
+ } catch (_err) {
298
+ return { trees, covered };
299
+ }
300
+ const byKey = new Map();
301
+ for (const name of names) {
302
+ const stem = name.replace(/\.(json|txt)$/, "");
303
+ if (stem !== name && !byKey.has(stem)) {
304
+ byKey.set(stem, name);
305
+ }
306
+ }
307
+ const take = (key, pomFile) => {
308
+ const name = byKey.get(key);
309
+ if (!name) {
310
+ return false;
311
+ }
312
+ byKey.delete(key);
313
+ trees.push({
314
+ pomFile,
315
+ text: readFileSync(join(treeDir, name), { encoding: "utf-8" }),
316
+ });
317
+ return true;
318
+ };
319
+ const rootKey = moduleTreeKey(rootPom);
320
+ if (rootKey) {
321
+ take(rootKey, rootPom);
322
+ }
323
+ for (const modulePom of resolveReactorPomFiles(rootPom)) {
324
+ const key = moduleTreeKey(modulePom);
325
+ if (key && take(key, modulePom)) {
326
+ covered.add(modulePom);
327
+ }
328
+ }
329
+ // Trees no pom of the reactor names, such as the single file of a Maven
330
+ // that did not expand the expressions, are kept under the root pom.
331
+ for (const key of [...byKey.keys()]) {
332
+ take(key, rootPom);
333
+ }
334
+ return { trees, covered };
335
+ }
336
+
176
337
  function mapMavenScope(componentScope, isOptional = false) {
177
338
  if (isOptional || componentScope === "test") {
178
339
  return "optional";
@@ -471,10 +632,16 @@ export function parseMavenTree(rawOutput, pomFile) {
471
632
  /**
472
633
  * Parse mill dependencies from file
473
634
  *
635
+ * Scala artifacts are emitted in the sbt purl form: the Scala binary suffix
636
+ * is stripped from the artifact name and the version it carried is recorded
637
+ * in the cdx:scala:compilerVersion property, so one library keeps a single
638
+ * purl whichever build tool reported it.
639
+ *
474
640
  * @param {string} module name of the module
475
641
  * @param {map} dependencies the parsed dependencies
476
642
  * @param {map} relations a map containing all relations
477
643
  * @param {string} millRootPath root of the project
644
+ * @param {string} logFileName name of the tree log the module wrote
478
645
  *
479
646
  * @returns the bom-ref of the module
480
647
  */
@@ -483,6 +650,7 @@ export function parseMillDependency(
483
650
  dependencies,
484
651
  relations,
485
652
  millRootPath,
653
+ logFileName = "ivyDepsTree.log",
486
654
  ) {
487
655
  const treeRegex = /^(?<treeIndentation>(?:[?├│└─ ]{3})+)(?<dependency>.*)/m;
488
656
  const ESC = "\\x1B";
@@ -522,12 +690,7 @@ export function parseMillDependency(
522
690
  // Now put the versions we removed above back into the path and replace the brackets at the same time
523
691
  moduleFilePath = moduleFilePath.replace("[]", `/${versionNumber}/`);
524
692
  }
525
- moduleFilePath = resolve(
526
- millRootPath,
527
- "out",
528
- moduleFilePath,
529
- "ivyDepsTree.log",
530
- );
693
+ moduleFilePath = resolve(millRootPath, "out", moduleFilePath, logFileName);
531
694
  moduleComponent.properties = [
532
695
  {
533
696
  name: "internal:SrcFile",
@@ -547,7 +710,27 @@ export function parseMillDependency(
547
710
  ],
548
711
  },
549
712
  };
550
- const dependencyTreeLog = readFileSync(moduleFilePath, "utf-8");
713
+ let dependencyTreeLog;
714
+ try {
715
+ dependencyTreeLog = readFileSync(moduleFilePath, "utf-8");
716
+ } catch (_err) {
717
+ // A cross-published module writes its log under the dotted version
718
+ // directory (0.23.38) while the resolve output prints it with
719
+ // underscores (0_23_38)
720
+ try {
721
+ dependencyTreeLog = readFileSync(
722
+ moduleFilePath.replace(/(\d+)_(\d+)_(\d+)/, "$1.$2.$3"),
723
+ "utf-8",
724
+ );
725
+ } catch (_err2) {
726
+ if (DEBUG_MODE) {
727
+ console.log(
728
+ `No Mill dependency tree log was written for the module ${module}.`,
729
+ );
730
+ }
731
+ return moduleComponent["bom-ref"];
732
+ }
733
+ }
551
734
  const dependencyTreeLines = dependencyTreeLog
552
735
  .trim()
553
736
  .split("\n")
@@ -568,11 +751,20 @@ export function parseMillDependency(
568
751
  [group, name, version] = match.groups.dependency.split(":");
569
752
  version = versionRegex.exec(version).groups.version;
570
753
  }
754
+ const scalaInfo = parseScalaArtifact(name);
571
755
  const component = completeComponent({
572
756
  group,
573
- name,
757
+ name: scalaInfo.purlName,
574
758
  version,
575
759
  });
760
+ if (scalaInfo.binaryVersion) {
761
+ component.properties = [
762
+ {
763
+ name: "cdx:scala:compilerVersion",
764
+ value: scalaInfo.binaryVersion,
765
+ },
766
+ ];
767
+ }
576
768
  if (!dependencies.has(component["bom-ref"])) {
577
769
  dependencies.set(component["bom-ref"], component);
578
770
  relations.set(component["bom-ref"], []);
@@ -587,7 +779,7 @@ export function parseMillDependency(
587
779
  return moduleComponent["bom-ref"];
588
780
  }
589
781
 
590
- function completeComponent(component) {
782
+ export function completeComponent(component) {
591
783
  component["type"] = component.group ? "library" : "application";
592
784
  try {
593
785
  const purl = build({