@cdxgen/cdxgen 13.4.0 → 13.6.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 (305) hide show
  1. package/README.md +9 -9
  2. package/bin/cdxgen-secure.js +15 -0
  3. package/bin/cdxgen.js +29 -5
  4. package/bin/evinse.js +8 -0
  5. package/bin/hbom.js +15 -7
  6. package/bin/repl.js +29 -0
  7. package/bin/validate.js +6 -0
  8. package/data/cbomosdb-queries.json +13 -1
  9. package/data/component-tags.json +8 -0
  10. package/data/license-aliases.json +24 -2
  11. package/data/queries-darwin.json +25 -1
  12. package/data/queries-win.json +19 -1
  13. package/data/queries.json +41 -0
  14. package/data/remediations.json +407 -1
  15. package/data/rules/ai-bom.yaml +1 -1
  16. package/data/rules/brotli-decoder.yaml +107 -0
  17. package/data/rules/cbom-security.yaml +235 -4
  18. package/data/rules/go-binaries.yaml +46 -0
  19. package/data/rules/golem-go.yaml +80 -0
  20. package/data/rules/hbom-compliance.yaml +40 -22
  21. package/data/rules/hbom-performance.yaml +7 -1
  22. package/data/rules/host-topology.yaml +1 -1
  23. package/data/rules/obom-runtime.yaml +31 -2
  24. package/data/rules/php-runtime.yaml +143 -0
  25. package/data/rules/vscode-extensions.yaml +75 -0
  26. package/data/spdx-license-list.json +121 -7
  27. package/data/spdx-licenses.json +69 -0
  28. package/data/spdx.schema.json +845 -2
  29. package/lib/audit/index.js +14 -0
  30. package/lib/cli/bomAssembly.js +120 -6
  31. package/lib/cli/cliOptions.js +110 -3
  32. package/lib/cli/index.js +182 -6
  33. package/lib/cli/jsBom.js +153 -64
  34. package/lib/cli/jvmBom.js +363 -66
  35. package/lib/cli/managedBom.js +853 -87
  36. package/lib/cli/nativeBom.js +267 -33
  37. package/lib/core/boundedFile.js +344 -0
  38. package/lib/core/brotliProbe.js +182 -0
  39. package/lib/core/env.js +38 -8
  40. package/lib/core/fs.js +40 -19
  41. package/lib/core/httpClient.js +172 -46
  42. package/lib/core/lockPaths.js +38 -0
  43. package/lib/core/paths.js +24 -1
  44. package/lib/core/processTree.js +5 -0
  45. package/lib/core/projectConfig.js +1 -0
  46. package/lib/core/projectTooling.js +97 -0
  47. package/lib/core/propertySanitizer.js +72 -0
  48. package/lib/core/toolRegistry.js +170 -0
  49. package/lib/ecosystems/cppEvidence.js +263 -106
  50. package/lib/ecosystems/cppIncludeResolver.js +16 -5
  51. package/lib/ecosystems/ecosystems.js +25 -10
  52. package/lib/ecosystems/gemHostProbe.js +301 -0
  53. package/lib/ecosystems/golem.js +550 -6
  54. package/lib/ecosystems/gradleutils.js +858 -244
  55. package/lib/ecosystems/parsers-bazel.js +107 -6
  56. package/lib/ecosystems/parsers-dotnet.js +117 -18
  57. package/lib/ecosystems/parsers-go.js +1090 -367
  58. package/lib/ecosystems/parsers-js.js +505 -22
  59. package/lib/ecosystems/parsers-jvm.js +259 -17
  60. package/lib/ecosystems/parsers-misc.js +438 -64
  61. package/lib/ecosystems/parsers-python.js +100 -12
  62. package/lib/ecosystems/pythonutils.js +128 -2
  63. package/lib/ecosystems/rubyutils.js +544 -34
  64. package/lib/ecosystems/rusi.js +10 -0
  65. package/lib/ecosystems/scalacliutils.js +4 -1
  66. package/lib/ecosystems/scalasem.js +798 -151
  67. package/lib/ecosystems/vendoredCode.js +237 -3
  68. package/lib/ecosystems/venvProbe.js +360 -0
  69. package/lib/evinser/evinser.js +472 -61
  70. package/lib/evinser/swiftsem.js +68 -5
  71. package/lib/helpers/agenticToolutils.js +413 -93
  72. package/lib/helpers/vsixutils.js +1574 -315
  73. package/lib/inventory/agentSkillSpec.js +17 -0
  74. package/lib/inventory/apkInstalledDb.js +243 -0
  75. package/lib/inventory/atomUtils.js +110 -0
  76. package/lib/inventory/auditCategories.js +3 -3
  77. package/lib/inventory/brotliDecoder.js +142 -0
  78. package/lib/inventory/buildToolRateLimit.js +1 -1
  79. package/lib/inventory/cacheDir.js +52 -9
  80. package/lib/inventory/cbomutils.js +56 -7
  81. package/lib/inventory/certificates.js +2371 -0
  82. package/lib/inventory/communityAiConfigParser.js +4 -9
  83. package/lib/inventory/deps.js +41 -4
  84. package/lib/inventory/display.js +200 -3
  85. package/lib/inventory/dosai.js +639 -63
  86. package/lib/inventory/dotnetRuntime.js +217 -0
  87. package/lib/inventory/dynamic.js +45 -3
  88. package/lib/inventory/envcontext.js +475 -18
  89. package/lib/inventory/evidenceUtils.js +447 -23
  90. package/lib/inventory/fetchBatch.js +1 -1
  91. package/lib/inventory/formulationParsers.js +32 -3
  92. package/lib/inventory/goBuildInfo.js +140 -0
  93. package/lib/inventory/gtfobins.js +13 -0
  94. package/lib/inventory/hbom.js +67 -7
  95. package/lib/inventory/hbomAnalysis.js +16 -0
  96. package/lib/inventory/hostRuntimeProbe.js +686 -0
  97. package/lib/inventory/hostTopology.js +21 -0
  98. package/lib/inventory/jvmLocalRepos.js +9 -2
  99. package/lib/inventory/jvmToolEnv.js +29 -0
  100. package/lib/inventory/licenseEnhancer.js +12 -0
  101. package/lib/inventory/mavenCentralMirror.js +534 -0
  102. package/lib/inventory/nodeBuildInfo.js +218 -0
  103. package/lib/inventory/nodeHostProbe.js +202 -0
  104. package/lib/inventory/osPackagePurl.js +244 -0
  105. package/lib/inventory/osPackageResolver.js +271 -87
  106. package/lib/inventory/osinfo.js +79 -31
  107. package/lib/inventory/osqueryTransform.js +114 -5
  108. package/lib/inventory/phpBuildInfo.js +438 -0
  109. package/lib/inventory/phpHostProbe.js +185 -0
  110. package/lib/inventory/rootfsCerts.js +426 -0
  111. package/lib/inventory/rubyBuildInfo.js +972 -0
  112. package/lib/inventory/rubyHostProbe.js +218 -0
  113. package/lib/inventory/scalaCoords.js +4 -3
  114. package/lib/inventory/scalaDedupe.js +440 -0
  115. package/lib/inventory/source.js +256 -5
  116. package/lib/inventory/spdx.js +144 -0
  117. package/lib/inventory/traceRunner.js +5 -1
  118. package/lib/inventory/tracedCrypto.js +129 -0
  119. package/lib/managers/binary.js +866 -186
  120. package/lib/managers/docker.js +245 -12
  121. package/lib/parsers/email.js +277 -0
  122. package/lib/parsers/largeJson.js +62 -13
  123. package/lib/parsers/vscodeExtensionId.js +183 -0
  124. package/lib/server/server.js +198 -2
  125. package/lib/stages/postgen/annotator.js +17 -1
  126. package/lib/stages/postgen/introspection/reflect.js +4 -1
  127. package/lib/stages/postgen/postgen.js +5 -5
  128. package/lib/stages/postgen/ruleEngine.js +71 -6
  129. package/lib/stages/postgen/specVersionCompat.js +30 -0
  130. package/lib/stages/pregen/envAudit.js +1 -0
  131. package/lib/stages/pregen/pregen.js +231 -62
  132. package/lib/validator/bomValidator.js +32 -6
  133. package/lib/validator/generated/runtime.mjs +13 -0
  134. package/lib/validator/generated/validate-1.6.mjs +27 -8
  135. package/lib/validator/generated/validate-1.7.mjs +32 -13
  136. package/lib/validator/generated/validate-2.0.mjs +31 -12
  137. package/lib/validator/index.js +1 -1
  138. package/package.json +19 -18
  139. package/types/bin/cdxgen-secure.d.ts +3 -0
  140. package/types/bin/cdxgen-secure.d.ts.map +1 -0
  141. package/types/bin/repl.d.ts.map +1 -1
  142. package/types/lib/audit/index.d.ts.map +1 -1
  143. package/types/lib/cli/bomAssembly.d.ts +22 -3
  144. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  145. package/types/lib/cli/cliOptions.d.ts +43 -0
  146. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  147. package/types/lib/cli/index.d.ts.map +1 -1
  148. package/types/lib/cli/jsBom.d.ts +1 -17
  149. package/types/lib/cli/jsBom.d.ts.map +1 -1
  150. package/types/lib/cli/jvmBom.d.ts +20 -0
  151. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  152. package/types/lib/cli/managedBom.d.ts.map +1 -1
  153. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  154. package/types/lib/core/boundedFile.d.ts +70 -0
  155. package/types/lib/core/boundedFile.d.ts.map +1 -0
  156. package/types/lib/core/brotliProbe.d.ts +37 -0
  157. package/types/lib/core/brotliProbe.d.ts.map +1 -0
  158. package/types/lib/core/env.d.ts +22 -2
  159. package/types/lib/core/env.d.ts.map +1 -1
  160. package/types/lib/core/fs.d.ts.map +1 -1
  161. package/types/lib/core/httpClient.d.ts +24 -0
  162. package/types/lib/core/httpClient.d.ts.map +1 -1
  163. package/types/lib/core/lockPaths.d.ts +19 -0
  164. package/types/lib/core/lockPaths.d.ts.map +1 -0
  165. package/types/lib/core/processTree.d.ts +2 -0
  166. package/types/lib/core/processTree.d.ts.map +1 -1
  167. package/types/lib/core/projectConfig.d.ts.map +1 -1
  168. package/types/lib/core/projectTooling.d.ts +41 -0
  169. package/types/lib/core/projectTooling.d.ts.map +1 -0
  170. package/types/lib/core/propertySanitizer.d.ts +26 -0
  171. package/types/lib/core/propertySanitizer.d.ts.map +1 -1
  172. package/types/lib/core/toolRegistry.d.ts +36 -0
  173. package/types/lib/core/toolRegistry.d.ts.map +1 -0
  174. package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
  175. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  176. package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
  177. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
  178. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  179. package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
  180. package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
  181. package/types/lib/ecosystems/golem.d.ts +81 -1
  182. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  183. package/types/lib/ecosystems/gradleutils.d.ts +57 -21
  184. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  185. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
  186. package/types/lib/ecosystems/parsers-dotnet.d.ts +40 -4
  187. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  188. package/types/lib/ecosystems/parsers-go.d.ts +85 -13
  189. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  190. package/types/lib/ecosystems/parsers-js.d.ts +17 -20
  191. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  192. package/types/lib/ecosystems/parsers-jvm.d.ts +28 -2
  193. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  194. package/types/lib/ecosystems/parsers-misc.d.ts +37 -58
  195. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  196. package/types/lib/ecosystems/parsers-python.d.ts +14 -3
  197. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  198. package/types/lib/ecosystems/pythonutils.d.ts +19 -1
  199. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  200. package/types/lib/ecosystems/rubyutils.d.ts +144 -12
  201. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  202. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  203. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
  204. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  205. package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
  206. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
  207. package/types/lib/ecosystems/venvProbe.d.ts +40 -0
  208. package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
  209. package/types/lib/evinser/evinser.d.ts +88 -8
  210. package/types/lib/evinser/evinser.d.ts.map +1 -1
  211. package/types/lib/evinser/swiftsem.d.ts +10 -0
  212. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  213. package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
  214. package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
  215. package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
  216. package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
  217. package/types/lib/inventory/atomUtils.d.ts +33 -0
  218. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  219. package/types/lib/inventory/brotliDecoder.d.ts +58 -0
  220. package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
  221. package/types/lib/inventory/cacheDir.d.ts +12 -4
  222. package/types/lib/inventory/cacheDir.d.ts.map +1 -1
  223. package/types/lib/inventory/cbomutils.d.ts +17 -0
  224. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  225. package/types/lib/inventory/certificates.d.ts +205 -0
  226. package/types/lib/inventory/certificates.d.ts.map +1 -0
  227. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  228. package/types/lib/inventory/deps.d.ts +2 -1
  229. package/types/lib/inventory/deps.d.ts.map +1 -1
  230. package/types/lib/inventory/display.d.ts +18 -0
  231. package/types/lib/inventory/display.d.ts.map +1 -1
  232. package/types/lib/inventory/dosai.d.ts +162 -7
  233. package/types/lib/inventory/dosai.d.ts.map +1 -1
  234. package/types/lib/inventory/dotnetRuntime.d.ts +44 -0
  235. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
  236. package/types/lib/inventory/dynamic.d.ts +6 -1
  237. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  238. package/types/lib/inventory/envcontext.d.ts +92 -8
  239. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  240. package/types/lib/inventory/evidenceUtils.d.ts +29 -10
  241. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  242. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  243. package/types/lib/inventory/goBuildInfo.d.ts +10 -0
  244. package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
  245. package/types/lib/inventory/gtfobins.d.ts.map +1 -1
  246. package/types/lib/inventory/hbom.d.ts.map +1 -1
  247. package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
  248. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
  249. package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
  250. package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
  251. package/types/lib/inventory/hostTopology.d.ts.map +1 -1
  252. package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
  253. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  254. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
  255. package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
  256. package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
  257. package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
  258. package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
  259. package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
  260. package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
  261. package/types/lib/inventory/osPackagePurl.d.ts +99 -0
  262. package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
  263. package/types/lib/inventory/osPackageResolver.d.ts +44 -2
  264. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  265. package/types/lib/inventory/osinfo.d.ts +31 -6
  266. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  267. package/types/lib/inventory/osqueryTransform.d.ts +51 -0
  268. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
  269. package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
  270. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
  271. package/types/lib/inventory/phpHostProbe.d.ts +35 -0
  272. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
  273. package/types/lib/inventory/rootfsCerts.d.ts +27 -0
  274. package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
  275. package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
  276. package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
  277. package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
  278. package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
  279. package/types/lib/inventory/source.d.ts +38 -0
  280. package/types/lib/inventory/source.d.ts.map +1 -1
  281. package/types/lib/inventory/spdx.d.ts +15 -0
  282. package/types/lib/inventory/spdx.d.ts.map +1 -1
  283. package/types/lib/inventory/traceRunner.d.ts +4 -1
  284. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  285. package/types/lib/inventory/tracedCrypto.d.ts +22 -0
  286. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -1
  287. package/types/lib/managers/binary.d.ts +10 -0
  288. package/types/lib/managers/binary.d.ts.map +1 -1
  289. package/types/lib/managers/docker.d.ts +24 -0
  290. package/types/lib/managers/docker.d.ts.map +1 -1
  291. package/types/lib/parsers/email.d.ts +19 -0
  292. package/types/lib/parsers/email.d.ts.map +1 -0
  293. package/types/lib/parsers/largeJson.d.ts +17 -3
  294. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  295. package/types/lib/server/server.d.ts +8 -0
  296. package/types/lib/server/server.d.ts.map +1 -1
  297. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  298. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
  299. package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
  300. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  301. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  302. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  303. package/types/lib/stages/pregen/pregen.d.ts +26 -9
  304. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  305. package/types/lib/validator/bomValidator.d.ts.map +1 -1
package/lib/cli/jvmBom.js CHANGED
@@ -1,17 +1,13 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import { readdirSync, readFileSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
- import { basename, dirname, join, resolve, sep } from "node:path";
4
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
5
5
  import process from "node:process";
6
6
 
7
7
  import { build } from "@cdxgen/cdx-purl";
8
8
  import { gte, lte } from "semver";
9
9
 
10
- import {
11
- DEBUG_MODE,
12
- isSecureMode,
13
- readEnvironmentVariable,
14
- } from "../core/activity.js";
10
+ import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
15
11
  import { commandOutputText, recordDegradation } from "../core/buildLedger.js";
16
12
  import {
17
13
  deferFailOnError,
@@ -39,23 +35,33 @@ import {
39
35
  } from "../core/fs.js";
40
36
  import { thoughtLog } from "../core/logger.js";
41
37
  import { dirNameStr } from "../core/paths.js";
38
+ import {
39
+ isSecureModeActive,
40
+ reportProjectToolingSkipped,
41
+ } from "../core/projectTooling.js";
42
42
  import { extractJarArchive, getMvnMetadata } from "../ecosystems/ecosystems.js";
43
43
  import {
44
44
  buildGradleCommandArguments,
45
45
  buildObjectForGradleModule,
46
46
  collectGradleDependencies,
47
+ excludeGradleBuildTooling,
47
48
  executeParallelGradleProperties,
48
49
  findMillDaemons,
49
50
  getGradleCommand,
50
51
  getMavenCommand,
51
52
  getMillCommand,
52
53
  getMillVersion,
54
+ gradleArgsFromEnv,
55
+ gradleDependencyTasks,
53
56
  hasGradleInvocationFailure,
57
+ isProjectToolingAllowed,
58
+ loadGradleModulesForReports,
54
59
  parseGradleDep,
55
60
  parseGradleInfoLogsForUrls,
56
61
  parseGradleProperties,
57
62
  parseGradleResolvedDistributions,
58
63
  parseGradleVersionCatalog,
64
+ reconcileGradleComponents,
59
65
  recordGradleInvocationFailure,
60
66
  splitOutputByGradleProjects,
61
67
  stopMillDaemons,
@@ -72,6 +78,7 @@ import {
72
78
  parseMavenTreeJson,
73
79
  parseMillDependency,
74
80
  parsePom,
81
+ parsePomWithLocalParents,
75
82
  readMavenModuleTrees,
76
83
  } from "../ecosystems/parsers-jvm.js";
77
84
  import {
@@ -89,6 +96,7 @@ import {
89
96
  } from "../ecosystems/sbtutils.js";
90
97
  import { collectScalaCliComponents } from "../ecosystems/scalacliutils.js";
91
98
  import { collectScalaJsNpmComponents } from "../ecosystems/scalasem.js";
99
+ import { isInVendoredDir } from "../ecosystems/vendoredCode.js";
92
100
  import {
93
101
  isBuildToolRateLimited,
94
102
  noteBuildToolRateLimit,
@@ -97,6 +105,7 @@ import {
97
105
  collectJarNS,
98
106
  collectMvnDependencies,
99
107
  convertJarNSToPackages,
108
+ namespaceMappingOfComponents,
100
109
  } from "../inventory/deps.js";
101
110
  import { mergeDependencies, trimComponents } from "../inventory/depsUtils.js";
102
111
  import {
@@ -110,8 +119,20 @@ import {
110
119
  projectSettingsArgs,
111
120
  } from "../inventory/jvmLocalRepos.js";
112
121
  import { readGradleWrapperVersion } from "../inventory/jvmToolEnv.js";
113
- import { applyPurl, mavenPurl } from "../inventory/purl.js";
122
+ import {
123
+ bazelMirrorArgs,
124
+ coursierMirrorEnv,
125
+ gradleMirrorInitScriptArgs,
126
+ mavenMirrorSettingsArgs,
127
+ mavenWrapperMirrorEnv,
128
+ sbtMirrorEnv,
129
+ } from "../inventory/mavenCentralMirror.js";
130
+ import { applyPurl, mavenPurl, tryBuildPurl } from "../inventory/purl.js";
114
131
  import { publishedArtifactId } from "../inventory/scalaCoords.js";
132
+ import {
133
+ mergeScalaDuplicateComponents,
134
+ rekeyNamespaceMapping,
135
+ } from "../inventory/scalaDedupe.js";
115
136
  import {
116
137
  buildBomNSData,
117
138
  createDefaultParentComponent,
@@ -251,6 +272,67 @@ export async function collectSbtDependencyJars(pkgList, known = {}) {
251
272
  return nsMapping;
252
273
  }
253
274
 
275
+ /**
276
+ * Keep apart two independent Maven projects that declare the same
277
+ * coordinates, such as sample projects in `one/` and `two/` that both call
278
+ * themselves `com.example:demo:0.0.1-SNAPSHOT`. When `component` has the
279
+ * bom-ref of the parent or of a module already collected but comes from a
280
+ * different pom.xml, it gets the directory of its pom, relative to the scan
281
+ * root, as the purl subpath, and the dependency entries that name it follow.
282
+ *
283
+ * @param {Object} component Root component of a Maven tree, updated in place
284
+ * @param {Object} parentComponent Parent component and the modules under it
285
+ * @param {string} rootPath Directory being scanned
286
+ * @param {Object[]} dependenciesList Dependency entries of the tree, updated in place
287
+ * @returns {boolean} True when the component got a distinct identity
288
+ */
289
+ export function separateDuplicateMavenProject(
290
+ component,
291
+ parentComponent,
292
+ rootPath,
293
+ dependenciesList = [],
294
+ ) {
295
+ const ref = component?.["bom-ref"];
296
+ if (!ref || !component.purl) {
297
+ return false;
298
+ }
299
+ const holder = [parentComponent, ...(parentComponent?.components || [])].find(
300
+ (candidate) => candidate?.["bom-ref"] === ref,
301
+ );
302
+ const srcFile = (c) =>
303
+ c?.properties?.find((p) => p.name === "internal:SrcFile")?.value;
304
+ const ownPom = srcFile(component);
305
+ const holderPom = srcFile(holder);
306
+ if (!ownPom || !holderPom || resolve(ownPom) === resolve(holderPom)) {
307
+ return false;
308
+ }
309
+ const subpath = relative(resolve(rootPath), dirname(resolve(ownPom)))
310
+ .split(sep)
311
+ .filter((segment) => segment && segment !== "..")
312
+ .join("/");
313
+ const purl =
314
+ subpath &&
315
+ tryBuildPurl({
316
+ type: "maven",
317
+ namespace: component.group || null,
318
+ name: component.name,
319
+ version: component.version || null,
320
+ qualifiers: component.qualifiers || { type: "jar" },
321
+ subpath,
322
+ });
323
+ if (!purl) {
324
+ return false;
325
+ }
326
+ applyPurl(component, purl);
327
+ const newRef = component["bom-ref"];
328
+ for (const entry of dependenciesList) {
329
+ if (entry.ref === ref) {
330
+ entry.ref = newRef;
331
+ }
332
+ }
333
+ return true;
334
+ }
335
+
254
336
  /**
255
337
  * The purls and bom-refs of a parent component and the module components
256
338
  * nested under it.
@@ -495,6 +577,63 @@ export function discoverSbtProjectsFromCmd(
495
577
  return discoverSbtProjects(basePath);
496
578
  }
497
579
 
580
+ /**
581
+ * Read one pom.xml without running Maven: the module it declares and its
582
+ * direct dependencies. Versions resolve from the pom's properties and
583
+ * dependencyManagement and from parent poms inside the scanned directory.
584
+ * Versions that an imported BOM or a parent outside the directory manages
585
+ * stay empty, and transitive dependencies are unknown.
586
+ *
587
+ * No dependency edges are produced. The build-fidelity rules (BF-JVM-001,
588
+ * BF-GEN-001) recognise a pom read without Maven by its missing graph, and a
589
+ * graph of direct edges alone would pass for a resolved one.
590
+ *
591
+ * @param {string} pomFile pom.xml to read
592
+ * @param {string} rootPath Scanned directory
593
+ * @param {Map} [cache] Parent pom results shared by the poms of one scan
594
+ * @returns {{moduleComponent: Object|undefined, pkgList: Object[]}}
595
+ */
596
+ export function readMavenModuleStatically(pomFile, rootPath, cache) {
597
+ let pomMap;
598
+ try {
599
+ pomMap = parsePomWithLocalParents(pomFile, rootPath, cache);
600
+ } catch (_err) {
601
+ return { moduleComponent: undefined, pkgList: [] };
602
+ }
603
+ const pkgList = [];
604
+ for (const dep of pomMap?.dependencies || []) {
605
+ const purl = mavenPurl(
606
+ dep.group || undefined,
607
+ dep.name,
608
+ dep.version || undefined,
609
+ dep.qualifiers,
610
+ );
611
+ pkgList.push(
612
+ purl ? { ...dep, purl, "bom-ref": decodeURIComponent(purl) } : dep,
613
+ );
614
+ }
615
+ const { artifactId, groupId, packaging, version } = pomMap?.properties || {};
616
+ const modulePurl = artifactId
617
+ ? mavenPurl(groupId || undefined, artifactId, version || undefined, {
618
+ type: packaging || "jar",
619
+ })
620
+ : undefined;
621
+ if (!modulePurl) {
622
+ return { moduleComponent: undefined, pkgList };
623
+ }
624
+ const moduleComponent = {
625
+ group: groupId || "",
626
+ name: artifactId,
627
+ version: version || "",
628
+ qualifiers: { type: packaging || "jar" },
629
+ type: "application",
630
+ purl: modulePurl,
631
+ "bom-ref": decodeURIComponent(modulePurl),
632
+ properties: [{ name: "internal:SrcFile", value: pomFile }],
633
+ };
634
+ return { moduleComponent, pkgList };
635
+ }
636
+
498
637
  /**
499
638
  * Function to create bom string for Java projects
500
639
  *
@@ -654,6 +793,55 @@ export async function createJavaBom(path, options) {
654
793
  }
655
794
  const firstPom = pomFiles.length ? pomFiles[0] : undefined;
656
795
  let mavenCmd = getMavenCommand(path, path, options);
796
+ // Executing Maven evaluates the pom's build section, loads `.mvn/`
797
+ // configuration such as jvm.config or extensions.xml, and resolves
798
+ // build extensions, so it belongs to the dependency-tooling surface
799
+ // disabled by --no-install-deps and the pre-build lifecycle. The scan
800
+ // then keeps only the direct dependencies each pom declares.
801
+ const mavenToolingAllowed = isProjectToolingAllowed(options);
802
+ if (!mavenToolingAllowed) {
803
+ reportProjectToolingSkipped(
804
+ options,
805
+ "jvm.maven.tooling-disabled",
806
+ "Skipping Maven since dependency installation is disabled. Only the direct dependencies declared in pom.xml files will be used.",
807
+ {
808
+ ecosystem: "java",
809
+ tool: "maven",
810
+ impact: "transitive-deps",
811
+ detail:
812
+ "Dependency installation is disabled, so Maven was not run and only the direct dependencies parsed from pom.xml are available.",
813
+ },
814
+ );
815
+ }
816
+ // Poms read without Maven: what their local parents define is shared
817
+ // across the scan, and each pom adds its module and direct dependencies.
818
+ const staticPomCache = new Map();
819
+ const addStaticMavenModule = (pomFile) => {
820
+ const staticModule = readMavenModuleStatically(
821
+ pomFile,
822
+ path,
823
+ staticPomCache,
824
+ );
825
+ if (staticModule.pkgList.length) {
826
+ pkgList = pkgList.concat(staticModule.pkgList);
827
+ }
828
+ const moduleComponent = staticModule.moduleComponent;
829
+ if (!moduleComponent) {
830
+ return;
831
+ }
832
+ if (!Object.keys(parentComponent).length) {
833
+ parentComponent = moduleComponent;
834
+ parentComponent.components = [];
835
+ } else if (
836
+ parentComponent["bom-ref"] !== moduleComponent["bom-ref"] &&
837
+ !parentComponent.components?.some(
838
+ (c) => c["bom-ref"] === moduleComponent["bom-ref"],
839
+ )
840
+ ) {
841
+ parentComponent.components = parentComponent.components || [];
842
+ parentComponent.components.push(moduleComponent);
843
+ }
844
+ };
657
845
  // Pom files whose own tree a recursive run of their aggregator already
658
846
  // returned. Each is skipped when its turn comes, so a reactor is run
659
847
  // once from its aggregator instead of once per member.
@@ -668,6 +856,12 @@ export async function createJavaBom(path, options) {
668
856
  }
669
857
  continue;
670
858
  }
859
+ // Without Maven every pom is read on its own, Quarkus modules too:
860
+ // the plugin run that would have covered them does not happen.
861
+ if (!mavenToolingAllowed) {
862
+ addStaticMavenModule(f);
863
+ continue;
864
+ }
671
865
  if (
672
866
  isQuarkus &&
673
867
  !options.deep &&
@@ -688,10 +882,7 @@ export async function createJavaBom(path, options) {
688
882
  `Parsing ${f} without Maven: the repository rate limited Maven in this scan.`,
689
883
  );
690
884
  }
691
- const dlist = parsePom(f)?.dependencies || [];
692
- if (dlist.length) {
693
- pkgList = pkgList.concat(dlist);
694
- }
885
+ addStaticMavenModule(f);
695
886
  continue;
696
887
  }
697
888
  if (mavenCmd?.endsWith("mvn")) {
@@ -701,8 +892,19 @@ export async function createJavaBom(path, options) {
701
892
  // user's own settings.xml, with its mirrors and credentials, still
702
893
  // applies on top of it.
703
894
  const projectSettings = projectSettingsArgs(basePath, {
704
- secureMode: isSecureMode,
895
+ secureMode: isSecureModeActive(),
705
896
  });
897
+ // When the mirror switch is on, Maven resolves Central from the
898
+ // mirror through a generated global settings file; a project
899
+ // settings.xml is merged into that copy so its repositories and its
900
+ // own mirror survive. A settings file the user named keeps winning.
901
+ const mirrorSettingsArgs = mavenMirrorSettingsArgs(
902
+ projectSettings.settingsFile,
903
+ );
904
+ const mvnSettingsArgs = mirrorSettingsArgs.length
905
+ ? mirrorSettingsArgs
906
+ : projectSettings.args;
907
+ const mvnSpawnEnv = { ...process.env, ...mavenWrapperMirrorEnv() };
706
908
  if (projectSettings.args.length) {
707
909
  console.log(
708
910
  `Passing ${projectSettings.settingsFile} to Maven as global settings (-gs), so your own settings.xml still applies. Name a settings file in MVN_ARGS to change this.`,
@@ -728,9 +930,10 @@ export async function createJavaBom(path, options) {
728
930
  if (!useMavenDepsTree && !isBuildToolRateLimited("maven")) {
729
931
  thoughtLog("The user wants me to use the cyclonedx-maven plugin.");
730
932
  console.log(`Executing '${mavenCmd}' in`, basePath);
731
- result = safeSpawnSync(mavenCmd, mvnArgs, {
933
+ result = safeSpawnSync(mavenCmd, mvnArgs.concat(mirrorSettingsArgs), {
732
934
  cwd: basePath,
733
935
  shell: isWin,
936
+ env: mvnSpawnEnv,
734
937
  });
735
938
  noteBuildToolRateLimit("maven", commandOutputText(result), {
736
939
  command: `${basename(mavenCmd)} ${mvnArgs.join(" ")}`,
@@ -795,7 +998,7 @@ export async function createJavaBom(path, options) {
795
998
  addArgs = parseMavenArgs(readEnvironmentVariable("MVN_ARGS"));
796
999
  mvnTreeArgs = mvnTreeArgs.concat(addArgs);
797
1000
  }
798
- mvnTreeArgs = mvnTreeArgs.concat(projectSettings.args);
1001
+ mvnTreeArgs = mvnTreeArgs.concat(mvnSettingsArgs);
799
1002
  // For the first pom alone, we need to execute first in non-recursive mode to capture
800
1003
  // the parent component. Then, we execute all of them in recursive mode
801
1004
  // A rate limit the --deep copy-dependencies run met ends it too.
@@ -811,11 +1014,12 @@ export async function createJavaBom(path, options) {
811
1014
  ];
812
1015
  findParentComponentArgs = findParentComponentArgs.concat(
813
1016
  addArgs,
814
- projectSettings.args,
1017
+ mvnSettingsArgs,
815
1018
  );
816
1019
  result = safeSpawnSync(mavenCmd, findParentComponentArgs, {
817
1020
  cwd: basePath,
818
1021
  shell: isWin,
1022
+ env: mvnSpawnEnv,
819
1023
  });
820
1024
  noteBuildToolRateLimit("maven", commandOutputText(result), {
821
1025
  command: `${basename(mavenCmd)} ${findParentComponentArgs.join(" ")}`,
@@ -845,10 +1049,11 @@ export async function createJavaBom(path, options) {
845
1049
  "dependency:tree",
846
1050
  "-N",
847
1051
  `-DoutputFile=${tempMvnParentTreeText}`,
848
- ].concat(addArgs, projectSettings.args);
1052
+ ].concat(addArgs, mvnSettingsArgs);
849
1053
  result = safeSpawnSync(mavenCmd, findParentComponentArgs, {
850
1054
  cwd: basePath,
851
1055
  shell: isWin,
1056
+ env: mvnSpawnEnv,
852
1057
  });
853
1058
  }
854
1059
  if (result.status === 0) {
@@ -893,6 +1098,7 @@ export async function createJavaBom(path, options) {
893
1098
  result = safeSpawnSync(mavenCmd, mvnTreeArgs, {
894
1099
  cwd: basePath,
895
1100
  shell: isWin,
1101
+ env: mvnSpawnEnv,
896
1102
  });
897
1103
  noteBuildToolRateLimit("maven", commandOutputText(result), {
898
1104
  command: `${basename(mavenCmd)} ${mvnTreeArgs.join(" ")}`,
@@ -918,10 +1124,11 @@ export async function createJavaBom(path, options) {
918
1124
  mvnTreeArgs = [
919
1125
  "dependency:tree",
920
1126
  `-DoutputFile=${tempMvnTreeText}`,
921
- ].concat(addArgs, projectSettings.args);
1127
+ ].concat(addArgs, mvnSettingsArgs);
922
1128
  result = safeSpawnSync(mavenCmd, mvnTreeArgs, {
923
1129
  cwd: basePath,
924
1130
  shell: isWin,
1131
+ env: mvnSpawnEnv,
925
1132
  });
926
1133
  noteBuildToolRateLimit("maven", commandOutputText(result), {
927
1134
  command: `${basename(mavenCmd)} ${mvnTreeArgs.join(" ")}`,
@@ -1031,11 +1238,7 @@ export async function createJavaBom(path, options) {
1031
1238
  thoughtLog(
1032
1239
  "**MAVEN**: There appear to be build errors, so the SBOM will be incomplete.",
1033
1240
  );
1034
- const pomMap = parsePom(f);
1035
- const dlist = pomMap?.dependencies || [];
1036
- if (dlist.length) {
1037
- pkgList = pkgList.concat(dlist);
1038
- }
1241
+ addStaticMavenModule(f);
1039
1242
  } else {
1040
1243
  // The text trees exist only when the JSON run failed and was
1041
1244
  // retried. Each reactor module has its own tree, and a member
@@ -1075,6 +1278,12 @@ export async function createJavaBom(path, options) {
1075
1278
  parentComponent = tmpParentComponent;
1076
1279
  parentComponent.components = [];
1077
1280
  } else {
1281
+ separateDuplicateMavenProject(
1282
+ tmpParentComponent,
1283
+ parentComponent,
1284
+ path,
1285
+ parsedList.dependenciesList,
1286
+ );
1078
1287
  parentComponent.components.push(tmpParentComponent);
1079
1288
  }
1080
1289
  if (parsedList?.dependenciesList?.length) {
@@ -1202,9 +1411,14 @@ export async function createJavaBom(path, options) {
1202
1411
  }
1203
1412
  }
1204
1413
  const allProjects = [];
1205
- const allProjectsAddedPurls = [];
1414
+ // Modules keyed by bom-ref: a module without a purl still has a bom-ref
1415
+ // of its own, while every such module shares an undefined purl.
1416
+ const allProjectsAddedRefs = new Map();
1206
1417
  const rootDependsOn = new Set();
1207
1418
  const gradleModules = new Map();
1419
+ // Included builds that build Gradle plugins, such as a build-logic build
1420
+ // included from pluginManagement. Their classpath is build tooling.
1421
+ const gradlePluginBuilds = new Set();
1208
1422
  // Determine the root path for gradle
1209
1423
  // Fixes gradle invocation for microservices-demo
1210
1424
  let gradleRootPath = path;
@@ -1306,14 +1520,19 @@ export async function createJavaBom(path, options) {
1306
1520
  const gradleInitArgs = readEnvironmentVariable("GRADLE_INCLUDED_BUILDS")
1307
1521
  ? []
1308
1522
  : ["--init-script", GRADLE_INIT_SCRIPT];
1523
+ // The mirror init script joins cdxgen's own helper script; Gradle
1524
+ // applies every init script it is given.
1525
+ const gradleMirrorArgs = gradleMirrorInitScriptArgs();
1526
+ if (gradleMirrorArgs.length) {
1527
+ gradleInitArgs.push(...gradleMirrorArgs);
1528
+ }
1309
1529
  if (resolveGradleDistribution && gradleInitArgs.length) {
1310
1530
  gradleInitArgs.push("-PcdxgenResolveDistribution=true");
1311
1531
  }
1312
1532
  // `gradle properties` evaluates the project's build scripts, so it is
1313
1533
  // part of the dependency-tooling surface gated by --no-install-deps
1314
1534
  // (and the pre-build lifecycle).
1315
- const gradleToolingAllowed =
1316
- options.installDeps !== false || options.introspect === true;
1535
+ const gradleToolingAllowed = isProjectToolingAllowed(options);
1317
1536
  if (gradleToolingAllowed) {
1318
1537
  parallelPropTaskOut = executeParallelGradleProperties(
1319
1538
  gradleRootPath,
@@ -1378,12 +1597,19 @@ export async function createJavaBom(path, options) {
1378
1597
  );
1379
1598
  if (!includedBuilds.includes(key)) {
1380
1599
  parentComponent = rootComponent;
1600
+ } else if (retMap.gradlePlugin) {
1601
+ gradlePluginBuilds.add(key);
1381
1602
  }
1382
1603
  gradleModules.set(key, rootComponent);
1383
- if (!allProjectsAddedPurls.includes(rootComponent["purl"])) {
1604
+ if (
1605
+ !allProjectsAddedRefs.has(rootComponent["bom-ref"] ?? rootComponent)
1606
+ ) {
1384
1607
  allProjects.push(rootComponent);
1385
1608
  rootDependsOn.add(rootComponent["bom-ref"]);
1386
- allProjectsAddedPurls.push(rootComponent["purl"]);
1609
+ allProjectsAddedRefs.set(
1610
+ rootComponent["bom-ref"] ?? rootComponent,
1611
+ rootComponent,
1612
+ );
1387
1613
  }
1388
1614
  allProjectsStr = allProjectsStr.concat(retMap.projects);
1389
1615
  }
@@ -1416,14 +1642,60 @@ export async function createJavaBom(path, options) {
1416
1642
  );
1417
1643
  const rootSubProject = retMap.rootProject;
1418
1644
  if (rootSubProject) {
1645
+ // A subproject whose properties task printed nothing is named as
1646
+ // Gradle publishes it by default: the last segment of its path,
1647
+ // with the root project's group and version.
1648
+ if (!splitPropTaskOut.has(subProject)) {
1649
+ retMap.metadata.group ||= parentComponent?.group || "";
1650
+ if (
1651
+ retMap.metadata.version === "latest" &&
1652
+ parentComponent?.version
1653
+ ) {
1654
+ retMap.metadata.version = parentComponent.version;
1655
+ }
1656
+ }
1419
1657
  const rootSubProjectObj = await buildObjectForGradleModule(
1420
- rootSubProject === "root" ? subProject : rootSubProject,
1658
+ rootSubProject === "root"
1659
+ ? subProject.split(":").pop()
1660
+ : rootSubProject,
1421
1661
  retMap.metadata,
1422
1662
  );
1423
- if (!allProjectsAddedPurls.includes(rootSubProjectObj["purl"])) {
1663
+ // Two subprojects that Gradle would publish under the same
1664
+ // coordinates, such as `:a:util` and `:b:util` with a shared group
1665
+ // and version, stay apart: the later one gets its project path as
1666
+ // the purl subpath. A module that shares the root project's
1667
+ // coordinates is the root's own artifact and merges with it.
1668
+ const holder = allProjectsAddedRefs.get(
1669
+ rootSubProjectObj["bom-ref"],
1670
+ );
1671
+ if (
1672
+ holder &&
1673
+ holder !== parentComponent &&
1674
+ holder !== gradleModules.get(subProject)
1675
+ ) {
1676
+ const subpath = subProject.split(":").filter(Boolean).join("/");
1677
+ const distinctPurl = rootSubProjectObj.purl
1678
+ ? tryBuildPurl({
1679
+ type: "maven",
1680
+ namespace: rootSubProjectObj.group || null,
1681
+ name: rootSubProjectObj.name,
1682
+ version: rootSubProjectObj.version || null,
1683
+ qualifiers: { type: "jar" },
1684
+ subpath,
1685
+ })
1686
+ : null;
1687
+ if (distinctPurl) {
1688
+ applyPurl(rootSubProjectObj, distinctPurl);
1689
+ } else {
1690
+ rootSubProjectObj["bom-ref"] =
1691
+ `${rootSubProjectObj["bom-ref"]}#${subpath}`;
1692
+ }
1693
+ }
1694
+ const moduleKey = rootSubProjectObj["bom-ref"] ?? rootSubProjectObj;
1695
+ if (!allProjectsAddedRefs.has(moduleKey)) {
1424
1696
  allProjects.push(rootSubProjectObj);
1425
1697
  rootDependsOn.add(rootSubProjectObj["bom-ref"]);
1426
- allProjectsAddedPurls.push(rootSubProjectObj["purl"]);
1698
+ allProjectsAddedRefs.set(moduleKey, rootSubProjectObj);
1427
1699
  }
1428
1700
  gradleModules.set(subProject, rootSubProjectObj);
1429
1701
  }
@@ -1455,7 +1727,6 @@ export async function createJavaBom(path, options) {
1455
1727
  ? readEnvironmentVariable("GRADLE_DEPENDENCY_TASK")
1456
1728
  : "dependencies";
1457
1729
 
1458
- const gradleSubCommands = [];
1459
1730
  let modulesToSkip = readEnvironmentVariable("GRADLE_SKIP_MODULES")
1460
1731
  ? readEnvironmentVariable("GRADLE_SKIP_MODULES").split(",")
1461
1732
  : [];
@@ -1464,23 +1735,16 @@ export async function createJavaBom(path, options) {
1464
1735
  readEnvironmentVariable("GRADLE_SKIP_MODULE_DEPENDENCIES").split(","),
1465
1736
  );
1466
1737
  }
1467
- if (!modulesToSkip.includes("root")) {
1468
- gradleSubCommands.push(gradleDepTask);
1469
- }
1470
- for (const [key, sp] of gradleModules) {
1471
- //create single command for dependencies tasks on all subprojects
1472
- if (sp.purl !== parentComponent.purl && !modulesToSkip.includes(key)) {
1473
- gradleSubCommands.push(`${key}:${gradleDepTask}`);
1474
- }
1475
- }
1738
+ const gradleSubCommands = gradleDependencyTasks(
1739
+ gradleModules,
1740
+ parentComponent,
1741
+ gradleDepTask,
1742
+ modulesToSkip,
1743
+ );
1476
1744
  const gradleArguments = buildGradleCommandArguments(
1477
- readEnvironmentVariable("GRADLE_ARGS")
1478
- ? readEnvironmentVariable("GRADLE_ARGS").split(" ")
1479
- : [],
1745
+ gradleArgsFromEnv("GRADLE_ARGS"),
1480
1746
  gradleSubCommands,
1481
- readEnvironmentVariable("GRADLE_ARGS_DEPENDENCIES")
1482
- ? readEnvironmentVariable("GRADLE_ARGS_DEPENDENCIES").split(" ")
1483
- : [],
1747
+ gradleArgsFromEnv("GRADLE_ARGS_DEPENDENCIES"),
1484
1748
  gradleCmd.length,
1485
1749
  );
1486
1750
  const allOutputs = [];
@@ -1541,14 +1805,30 @@ export async function createJavaBom(path, options) {
1541
1805
  const perProjectOutput = splitOutputByGradleProjects(cmdOutput, [
1542
1806
  gradleDepTask,
1543
1807
  ]);
1808
+ await loadGradleModulesForReports(
1809
+ perProjectOutput.values(),
1810
+ gradleModules,
1811
+ gradleRootPath,
1812
+ options,
1813
+ );
1814
+ const reportedProjects = new Set(perProjectOutput.keys());
1544
1815
  for (const key of gradleModules.keys()) {
1545
1816
  const parsedList = await parseGradleDep(
1546
1817
  perProjectOutput.has(key) ? perProjectOutput.get(key) : "",
1547
1818
  key,
1548
1819
  gradleModules,
1549
1820
  gradleRootPath,
1821
+ reportedProjects,
1550
1822
  );
1551
1823
  const dlist = parsedList.pkgList;
1824
+ if (
1825
+ dlist?.length &&
1826
+ [...gradlePluginBuilds].some(
1827
+ (build) => key === build || key.startsWith(`${build}:`),
1828
+ )
1829
+ ) {
1830
+ excludeGradleBuildTooling(dlist, gradleModules);
1831
+ }
1552
1832
  if (parsedList?.dependenciesList?.length) {
1553
1833
  dependencies = mergeDependencies(
1554
1834
  dependencies,
@@ -1566,6 +1846,7 @@ export async function createJavaBom(path, options) {
1566
1846
  pkgList = pkgList.concat(dlist);
1567
1847
  }
1568
1848
  }
1849
+ reconcileGradleComponents(pkgList);
1569
1850
  }
1570
1851
  if (pkgList.length) {
1571
1852
  const fileToUrlMap = parseGradleInfoLogsForUrls(sstdout);
@@ -1579,9 +1860,15 @@ export async function createJavaBom(path, options) {
1579
1860
  if (!p.externalReferences) {
1580
1861
  p.externalReferences = [];
1581
1862
  }
1582
- if (!p.externalReferences.find((r) => r.type === "distribution")) {
1863
+ // The repository URLs name jar files, which a platform does not
1864
+ // have. The leading slash keeps group `foo` from matching a URL
1865
+ // under `org/foo/`.
1866
+ if (
1867
+ p.qualifiers?.type !== "pom" &&
1868
+ !p.externalReferences.find((r) => r.type === "distribution")
1869
+ ) {
1583
1870
  const groupPath = p.group.replace(/\./g, "/");
1584
- const matchPath = `${groupPath}/${p.name}/${p.version}/`;
1871
+ const matchPath = `/${groupPath}/${p.name}/${p.version}/`;
1585
1872
  let matchedUrl = Object.values(fileToUrlMap).find((url) =>
1586
1873
  url.includes(matchPath),
1587
1874
  );
@@ -1703,11 +1990,14 @@ export async function createJavaBom(path, options) {
1703
1990
  // Bazel
1704
1991
  // Look for the BUILD file only in the root directory
1705
1992
  // NOTE: This can match BUILD files used by perl, so could lead to errors in some projects
1993
+ // A workspace under third_party/ or vendor/ is a copied project's: its
1994
+ // bazel_dep entries are that project's build tooling, and building it
1995
+ // would run its build.
1706
1996
  const bazelFiles = getAllFiles(
1707
1997
  path,
1708
1998
  `${options.multiProject ? "**/" : ""}{WORKSPACE{,.bazel},MODULE.bazel}`,
1709
1999
  options,
1710
- );
2000
+ ).filter((f) => !isInVendoredDir(path, f));
1711
2001
  if (
1712
2002
  bazelFiles?.length &&
1713
2003
  !hasAnyProjectType(
@@ -1780,6 +2070,9 @@ export async function createJavaBom(path, options) {
1780
2070
  ) {
1781
2071
  bArgs = ["--bazelrc=.bazelrc", "build", bazelTarget];
1782
2072
  }
2073
+ // Repository rules see the mirror through their own environment,
2074
+ // which only a --repo_env flag reaches.
2075
+ bArgs = bArgs.concat(bazelMirrorArgs());
1783
2076
  console.log("Executing", BAZEL_CMD, "in", basePath);
1784
2077
  let result = safeSpawnSync(BAZEL_CMD, bArgs, {
1785
2078
  cwd: basePath,
@@ -1941,7 +2234,7 @@ export async function createJavaBom(path, options) {
1941
2234
  pkgList = pkgList.concat(dlist);
1942
2235
  }
1943
2236
  }
1944
- } else if (options.installDeps === false && !options.introspect) {
2237
+ } else if (!isProjectToolingAllowed(options)) {
1945
2238
  // Executing sbt evaluates the project's build definition (arbitrary
1946
2239
  // Scala), so it belongs to the dependency-tooling surface disabled
1947
2240
  // by --no-install-deps and the pre-build lifecycle.
@@ -1993,7 +2286,7 @@ export async function createJavaBom(path, options) {
1993
2286
  // sbt resolves into the user's own caches, so a project that has been
1994
2287
  // built resolves without the network. The dependency trees download
1995
2288
  // the jar of every dependency they list, which is all --deep reads.
1996
- const env = { ...process.env };
2289
+ const env = { ...process.env, ...sbtMirrorEnv() };
1997
2290
  for (const i in sbtProjects) {
1998
2291
  if (isBuildToolRateLimited("sbt")) {
1999
2292
  // Every remaining sbt build in this scan resolves from the same
@@ -2344,26 +2637,20 @@ export async function createJavaBom(path, options) {
2344
2637
  const jarNSData = await createJarBom(path, options);
2345
2638
  if (jarNSData?.bomJson?.components) {
2346
2639
  pkgList = pkgList.concat(jarNSData?.bomJson?.components);
2347
- const targetJarNSMapping = {};
2348
- for (const p of jarNSData.bomJson.components) {
2349
- if (!p?.purl || !p?.properties?.length) {
2350
- continue;
2351
- }
2352
- const nsProp = p.properties.filter(
2353
- (prop) => prop.name === "internal:Namespaces",
2354
- );
2355
- if (nsProp.length) {
2356
- targetJarNSMapping[p.purl] = nsProp[0].value;
2357
- }
2358
- }
2359
- jarNSMapping = { ...jarNSMapping, ...targetJarNSMapping };
2640
+ jarNSMapping = {
2641
+ ...jarNSMapping,
2642
+ ...namespaceMappingOfComponents(
2643
+ jarNSData.bomJson.components,
2644
+ jarNSMapping,
2645
+ ),
2646
+ };
2360
2647
  }
2361
2648
  }
2362
2649
  }
2363
2650
 
2364
2651
  if (
2365
2652
  millFiles?.length &&
2366
- (options.installDeps !== false || options.introspect === true) &&
2653
+ isProjectToolingAllowed(options) &&
2367
2654
  isPackageManagerAllowed(
2368
2655
  "mill",
2369
2656
  ["bazel", "sbt", "gradle", "maven"],
@@ -2398,10 +2685,12 @@ export async function createJavaBom(path, options) {
2398
2685
  if (DEBUG_MODE) {
2399
2686
  console.log("Executing", millCmd, "in", millRootPath);
2400
2687
  }
2688
+ const millSpawnEnv = { ...process.env, ...coursierMirrorEnv() };
2401
2689
  let sresult = safeSpawnSync(millCmd, millArgs, {
2402
2690
  cwd: millRootPath,
2403
2691
  shell: isWin,
2404
2692
  maxBuffer: MAX_BUFFER * 10,
2693
+ env: millSpawnEnv,
2405
2694
  });
2406
2695
  noteBuildToolRateLimit("mill", commandOutputText(sresult), {
2407
2696
  command: `${basename(millCmd)} ${millArgs.join(" ")}`,
@@ -2440,6 +2729,7 @@ export async function createJavaBom(path, options) {
2440
2729
  sresult = safeSpawnSync(millCmd, millResolveArgs, {
2441
2730
  cwd: millRootPath,
2442
2731
  shell: isWin,
2732
+ env: millSpawnEnv,
2443
2733
  });
2444
2734
  if (sresult.status !== 0 || sresult.error) {
2445
2735
  if (options.failOnError || DEBUG_MODE) {
@@ -2572,7 +2862,7 @@ export async function createJavaBom(path, options) {
2572
2862
  } finally {
2573
2863
  // A build this scan ran can have started a Mill daemon of its own
2574
2864
  // without --no-server; stop those even when a later step failed.
2575
- stopMillDaemons(path, millDaemonsBefore);
2865
+ stopMillDaemons(path, millDaemonsBefore, options);
2576
2866
  }
2577
2867
  // A Scala.js build bundles npm packages: scalajs-bundler installs them
2578
2868
  // under target, and a bundler workspace keeps its lock file beside the
@@ -2604,6 +2894,13 @@ export async function createJavaBom(path, options) {
2604
2894
  // Deduplication and metadata belong to whatever was collected, so they run
2605
2895
  // on the deferred path too.
2606
2896
  pkgList = trimComponents(pkgList);
2897
+ // A library the build tool reported and a jar on disk named again is one
2898
+ // component, wherever the jar scan spelled it differently. The jar
2899
+ // namespace map follows the purls the merge settled on.
2900
+ const scalaMerge = mergeScalaDuplicateComponents(pkgList, dependencies);
2901
+ pkgList = scalaMerge.components;
2902
+ dependencies = scalaMerge.dependencies;
2903
+ jarNSMapping = rekeyNamespaceMapping(jarNSMapping, scalaMerge.purlMap);
2607
2904
  // --deep collects namespaces; it does not opt in to remote licence lookups,
2608
2905
  // which FETCH_LICENSE controls. The project's own modules are never looked
2609
2906
  // up remotely: they are not published.