@cdxgen/cdxgen 13.5.0 → 13.7.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 (380) hide show
  1. package/README.md +17 -10
  2. package/bin/audit.js +7 -4
  3. package/bin/cdxgen.js +260 -37
  4. package/bin/convert.js +146 -22
  5. package/bin/evinse.js +28 -3
  6. package/bin/repl.js +47 -3
  7. package/bin/sign.js +211 -34
  8. package/bin/tracebom.js +18 -6
  9. package/bin/validate.js +24 -3
  10. package/bin/verify.js +113 -5
  11. package/data/behavior-taxonomy.schema.json +1491 -0
  12. package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
  13. package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
  14. package/data/license-aliases.json +24 -2
  15. package/data/pqc-migration.json +239 -0
  16. package/data/remediations.json +176 -0
  17. package/data/rules/ai-bom.yaml +1 -1
  18. package/data/rules/brotli-decoder.yaml +107 -0
  19. package/data/rules/cbom-security.yaml +4 -3
  20. package/data/rules/package-integrity.yaml +28 -0
  21. package/data/rules/vscode-extensions.yaml +75 -0
  22. package/data/spdx-license-list.json +121 -7
  23. package/data/spdx-licenses.json +69 -0
  24. package/data/spdx.schema.json +845 -2
  25. package/lib/audit/index.js +25 -3
  26. package/lib/cli/bomAssembly.js +172 -4
  27. package/lib/cli/cliOptions.js +121 -17
  28. package/lib/cli/index.js +720 -902
  29. package/lib/cli/jsBom.js +156 -64
  30. package/lib/cli/jvmBom.js +602 -86
  31. package/lib/cli/managedBom.js +161 -19
  32. package/lib/cli/nativeBom.js +52 -5
  33. package/lib/core/boundedFile.js +344 -0
  34. package/lib/core/brotliProbe.js +182 -0
  35. package/lib/core/env.js +34 -0
  36. package/lib/core/fs.js +138 -3
  37. package/lib/core/globPatterns.js +218 -0
  38. package/lib/core/httpClient.js +98 -1
  39. package/lib/core/lockPaths.js +38 -0
  40. package/lib/core/paths.js +24 -1
  41. package/lib/core/processTree.js +100 -8
  42. package/lib/core/projectConfig.js +332 -25
  43. package/lib/core/toolRegistry.js +184 -0
  44. package/lib/ecosystems/cppEvidence.js +263 -106
  45. package/lib/ecosystems/cppIncludeResolver.js +16 -5
  46. package/lib/ecosystems/denoutils.js +2 -2
  47. package/lib/ecosystems/ecosystems.js +25 -10
  48. package/lib/ecosystems/gemHostProbe.js +301 -0
  49. package/lib/ecosystems/golem.js +3 -8
  50. package/lib/ecosystems/gradleutils.js +1909 -229
  51. package/lib/ecosystems/jsEvidence.js +90 -38
  52. package/lib/ecosystems/kosi.js +3 -5
  53. package/lib/ecosystems/npmVersionResolution.js +376 -0
  54. package/lib/ecosystems/parsers-bazel.js +107 -6
  55. package/lib/ecosystems/parsers-js.js +505 -22
  56. package/lib/ecosystems/parsers-misc.js +209 -20
  57. package/lib/ecosystems/parsers-python.js +178 -30
  58. package/lib/ecosystems/parsers-rust.js +117 -15
  59. package/lib/ecosystems/pythonutils.js +128 -2
  60. package/lib/ecosystems/remote/huggingface.js +22 -0
  61. package/lib/ecosystems/rubyutils.js +538 -31
  62. package/lib/ecosystems/rusi.js +2 -4
  63. package/lib/ecosystems/scalacliutils.js +2 -0
  64. package/lib/ecosystems/scalasem.js +798 -152
  65. package/lib/ecosystems/vendoredCode.js +237 -3
  66. package/lib/ecosystems/venvProbe.js +360 -0
  67. package/lib/evinser/evinser.js +536 -71
  68. package/lib/evinser/scalasem.js +5 -1
  69. package/lib/helpers/agenticToolutils.js +525 -94
  70. package/lib/helpers/bomSigner.js +43 -9
  71. package/lib/helpers/jssSigner.js +1276 -0
  72. package/lib/helpers/vsixutils.js +1574 -315
  73. package/lib/inventory/agentBlueprint.js +193 -0
  74. package/lib/inventory/agentToolNames.js +145 -0
  75. package/lib/inventory/aiCollector.js +153 -7
  76. package/lib/inventory/aiInventory.js +44 -8
  77. package/lib/inventory/aiProviders.js +324 -0
  78. package/lib/inventory/atomUtils.js +77 -0
  79. package/lib/inventory/bomUtils.js +8 -1
  80. package/lib/inventory/brotliDecoder.js +142 -0
  81. package/lib/inventory/buildToolRateLimit.js +1 -1
  82. package/lib/inventory/cbomutils.js +56 -7
  83. package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
  84. package/lib/inventory/citations.js +31 -2
  85. package/lib/inventory/communityAiConfigParser.js +978 -80
  86. package/lib/inventory/cyclonedx20.js +152 -0
  87. package/lib/inventory/cyclonedx20Common.js +286 -0
  88. package/lib/inventory/cyclonedx20Derived.js +159 -0
  89. package/lib/inventory/cyclonedx20Parties.js +552 -0
  90. package/lib/inventory/cyclonedx20Read.js +768 -0
  91. package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
  92. package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
  93. package/lib/inventory/cyclonedx20ReadContext.js +380 -0
  94. package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
  95. package/lib/inventory/cyclonedx20ReadSections.js +599 -0
  96. package/lib/inventory/cyclonedx20Render.js +766 -0
  97. package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
  98. package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
  99. package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
  100. package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
  101. package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
  102. package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
  103. package/lib/inventory/cyclonedx20RenderSections.js +499 -0
  104. package/lib/inventory/cyclonedx20Tables.js +594 -0
  105. package/lib/inventory/deps.js +141 -13
  106. package/lib/inventory/dosai.js +375 -6
  107. package/lib/inventory/dosaiParsers.js +3 -6
  108. package/lib/inventory/dotnetRuntime.js +367 -0
  109. package/lib/inventory/ecosystemFormulationParsers.js +979 -0
  110. package/lib/inventory/envcontext.js +436 -48
  111. package/lib/inventory/evidenceUtils.js +13 -1
  112. package/lib/inventory/fetchBatch.js +1 -1
  113. package/lib/inventory/formulationParsers.js +124 -14
  114. package/lib/inventory/gtfobins.js +13 -0
  115. package/lib/inventory/hbom.js +45 -6
  116. package/lib/inventory/hbomLoader.js +166 -0
  117. package/lib/inventory/hostRuntimeProbe.js +686 -0
  118. package/lib/inventory/jvmLocalRepos.js +9 -2
  119. package/lib/inventory/jvmToolEnv.js +29 -0
  120. package/lib/inventory/licenseEnhancer.js +12 -0
  121. package/lib/inventory/mavenCentralMirror.js +534 -0
  122. package/lib/inventory/mcpConfigParser.js +5 -0
  123. package/lib/inventory/nodeBuildInfo.js +218 -0
  124. package/lib/inventory/nodeHostProbe.js +202 -0
  125. package/lib/inventory/occurrenceKey.js +90 -0
  126. package/lib/inventory/osPackageResolver.js +182 -0
  127. package/lib/inventory/phpBuildInfo.js +10 -59
  128. package/lib/inventory/phpHostProbe.js +32 -488
  129. package/lib/inventory/pqcMigration.js +426 -0
  130. package/lib/inventory/rubyBuildInfo.js +972 -0
  131. package/lib/inventory/rubyHostProbe.js +218 -0
  132. package/lib/inventory/rustFormulationParser.js +21 -10
  133. package/lib/inventory/scalaCoords.js +4 -3
  134. package/lib/inventory/scalaDedupe.js +440 -0
  135. package/lib/inventory/source.js +114 -8
  136. package/lib/managers/binary.js +337 -3
  137. package/lib/managers/docker.js +79 -0
  138. package/lib/parsers/huggingfaceManifest.js +9 -1
  139. package/lib/parsers/huggingfaceUtils.js +18 -0
  140. package/lib/parsers/kiroPermissions.js +166 -0
  141. package/lib/parsers/largeJson.js +28 -11
  142. package/lib/parsers/vscodeExtensionId.js +183 -0
  143. package/lib/server/server.js +48 -35
  144. package/lib/stages/postgen/postgen.js +41 -1
  145. package/lib/stages/postgen/ruleEngine.js +13 -5
  146. package/lib/stages/postgen/specVersionCompat.js +76 -0
  147. package/lib/stages/pregen/envAudit.js +1 -0
  148. package/lib/stages/pregen/pregen.js +114 -57
  149. package/lib/validator/bomValidator.js +14 -2
  150. package/lib/validator/complianceEngine.js +30 -3
  151. package/lib/validator/complianceRules.js +1416 -4
  152. package/lib/validator/generated/validate-1.6.mjs +1 -1
  153. package/lib/validator/generated/validate-1.7.mjs +1 -1
  154. package/lib/validator/generated/validate-2.0.mjs +57554 -13851
  155. package/lib/validator/index.js +49 -8
  156. package/lib/validator/reporters/json.js +6 -0
  157. package/package.json +11 -10
  158. package/types/bin/repl.d.ts.map +1 -1
  159. package/types/lib/audit/index.d.ts +6 -2
  160. package/types/lib/audit/index.d.ts.map +1 -1
  161. package/types/lib/cli/bomAssembly.d.ts +29 -1
  162. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  163. package/types/lib/cli/cliOptions.d.ts +6 -0
  164. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  165. package/types/lib/cli/index.d.ts.map +1 -1
  166. package/types/lib/cli/jsBom.d.ts +3 -18
  167. package/types/lib/cli/jsBom.d.ts.map +1 -1
  168. package/types/lib/cli/jvmBom.d.ts +15 -0
  169. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  170. package/types/lib/cli/managedBom.d.ts.map +1 -1
  171. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  172. package/types/lib/core/boundedFile.d.ts +100 -0
  173. package/types/lib/core/boundedFile.d.ts.map +1 -0
  174. package/types/lib/core/brotliProbe.d.ts +37 -0
  175. package/types/lib/core/brotliProbe.d.ts.map +1 -0
  176. package/types/lib/core/env.d.ts +14 -0
  177. package/types/lib/core/env.d.ts.map +1 -1
  178. package/types/lib/core/fs.d.ts +4 -0
  179. package/types/lib/core/fs.d.ts.map +1 -1
  180. package/types/lib/core/globPatterns.d.ts +70 -0
  181. package/types/lib/core/globPatterns.d.ts.map +1 -0
  182. package/types/lib/core/httpClient.d.ts +24 -0
  183. package/types/lib/core/httpClient.d.ts.map +1 -1
  184. package/types/lib/core/lockPaths.d.ts +19 -0
  185. package/types/lib/core/lockPaths.d.ts.map +1 -0
  186. package/types/lib/core/paths.d.ts +9 -0
  187. package/types/lib/core/paths.d.ts.map +1 -1
  188. package/types/lib/core/processTree.d.ts +17 -4
  189. package/types/lib/core/processTree.d.ts.map +1 -1
  190. package/types/lib/core/projectConfig.d.ts +47 -8
  191. package/types/lib/core/projectConfig.d.ts.map +1 -1
  192. package/types/lib/core/toolRegistry.d.ts +36 -0
  193. package/types/lib/core/toolRegistry.d.ts.map +1 -0
  194. package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
  195. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  196. package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
  197. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
  198. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  199. package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
  200. package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
  201. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  202. package/types/lib/ecosystems/gradleutils.d.ts +294 -20
  203. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  204. package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
  205. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  206. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  207. package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
  208. package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
  209. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
  210. package/types/lib/ecosystems/parsers-js.d.ts +17 -20
  211. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  212. package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
  213. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  214. package/types/lib/ecosystems/parsers-python.d.ts +10 -0
  215. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  216. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  217. package/types/lib/ecosystems/pythonutils.d.ts +19 -1
  218. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  219. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  220. package/types/lib/ecosystems/rubyutils.d.ts +144 -12
  221. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  222. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  223. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
  224. package/types/lib/ecosystems/scalasem.d.ts +58 -16
  225. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  226. package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
  227. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
  228. package/types/lib/ecosystems/venvProbe.d.ts +40 -0
  229. package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
  230. package/types/lib/evinser/evinser.d.ts +120 -13
  231. package/types/lib/evinser/evinser.d.ts.map +1 -1
  232. package/types/lib/evinser/scalasem.d.ts.map +1 -1
  233. package/types/lib/helpers/agenticToolutils.d.ts +53 -19
  234. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
  235. package/types/lib/helpers/bomSigner.d.ts +20 -3
  236. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  237. package/types/lib/helpers/jssSigner.d.ts +288 -0
  238. package/types/lib/helpers/jssSigner.d.ts.map +1 -0
  239. package/types/lib/helpers/vsixutils.d.ts +303 -19
  240. package/types/lib/helpers/vsixutils.d.ts.map +1 -1
  241. package/types/lib/inventory/agentBlueprint.d.ts +70 -0
  242. package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
  243. package/types/lib/inventory/agentToolNames.d.ts +51 -0
  244. package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
  245. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  246. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  247. package/types/lib/inventory/aiProviders.d.ts +85 -0
  248. package/types/lib/inventory/aiProviders.d.ts.map +1 -0
  249. package/types/lib/inventory/atomUtils.d.ts +19 -0
  250. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  251. package/types/lib/inventory/bomUtils.d.ts.map +1 -1
  252. package/types/lib/inventory/brotliDecoder.d.ts +58 -0
  253. package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
  254. package/types/lib/inventory/cbomutils.d.ts +17 -0
  255. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  256. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
  257. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
  258. package/types/lib/inventory/citations.d.ts +5 -0
  259. package/types/lib/inventory/citations.d.ts.map +1 -1
  260. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  261. package/types/lib/inventory/cyclonedx20.d.ts +99 -0
  262. package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
  263. package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
  264. package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
  265. package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
  266. package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
  267. package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
  268. package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
  269. package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
  270. package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
  271. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
  272. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
  273. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
  274. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
  275. package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
  276. package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
  277. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
  278. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
  279. package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
  280. package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
  281. package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
  282. package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
  283. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
  284. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
  285. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
  286. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
  287. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
  288. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
  289. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
  290. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
  291. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
  292. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
  293. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
  294. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
  295. package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
  296. package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
  297. package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
  298. package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
  299. package/types/lib/inventory/deps.d.ts +35 -2
  300. package/types/lib/inventory/deps.d.ts.map +1 -1
  301. package/types/lib/inventory/dosai.d.ts +36 -1
  302. package/types/lib/inventory/dosai.d.ts.map +1 -1
  303. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  304. package/types/lib/inventory/dotnetRuntime.d.ts +91 -0
  305. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
  306. package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
  307. package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
  308. package/types/lib/inventory/envcontext.d.ts +74 -12
  309. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  310. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  311. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  312. package/types/lib/inventory/gtfobins.d.ts.map +1 -1
  313. package/types/lib/inventory/hbom.d.ts +11 -0
  314. package/types/lib/inventory/hbom.d.ts.map +1 -1
  315. package/types/lib/inventory/hbomLoader.d.ts +54 -0
  316. package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
  317. package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
  318. package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
  319. package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
  320. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
  321. package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
  322. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  323. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
  324. package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
  325. package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
  326. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  327. package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
  328. package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
  329. package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
  330. package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
  331. package/types/lib/inventory/occurrenceKey.d.ts +52 -0
  332. package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
  333. package/types/lib/inventory/osPackageResolver.d.ts +38 -0
  334. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  335. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
  336. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
  337. package/types/lib/inventory/pqcMigration.d.ts +84 -0
  338. package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
  339. package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
  340. package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
  341. package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
  342. package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
  343. package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
  344. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  345. package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
  346. package/types/lib/inventory/scalaDedupe.d.ts +43 -0
  347. package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
  348. package/types/lib/inventory/source.d.ts +25 -0
  349. package/types/lib/inventory/source.d.ts.map +1 -1
  350. package/types/lib/managers/binary.d.ts.map +1 -1
  351. package/types/lib/managers/docker.d.ts +12 -0
  352. package/types/lib/managers/docker.d.ts.map +1 -1
  353. package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
  354. package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
  355. package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
  356. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  357. package/types/lib/parsers/kiroPermissions.d.ts +62 -0
  358. package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
  359. package/types/lib/parsers/largeJson.d.ts +4 -3
  360. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  361. package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
  362. package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
  363. package/types/lib/server/server.d.ts.map +1 -1
  364. package/types/lib/stages/postgen/postgen.d.ts +7 -0
  365. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  366. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  367. package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
  368. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  369. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  370. package/types/lib/stages/pregen/pregen.d.ts +12 -8
  371. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  372. package/types/lib/validator/bomValidator.d.ts +5 -1
  373. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  374. package/types/lib/validator/complianceEngine.d.ts +4 -4
  375. package/types/lib/validator/complianceEngine.d.ts.map +1 -1
  376. package/types/lib/validator/complianceRules.d.ts +14 -2
  377. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  378. package/types/lib/validator/index.d.ts +10 -0
  379. package/types/lib/validator/index.d.ts.map +1 -1
  380. package/types/lib/validator/reporters/json.d.ts.map +1 -1
package/lib/cli/jvmBom.js CHANGED
@@ -1,7 +1,7 @@
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";
@@ -41,22 +41,34 @@ import {
41
41
  } from "../core/projectTooling.js";
42
42
  import { extractJarArchive, getMvnMetadata } from "../ecosystems/ecosystems.js";
43
43
  import {
44
+ applyGradleVerificationMetadata,
44
45
  buildGradleCommandArguments,
45
46
  buildObjectForGradleModule,
46
47
  collectGradleDependencies,
48
+ excludeGradleBuildTooling,
47
49
  executeParallelGradleProperties,
50
+ findGradleLockfiles,
51
+ findGradleVerificationMetadata,
48
52
  findMillDaemons,
49
53
  getGradleCommand,
50
54
  getMavenCommand,
51
55
  getMillCommand,
52
56
  getMillVersion,
57
+ gradleArgsFromEnv,
58
+ gradleDependencyTasks,
59
+ gradleVerificationMainArtifact,
53
60
  hasGradleInvocationFailure,
54
61
  isProjectToolingAllowed,
62
+ loadGradleModulesForReports,
55
63
  parseGradleDep,
56
64
  parseGradleInfoLogsForUrls,
65
+ parseGradleLockfiles,
57
66
  parseGradleProperties,
58
67
  parseGradleResolvedDistributions,
68
+ parseGradleVerificationMetadata,
59
69
  parseGradleVersionCatalog,
70
+ readGradleRootProjectName,
71
+ reconcileGradleComponents,
60
72
  recordGradleInvocationFailure,
61
73
  splitOutputByGradleProjects,
62
74
  stopMillDaemons,
@@ -91,6 +103,7 @@ import {
91
103
  } from "../ecosystems/sbtutils.js";
92
104
  import { collectScalaCliComponents } from "../ecosystems/scalacliutils.js";
93
105
  import { collectScalaJsNpmComponents } from "../ecosystems/scalasem.js";
106
+ import { isInVendoredDir } from "../ecosystems/vendoredCode.js";
94
107
  import {
95
108
  isBuildToolRateLimited,
96
109
  noteBuildToolRateLimit,
@@ -99,6 +112,7 @@ import {
99
112
  collectJarNS,
100
113
  collectMvnDependencies,
101
114
  convertJarNSToPackages,
115
+ namespaceMappingOfComponents,
102
116
  } from "../inventory/deps.js";
103
117
  import { mergeDependencies, trimComponents } from "../inventory/depsUtils.js";
104
118
  import {
@@ -112,8 +126,20 @@ import {
112
126
  projectSettingsArgs,
113
127
  } from "../inventory/jvmLocalRepos.js";
114
128
  import { readGradleWrapperVersion } from "../inventory/jvmToolEnv.js";
115
- import { applyPurl, mavenPurl } from "../inventory/purl.js";
129
+ import {
130
+ bazelMirrorArgs,
131
+ coursierMirrorEnv,
132
+ gradleMirrorInitScriptArgs,
133
+ mavenMirrorSettingsArgs,
134
+ mavenWrapperMirrorEnv,
135
+ sbtMirrorEnv,
136
+ } from "../inventory/mavenCentralMirror.js";
137
+ import { applyPurl, mavenPurl, tryBuildPurl } from "../inventory/purl.js";
116
138
  import { publishedArtifactId } from "../inventory/scalaCoords.js";
139
+ import {
140
+ mergeScalaDuplicateComponents,
141
+ rekeyNamespaceMapping,
142
+ } from "../inventory/scalaDedupe.js";
117
143
  import {
118
144
  buildBomNSData,
119
145
  createDefaultParentComponent,
@@ -253,6 +279,67 @@ export async function collectSbtDependencyJars(pkgList, known = {}) {
253
279
  return nsMapping;
254
280
  }
255
281
 
282
+ /**
283
+ * Keep apart two independent Maven projects that declare the same
284
+ * coordinates, such as sample projects in `one/` and `two/` that both call
285
+ * themselves `com.example:demo:0.0.1-SNAPSHOT`. When `component` has the
286
+ * bom-ref of the parent or of a module already collected but comes from a
287
+ * different pom.xml, it gets the directory of its pom, relative to the scan
288
+ * root, as the purl subpath, and the dependency entries that name it follow.
289
+ *
290
+ * @param {Object} component Root component of a Maven tree, updated in place
291
+ * @param {Object} parentComponent Parent component and the modules under it
292
+ * @param {string} rootPath Directory being scanned
293
+ * @param {Object[]} dependenciesList Dependency entries of the tree, updated in place
294
+ * @returns {boolean} True when the component got a distinct identity
295
+ */
296
+ export function separateDuplicateMavenProject(
297
+ component,
298
+ parentComponent,
299
+ rootPath,
300
+ dependenciesList = [],
301
+ ) {
302
+ const ref = component?.["bom-ref"];
303
+ if (!ref || !component.purl) {
304
+ return false;
305
+ }
306
+ const holder = [parentComponent, ...(parentComponent?.components || [])].find(
307
+ (candidate) => candidate?.["bom-ref"] === ref,
308
+ );
309
+ const srcFile = (c) =>
310
+ c?.properties?.find((p) => p.name === "internal:SrcFile")?.value;
311
+ const ownPom = srcFile(component);
312
+ const holderPom = srcFile(holder);
313
+ if (!ownPom || !holderPom || resolve(ownPom) === resolve(holderPom)) {
314
+ return false;
315
+ }
316
+ const subpath = relative(resolve(rootPath), dirname(resolve(ownPom)))
317
+ .split(sep)
318
+ .filter((segment) => segment && segment !== "..")
319
+ .join("/");
320
+ const purl =
321
+ subpath &&
322
+ tryBuildPurl({
323
+ type: "maven",
324
+ namespace: component.group || null,
325
+ name: component.name,
326
+ version: component.version || null,
327
+ qualifiers: component.qualifiers || { type: "jar" },
328
+ subpath,
329
+ });
330
+ if (!purl) {
331
+ return false;
332
+ }
333
+ applyPurl(component, purl);
334
+ const newRef = component["bom-ref"];
335
+ for (const entry of dependenciesList) {
336
+ if (entry.ref === ref) {
337
+ entry.ref = newRef;
338
+ }
339
+ }
340
+ return true;
341
+ }
342
+
256
343
  /**
257
344
  * The purls and bom-refs of a parent component and the module components
258
345
  * nested under it.
@@ -565,6 +652,33 @@ export async function createJavaBom(path, options) {
565
652
  let jarNSMapping = {};
566
653
  let pkgList = [];
567
654
  let dependencies = [];
655
+ // The committed gradle/verification-metadata.xml of this build, if any,
656
+ // and the metadata parsed from it exactly once. These live at function
657
+ // scope because the fallback runs inside the Gradle block below while the
658
+ // enrichment runs after getMvnMetadata, outside the try that guards the
659
+ // per-tool sections, so a deferred failure keeps the enrichment too.
660
+ let gradleVerificationFile;
661
+ let gradleVerificationRootDir;
662
+ let gradleVerificationData;
663
+ let gradleVerificationParsed = false;
664
+ const gradleVerification = () => {
665
+ if (!gradleVerificationParsed) {
666
+ gradleVerificationParsed = true;
667
+ if (gradleVerificationFile) {
668
+ gradleVerificationData = parseGradleVerificationMetadata(
669
+ gradleVerificationFile,
670
+ { rootDir: gradleVerificationRootDir },
671
+ );
672
+ if (gradleVerificationData) {
673
+ const { configuration } = gradleVerificationData;
674
+ thoughtLog(
675
+ `${gradleVerificationFile} records ${configuration.trustedKeyCount} trusted key(s), ${configuration.trustedArtifactRuleCount} trust rule(s), ${configuration.ignoredKeyCount} ignored key(s) and ${configuration.keyServerCount} key server(s).`,
676
+ );
677
+ }
678
+ }
679
+ }
680
+ return gradleVerificationData;
681
+ };
568
682
  // cyclone-dx-maven plugin creates a component for the app under metadata
569
683
  // This is subsequently referred to in the dependencies list
570
684
  let parentComponent = {};
@@ -814,6 +928,17 @@ export async function createJavaBom(path, options) {
814
928
  const projectSettings = projectSettingsArgs(basePath, {
815
929
  secureMode: isSecureModeActive(),
816
930
  });
931
+ // When the mirror switch is on, Maven resolves Central from the
932
+ // mirror through a generated global settings file; a project
933
+ // settings.xml is merged into that copy so its repositories and its
934
+ // own mirror survive. A settings file the user named keeps winning.
935
+ const mirrorSettingsArgs = mavenMirrorSettingsArgs(
936
+ projectSettings.settingsFile,
937
+ );
938
+ const mvnSettingsArgs = mirrorSettingsArgs.length
939
+ ? mirrorSettingsArgs
940
+ : projectSettings.args;
941
+ const mvnSpawnEnv = { ...process.env, ...mavenWrapperMirrorEnv() };
817
942
  if (projectSettings.args.length) {
818
943
  console.log(
819
944
  `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.`,
@@ -839,9 +964,10 @@ export async function createJavaBom(path, options) {
839
964
  if (!useMavenDepsTree && !isBuildToolRateLimited("maven")) {
840
965
  thoughtLog("The user wants me to use the cyclonedx-maven plugin.");
841
966
  console.log(`Executing '${mavenCmd}' in`, basePath);
842
- result = safeSpawnSync(mavenCmd, mvnArgs, {
967
+ result = safeSpawnSync(mavenCmd, mvnArgs.concat(mirrorSettingsArgs), {
843
968
  cwd: basePath,
844
969
  shell: isWin,
970
+ env: mvnSpawnEnv,
845
971
  });
846
972
  noteBuildToolRateLimit("maven", commandOutputText(result), {
847
973
  command: `${basename(mavenCmd)} ${mvnArgs.join(" ")}`,
@@ -906,7 +1032,7 @@ export async function createJavaBom(path, options) {
906
1032
  addArgs = parseMavenArgs(readEnvironmentVariable("MVN_ARGS"));
907
1033
  mvnTreeArgs = mvnTreeArgs.concat(addArgs);
908
1034
  }
909
- mvnTreeArgs = mvnTreeArgs.concat(projectSettings.args);
1035
+ mvnTreeArgs = mvnTreeArgs.concat(mvnSettingsArgs);
910
1036
  // For the first pom alone, we need to execute first in non-recursive mode to capture
911
1037
  // the parent component. Then, we execute all of them in recursive mode
912
1038
  // A rate limit the --deep copy-dependencies run met ends it too.
@@ -922,11 +1048,12 @@ export async function createJavaBom(path, options) {
922
1048
  ];
923
1049
  findParentComponentArgs = findParentComponentArgs.concat(
924
1050
  addArgs,
925
- projectSettings.args,
1051
+ mvnSettingsArgs,
926
1052
  );
927
1053
  result = safeSpawnSync(mavenCmd, findParentComponentArgs, {
928
1054
  cwd: basePath,
929
1055
  shell: isWin,
1056
+ env: mvnSpawnEnv,
930
1057
  });
931
1058
  noteBuildToolRateLimit("maven", commandOutputText(result), {
932
1059
  command: `${basename(mavenCmd)} ${findParentComponentArgs.join(" ")}`,
@@ -956,10 +1083,11 @@ export async function createJavaBom(path, options) {
956
1083
  "dependency:tree",
957
1084
  "-N",
958
1085
  `-DoutputFile=${tempMvnParentTreeText}`,
959
- ].concat(addArgs, projectSettings.args);
1086
+ ].concat(addArgs, mvnSettingsArgs);
960
1087
  result = safeSpawnSync(mavenCmd, findParentComponentArgs, {
961
1088
  cwd: basePath,
962
1089
  shell: isWin,
1090
+ env: mvnSpawnEnv,
963
1091
  });
964
1092
  }
965
1093
  if (result.status === 0) {
@@ -1004,6 +1132,7 @@ export async function createJavaBom(path, options) {
1004
1132
  result = safeSpawnSync(mavenCmd, mvnTreeArgs, {
1005
1133
  cwd: basePath,
1006
1134
  shell: isWin,
1135
+ env: mvnSpawnEnv,
1007
1136
  });
1008
1137
  noteBuildToolRateLimit("maven", commandOutputText(result), {
1009
1138
  command: `${basename(mavenCmd)} ${mvnTreeArgs.join(" ")}`,
@@ -1029,10 +1158,11 @@ export async function createJavaBom(path, options) {
1029
1158
  mvnTreeArgs = [
1030
1159
  "dependency:tree",
1031
1160
  `-DoutputFile=${tempMvnTreeText}`,
1032
- ].concat(addArgs, projectSettings.args);
1161
+ ].concat(addArgs, mvnSettingsArgs);
1033
1162
  result = safeSpawnSync(mavenCmd, mvnTreeArgs, {
1034
1163
  cwd: basePath,
1035
1164
  shell: isWin,
1165
+ env: mvnSpawnEnv,
1036
1166
  });
1037
1167
  noteBuildToolRateLimit("maven", commandOutputText(result), {
1038
1168
  command: `${basename(mavenCmd)} ${mvnTreeArgs.join(" ")}`,
@@ -1182,6 +1312,12 @@ export async function createJavaBom(path, options) {
1182
1312
  parentComponent = tmpParentComponent;
1183
1313
  parentComponent.components = [];
1184
1314
  } else {
1315
+ separateDuplicateMavenProject(
1316
+ tmpParentComponent,
1317
+ parentComponent,
1318
+ path,
1319
+ parsedList.dependenciesList,
1320
+ );
1185
1321
  parentComponent.components.push(tmpParentComponent);
1186
1322
  }
1187
1323
  if (parsedList?.dependenciesList?.length) {
@@ -1309,9 +1445,14 @@ export async function createJavaBom(path, options) {
1309
1445
  }
1310
1446
  }
1311
1447
  const allProjects = [];
1312
- const allProjectsAddedPurls = [];
1448
+ // Modules keyed by bom-ref: a module without a purl still has a bom-ref
1449
+ // of its own, while every such module shares an undefined purl.
1450
+ const allProjectsAddedRefs = new Map();
1313
1451
  const rootDependsOn = new Set();
1314
1452
  const gradleModules = new Map();
1453
+ // Included builds that build Gradle plugins, such as a build-logic build
1454
+ // included from pluginManagement. Their classpath is build tooling.
1455
+ const gradlePluginBuilds = new Set();
1315
1456
  // Determine the root path for gradle
1316
1457
  // Fixes gradle invocation for microservices-demo
1317
1458
  let gradleRootPath = path;
@@ -1329,15 +1470,51 @@ export async function createJavaBom(path, options) {
1329
1470
  "Hmm, there is a gradle.properties file. Do we need any private modules or custom JVM arguments for this project 🤔?",
1330
1471
  );
1331
1472
  }
1332
- // Execute gradle properties
1333
- if (
1334
- gradleFiles?.length &&
1335
- isPackageManagerAllowed(
1336
- "gradle",
1337
- ["maven", "bazel", "sbt", "mill"],
1338
- options,
1339
- )
1340
- ) {
1473
+ // The package-manager gate the Gradle invocations below share: a scan
1474
+ // that prefers another JVM build tool over Gradle reads no Gradle
1475
+ // lockfiles either.
1476
+ const gradleManagerAllowed = isPackageManagerAllowed(
1477
+ "gradle",
1478
+ ["maven", "bazel", "sbt", "mill"],
1479
+ options,
1480
+ );
1481
+ // The committed Gradle dependency lockfiles record the resolved versions
1482
+ // of every locked configuration, so they are an inventory source that
1483
+ // needs no Gradle run. They are looked for only in a Gradle build: a
1484
+ // Maven-rooted scan does not pick up a nested sample's lockfile.
1485
+ const gradleLockFiles =
1486
+ gradleFiles?.length && gradleManagerAllowed
1487
+ ? findGradleLockfiles(gradleRootPath, options)
1488
+ : [];
1489
+ // The committed gradle/verification-metadata.xml records the checksums
1490
+ // and PGP key ids Gradle verified when `gradle
1491
+ // --write-verification-metadata` last ran. Like the lockfiles, it is an
1492
+ // inventory source that needs no Gradle run, and it is looked for only in
1493
+ // a Gradle build.
1494
+ gradleVerificationFile =
1495
+ gradleFiles?.length && gradleManagerAllowed
1496
+ ? findGradleVerificationMetadata(gradleRootPath)
1497
+ : undefined;
1498
+ gradleVerificationRootDir = gradleRootPath;
1499
+ // `gradle properties` evaluates the project's build scripts and `gradle
1500
+ // dependencies` resolves the graph, so both belong to the
1501
+ // dependency-tooling surface. The properties task runs whenever
1502
+ // `isProjectToolingAllowed` lets it, as it names the root project and
1503
+ // the subprojects; only the gradle-prefer-lockfile preference holds it
1504
+ // back. The dependencies task also needs `options.installDeps`. When
1505
+ // either check would stop the dependencies task, or when the preference
1506
+ // asks for it, the lockfiles supply the components instead.
1507
+ const gradleToolingAllowed = isProjectToolingAllowed(options);
1508
+ const gradlePreferLockfile = isFeatureEnabled(
1509
+ options,
1510
+ "gradle-prefer-lockfile",
1511
+ );
1512
+ const gradleSkipProperties =
1513
+ gradlePreferLockfile && gradleLockFiles.length > 0;
1514
+ const gradleUseLockfiles =
1515
+ gradleLockFiles.length > 0 &&
1516
+ (gradlePreferLockfile || !gradleToolingAllowed || !options.installDeps);
1517
+ if (gradleFiles?.length && gradleManagerAllowed) {
1341
1518
  const wrapperInfo = readGradleWrapperVersion(gradleRootPath);
1342
1519
  if (wrapperInfo) {
1343
1520
  try {
@@ -1399,6 +1576,177 @@ export async function createJavaBom(path, options) {
1399
1576
  // ignore
1400
1577
  }
1401
1578
  }
1579
+ }
1580
+ // A module of this build that another one consumes by its coordinates is
1581
+ // a project, not a dependency, so the fallback sources below drop it.
1582
+ const dropGradleModuleComponents = (pkgs) => {
1583
+ const moduleRefs = new Set(
1584
+ [parentComponent, ...(parentComponent?.components || [])]
1585
+ .map((c) => c?.["bom-ref"])
1586
+ .filter(Boolean),
1587
+ );
1588
+ return pkgs.filter((pkg) => !moduleRefs.has(pkg["bom-ref"]));
1589
+ };
1590
+ // Without `gradle properties` nothing named the root project. The
1591
+ // settings script names it, and the directory name stands in when it
1592
+ // does not, so the BOM still has a primary component.
1593
+ const ensureGradleParentComponent = () => {
1594
+ if (!parentComponent || !Object.keys(parentComponent).length) {
1595
+ const rootProjectName = readGradleRootProjectName(gradleRootPath);
1596
+ parentComponent = createDefaultParentComponent(
1597
+ gradleRootPath,
1598
+ "maven",
1599
+ rootProjectName && !("project-name" in options)
1600
+ ? {
1601
+ ...options,
1602
+ "project-name": rootProjectName,
1603
+ projectName: rootProjectName,
1604
+ }
1605
+ : options,
1606
+ );
1607
+ }
1608
+ };
1609
+ // The lockfiles stand in for the Gradle dependency resolution this scan
1610
+ // is not making, prefers not to make, or that produced nothing. They
1611
+ // record resolved versions but no graph, so the components carry no
1612
+ // dependency edges and the scan records what that costs. Returns the
1613
+ // number of components added.
1614
+ const addGradleLockfileComponents = () => {
1615
+ const lockfilePkgs = dropGradleModuleComponents(
1616
+ parseGradleLockfiles(gradleLockFiles, {
1617
+ rootDir: gradleRootPath,
1618
+ }),
1619
+ );
1620
+ if (!lockfilePkgs.length) {
1621
+ return 0;
1622
+ }
1623
+ pkgList = pkgList.concat(lockfilePkgs);
1624
+ recordDegradation("jvm.gradle.lockfile-only", {
1625
+ ecosystem: "java",
1626
+ tool: "gradle",
1627
+ impact: "transitive-deps",
1628
+ detail:
1629
+ "The components came from Gradle dependency lockfiles, which record no dependency graph between them.",
1630
+ });
1631
+ ensureGradleParentComponent();
1632
+ return lockfilePkgs.length;
1633
+ };
1634
+ // The version catalog and, on request, the verification file declare
1635
+ // coordinates without resolving them, so both are used only when Gradle
1636
+ // and the lockfiles produced no dependency information at all. The call
1637
+ // inside the Gradle run block happens before its failure paths, so a
1638
+ // build whose static sources resolved it is not degraded twice and not
1639
+ // failed with --fail-on-error over a BOM that has components. The
1640
+ // verification fallback stays off unless the
1641
+ // gradle-verification-fallback feature flag asks for it: the file
1642
+ // records what the build resolved when --write-verification-metadata
1643
+ // ran, which may be stale, and its components can only be optional.
1644
+ // Both belong to a Gradle build, so a scan without Gradle build files,
1645
+ // or one that prefers another JVM build tool, reads neither.
1646
+ const addGradleStaticFallbackComponents = () => {
1647
+ if (!gradleFiles?.length || !gradleManagerAllowed || pkgList.length) {
1648
+ return;
1649
+ }
1650
+ const catalogFiles = getAllFiles(
1651
+ path,
1652
+ `${options.multiProject ? "**/" : ""}gradle/libs.versions.toml`,
1653
+ options,
1654
+ );
1655
+ for (const catalogFile of catalogFiles) {
1656
+ if (DEBUG_MODE) {
1657
+ console.log(`Parsing ${catalogFile}`);
1658
+ }
1659
+ const dlist = parseGradleVersionCatalog(catalogFile);
1660
+ if (dlist?.length) {
1661
+ pkgList = pkgList.concat(dlist);
1662
+ }
1663
+ }
1664
+ if (
1665
+ pkgList.length ||
1666
+ !gradleVerificationFile ||
1667
+ !isFeatureEnabled(options, "gradle-verification-fallback")
1668
+ ) {
1669
+ return;
1670
+ }
1671
+ const verificationMetadata = gradleVerification();
1672
+ if (!verificationMetadata) {
1673
+ return;
1674
+ }
1675
+ const verificationPkgs = [];
1676
+ for (const component of verificationMetadata.components.values()) {
1677
+ // A Gradle plugin marker coordinate publishes only a POM that
1678
+ // points at the real plugin, so it is not a dependency of the
1679
+ // build, and a coordinate without a main artifact is a parent or a
1680
+ // BOM rather than a component of this BOM.
1681
+ if (component.name.endsWith(".gradle.plugin")) {
1682
+ continue;
1683
+ }
1684
+ if (!gradleVerificationMainArtifact(component, false)) {
1685
+ continue;
1686
+ }
1687
+ const purl = mavenPurl(
1688
+ component.group,
1689
+ component.name,
1690
+ component.version,
1691
+ {
1692
+ type: "jar",
1693
+ },
1694
+ );
1695
+ if (!purl) {
1696
+ continue;
1697
+ }
1698
+ verificationPkgs.push({
1699
+ group: component.group,
1700
+ name: component.name,
1701
+ version: component.version,
1702
+ qualifiers: { type: "jar" },
1703
+ type: "library",
1704
+ // A component with no scope counts as required, and the file
1705
+ // cannot tell a runtime dependency from build tooling, so the
1706
+ // components are optional.
1707
+ scope: "optional",
1708
+ purl,
1709
+ "bom-ref": decodeURIComponent(purl),
1710
+ // The digests are merged in at the end of the scan, from the file
1711
+ // or from the artifact itself when the local cache holds it.
1712
+ properties: [
1713
+ { name: "internal:SrcFile", value: gradleVerificationFile },
1714
+ { name: "cdx:gradle:verificationMetadata", value: "true" },
1715
+ ],
1716
+ evidence: {
1717
+ identity: {
1718
+ field: "purl",
1719
+ confidence: 1,
1720
+ concludedValue: purl,
1721
+ methods: [
1722
+ {
1723
+ technique: "manifest-analysis",
1724
+ confidence: 1,
1725
+ value: gradleVerificationFile,
1726
+ },
1727
+ ],
1728
+ },
1729
+ },
1730
+ });
1731
+ }
1732
+ const addedPkgs = dropGradleModuleComponents(verificationPkgs);
1733
+ if (addedPkgs.length) {
1734
+ pkgList = pkgList.concat(addedPkgs);
1735
+ recordDegradation("jvm.gradle.verification-metadata-only", {
1736
+ ecosystem: "java",
1737
+ tool: "gradle",
1738
+ impact: "transitive-deps",
1739
+ detail:
1740
+ "The components came from gradle/verification-metadata.xml, which records no dependency graph between them.",
1741
+ });
1742
+ ensureGradleParentComponent();
1743
+ thoughtLog(
1744
+ `Obtained ${addedPkgs.length} components from ${gradleVerificationFile}. It says nothing about which module depends on which, and nothing about scopes.`,
1745
+ );
1746
+ }
1747
+ };
1748
+ // Execute gradle properties
1749
+ if (gradleFiles?.length && gradleManagerAllowed && !gradleSkipProperties) {
1402
1750
  let includedBuilds = [];
1403
1751
  let allProjectsStr = [];
1404
1752
  if (readEnvironmentVariable("GRADLE_INCLUDED_BUILDS")) {
@@ -1413,13 +1761,15 @@ export async function createJavaBom(path, options) {
1413
1761
  const gradleInitArgs = readEnvironmentVariable("GRADLE_INCLUDED_BUILDS")
1414
1762
  ? []
1415
1763
  : ["--init-script", GRADLE_INIT_SCRIPT];
1764
+ // The mirror init script joins cdxgen's own helper script; Gradle
1765
+ // applies every init script it is given.
1766
+ const gradleMirrorArgs = gradleMirrorInitScriptArgs();
1767
+ if (gradleMirrorArgs.length) {
1768
+ gradleInitArgs.push(...gradleMirrorArgs);
1769
+ }
1416
1770
  if (resolveGradleDistribution && gradleInitArgs.length) {
1417
1771
  gradleInitArgs.push("-PcdxgenResolveDistribution=true");
1418
1772
  }
1419
- // `gradle properties` evaluates the project's build scripts, so it is
1420
- // part of the dependency-tooling surface gated by --no-install-deps
1421
- // (and the pre-build lifecycle).
1422
- const gradleToolingAllowed = isProjectToolingAllowed(options);
1423
1773
  if (gradleToolingAllowed) {
1424
1774
  parallelPropTaskOut = executeParallelGradleProperties(
1425
1775
  gradleRootPath,
@@ -1484,12 +1834,19 @@ export async function createJavaBom(path, options) {
1484
1834
  );
1485
1835
  if (!includedBuilds.includes(key)) {
1486
1836
  parentComponent = rootComponent;
1837
+ } else if (retMap.gradlePlugin) {
1838
+ gradlePluginBuilds.add(key);
1487
1839
  }
1488
1840
  gradleModules.set(key, rootComponent);
1489
- if (!allProjectsAddedPurls.includes(rootComponent["purl"])) {
1841
+ if (
1842
+ !allProjectsAddedRefs.has(rootComponent["bom-ref"] ?? rootComponent)
1843
+ ) {
1490
1844
  allProjects.push(rootComponent);
1491
1845
  rootDependsOn.add(rootComponent["bom-ref"]);
1492
- allProjectsAddedPurls.push(rootComponent["purl"]);
1846
+ allProjectsAddedRefs.set(
1847
+ rootComponent["bom-ref"] ?? rootComponent,
1848
+ rootComponent,
1849
+ );
1493
1850
  }
1494
1851
  allProjectsStr = allProjectsStr.concat(retMap.projects);
1495
1852
  }
@@ -1522,14 +1879,60 @@ export async function createJavaBom(path, options) {
1522
1879
  );
1523
1880
  const rootSubProject = retMap.rootProject;
1524
1881
  if (rootSubProject) {
1882
+ // A subproject whose properties task printed nothing is named as
1883
+ // Gradle publishes it by default: the last segment of its path,
1884
+ // with the root project's group and version.
1885
+ if (!splitPropTaskOut.has(subProject)) {
1886
+ retMap.metadata.group ||= parentComponent?.group || "";
1887
+ if (
1888
+ retMap.metadata.version === "latest" &&
1889
+ parentComponent?.version
1890
+ ) {
1891
+ retMap.metadata.version = parentComponent.version;
1892
+ }
1893
+ }
1525
1894
  const rootSubProjectObj = await buildObjectForGradleModule(
1526
- rootSubProject === "root" ? subProject : rootSubProject,
1895
+ rootSubProject === "root"
1896
+ ? subProject.split(":").pop()
1897
+ : rootSubProject,
1527
1898
  retMap.metadata,
1528
1899
  );
1529
- if (!allProjectsAddedPurls.includes(rootSubProjectObj["purl"])) {
1900
+ // Two subprojects that Gradle would publish under the same
1901
+ // coordinates, such as `:a:util` and `:b:util` with a shared group
1902
+ // and version, stay apart: the later one gets its project path as
1903
+ // the purl subpath. A module that shares the root project's
1904
+ // coordinates is the root's own artifact and merges with it.
1905
+ const holder = allProjectsAddedRefs.get(
1906
+ rootSubProjectObj["bom-ref"],
1907
+ );
1908
+ if (
1909
+ holder &&
1910
+ holder !== parentComponent &&
1911
+ holder !== gradleModules.get(subProject)
1912
+ ) {
1913
+ const subpath = subProject.split(":").filter(Boolean).join("/");
1914
+ const distinctPurl = rootSubProjectObj.purl
1915
+ ? tryBuildPurl({
1916
+ type: "maven",
1917
+ namespace: rootSubProjectObj.group || null,
1918
+ name: rootSubProjectObj.name,
1919
+ version: rootSubProjectObj.version || null,
1920
+ qualifiers: { type: "jar" },
1921
+ subpath,
1922
+ })
1923
+ : null;
1924
+ if (distinctPurl) {
1925
+ applyPurl(rootSubProjectObj, distinctPurl);
1926
+ } else {
1927
+ rootSubProjectObj["bom-ref"] =
1928
+ `${rootSubProjectObj["bom-ref"]}#${subpath}`;
1929
+ }
1930
+ }
1931
+ const moduleKey = rootSubProjectObj["bom-ref"] ?? rootSubProjectObj;
1932
+ if (!allProjectsAddedRefs.has(moduleKey)) {
1530
1933
  allProjects.push(rootSubProjectObj);
1531
1934
  rootDependsOn.add(rootSubProjectObj["bom-ref"]);
1532
- allProjectsAddedPurls.push(rootSubProjectObj["purl"]);
1935
+ allProjectsAddedRefs.set(moduleKey, rootSubProjectObj);
1533
1936
  }
1534
1937
  gradleModules.set(subProject, rootSubProjectObj);
1535
1938
  }
@@ -1546,14 +1949,39 @@ export async function createJavaBom(path, options) {
1546
1949
  });
1547
1950
  }
1548
1951
  }
1952
+ if (gradleUseLockfiles) {
1953
+ const lockfileCount = addGradleLockfileComponents();
1954
+ if (lockfileCount) {
1955
+ console.log(
1956
+ `Using the dependencies locked by ${gradleLockFiles.length} Gradle lockfile(s) instead of executing 'gradle dependencies'.`,
1957
+ );
1958
+ thoughtLog(
1959
+ `Obtained ${lockfileCount} components from ${gradleLockFiles.length} Gradle lockfile(s). A lockfile is flat, so it says nothing about which module depends on which.`,
1960
+ );
1961
+ // --deep reads the jar namespaces of the versions the local Gradle
1962
+ // cache holds, as it does after a Gradle run; a cold cache yields
1963
+ // nothing.
1964
+ if (options.resolveClass || options.deep) {
1965
+ const tmpjarNSMapping = await collectJarNS(GRADLE_CACHE_DIR);
1966
+ if (tmpjarNSMapping && Object.keys(tmpjarNSMapping).length) {
1967
+ jarNSMapping = { ...jarNSMapping, ...tmpjarNSMapping };
1968
+ }
1969
+ }
1970
+ } else {
1971
+ thoughtLog(
1972
+ "The Gradle lockfiles yielded no components, so the remaining static sources are tried.",
1973
+ );
1974
+ }
1975
+ }
1976
+ // Components that came from somewhere other than this Gradle run, such
1977
+ // as a Maven build in the same root, so the decisions below look only at
1978
+ // what Gradle itself produced.
1979
+ const pkgCountBeforeGradleRun = pkgList.length;
1549
1980
  if (
1550
1981
  gradleFiles?.length &&
1551
1982
  options.installDeps &&
1552
- isPackageManagerAllowed(
1553
- "gradle",
1554
- ["maven", "bazel", "sbt", "mill"],
1555
- options,
1556
- )
1983
+ gradleManagerAllowed &&
1984
+ !gradleUseLockfiles
1557
1985
  ) {
1558
1986
  allProjects.push(parentComponent);
1559
1987
  const gradleCmd = getGradleCommand(gradleRootPath, null, options);
@@ -1561,7 +1989,6 @@ export async function createJavaBom(path, options) {
1561
1989
  ? readEnvironmentVariable("GRADLE_DEPENDENCY_TASK")
1562
1990
  : "dependencies";
1563
1991
 
1564
- const gradleSubCommands = [];
1565
1992
  let modulesToSkip = readEnvironmentVariable("GRADLE_SKIP_MODULES")
1566
1993
  ? readEnvironmentVariable("GRADLE_SKIP_MODULES").split(",")
1567
1994
  : [];
@@ -1570,23 +1997,16 @@ export async function createJavaBom(path, options) {
1570
1997
  readEnvironmentVariable("GRADLE_SKIP_MODULE_DEPENDENCIES").split(","),
1571
1998
  );
1572
1999
  }
1573
- if (!modulesToSkip.includes("root")) {
1574
- gradleSubCommands.push(gradleDepTask);
1575
- }
1576
- for (const [key, sp] of gradleModules) {
1577
- //create single command for dependencies tasks on all subprojects
1578
- if (sp.purl !== parentComponent.purl && !modulesToSkip.includes(key)) {
1579
- gradleSubCommands.push(`${key}:${gradleDepTask}`);
1580
- }
1581
- }
2000
+ const gradleSubCommands = gradleDependencyTasks(
2001
+ gradleModules,
2002
+ parentComponent,
2003
+ gradleDepTask,
2004
+ modulesToSkip,
2005
+ );
1582
2006
  const gradleArguments = buildGradleCommandArguments(
1583
- readEnvironmentVariable("GRADLE_ARGS")
1584
- ? readEnvironmentVariable("GRADLE_ARGS").split(" ")
1585
- : [],
2007
+ gradleArgsFromEnv("GRADLE_ARGS"),
1586
2008
  gradleSubCommands,
1587
- readEnvironmentVariable("GRADLE_ARGS_DEPENDENCIES")
1588
- ? readEnvironmentVariable("GRADLE_ARGS_DEPENDENCIES").split(" ")
1589
- : [],
2009
+ gradleArgsFromEnv("GRADLE_ARGS_DEPENDENCIES"),
1590
2010
  gradleCmd.length,
1591
2011
  );
1592
2012
  const allOutputs = [];
@@ -1647,14 +2067,30 @@ export async function createJavaBom(path, options) {
1647
2067
  const perProjectOutput = splitOutputByGradleProjects(cmdOutput, [
1648
2068
  gradleDepTask,
1649
2069
  ]);
2070
+ await loadGradleModulesForReports(
2071
+ perProjectOutput.values(),
2072
+ gradleModules,
2073
+ gradleRootPath,
2074
+ options,
2075
+ );
2076
+ const reportedProjects = new Set(perProjectOutput.keys());
1650
2077
  for (const key of gradleModules.keys()) {
1651
2078
  const parsedList = await parseGradleDep(
1652
2079
  perProjectOutput.has(key) ? perProjectOutput.get(key) : "",
1653
2080
  key,
1654
2081
  gradleModules,
1655
2082
  gradleRootPath,
2083
+ reportedProjects,
1656
2084
  );
1657
2085
  const dlist = parsedList.pkgList;
2086
+ if (
2087
+ dlist?.length &&
2088
+ [...gradlePluginBuilds].some(
2089
+ (build) => key === build || key.startsWith(`${build}:`),
2090
+ )
2091
+ ) {
2092
+ excludeGradleBuildTooling(dlist, gradleModules);
2093
+ }
1658
2094
  if (parsedList?.dependenciesList?.length) {
1659
2095
  dependencies = mergeDependencies(
1660
2096
  dependencies,
@@ -1672,7 +2108,25 @@ export async function createJavaBom(path, options) {
1672
2108
  pkgList = pkgList.concat(dlist);
1673
2109
  }
1674
2110
  }
2111
+ reconcileGradleComponents(pkgList);
1675
2112
  }
2113
+ const gradleRunPkgCount = pkgList.length - pkgCountBeforeGradleRun;
2114
+ if (!gradleRunPkgCount && gradleLockFiles.length) {
2115
+ // Gradle produced no components of its own. The lockfiles are the
2116
+ // next source before the failure paths below: Gradle may have run
2117
+ // fine over configurations the project never locked, or failed
2118
+ // outright while the committed lockfiles still describe the build.
2119
+ const lockfileCount = addGradleLockfileComponents();
2120
+ if (lockfileCount) {
2121
+ thoughtLog(
2122
+ `Gradle produced no components, so the ${lockfileCount} components of ${gradleLockFiles.length} Gradle lockfile(s) were used instead.`,
2123
+ );
2124
+ }
2125
+ }
2126
+ // A catalog or verification fallback can supply these components when
2127
+ // Gradle produced none, so they pass through the same distribution and
2128
+ // subproject handling below.
2129
+ addGradleStaticFallbackComponents();
1676
2130
  if (pkgList.length) {
1677
2131
  const fileToUrlMap = parseGradleInfoLogsForUrls(sstdout);
1678
2132
  // When the `resolve-gradle-distribution` feature flag is enabled, the init script
@@ -1685,9 +2139,15 @@ export async function createJavaBom(path, options) {
1685
2139
  if (!p.externalReferences) {
1686
2140
  p.externalReferences = [];
1687
2141
  }
1688
- if (!p.externalReferences.find((r) => r.type === "distribution")) {
2142
+ // The repository URLs name jar files, which a platform does not
2143
+ // have. The leading slash keeps group `foo` from matching a URL
2144
+ // under `org/foo/`.
2145
+ if (
2146
+ p.qualifiers?.type !== "pom" &&
2147
+ !p.externalReferences.find((r) => r.type === "distribution")
2148
+ ) {
1689
2149
  const groupPath = p.group.replace(/\./g, "/");
1690
- const matchPath = `${groupPath}/${p.name}/${p.version}/`;
2150
+ const matchPath = `/${groupPath}/${p.name}/${p.version}/`;
1691
2151
  let matchedUrl = Object.values(fileToUrlMap).find((url) =>
1692
2152
  url.includes(matchPath),
1693
2153
  );
@@ -1713,11 +2173,21 @@ export async function createJavaBom(path, options) {
1713
2173
  }
1714
2174
  }
1715
2175
  thoughtLog(
1716
- `Obtained ${pkgList.length} components by executing the '${basename(gradleCmd)}' command.`,
2176
+ gradleRunPkgCount
2177
+ ? `Obtained ${pkgList.length} components by executing the '${basename(gradleCmd)}' command.`
2178
+ : `Obtained ${pkgList.length} components from the static Gradle sources after '${basename(gradleCmd)}' produced none.`,
1717
2179
  );
1718
2180
  if (DEBUG_MODE) {
1719
2181
  console.log("Obtained", pkgList.length, "from this gradle project.");
1720
2182
  }
2183
+ if (gradleRunPkgCount && gradleLockFiles.length) {
2184
+ // Gradle resolved against the lockfiles itself, so the versions it
2185
+ // reports already are the locked ones and parsing the files again
2186
+ // would add nothing.
2187
+ thoughtLog(
2188
+ `Gradle enforced the ${gradleLockFiles.length} Gradle lockfile(s) of this build, so they are not parsed separately.`,
2189
+ );
2190
+ }
1721
2191
  } else if (hasGradleInvocationFailure()) {
1722
2192
  // The invocation failure is already ranked, and an empty
1723
2193
  // configuration is what that failure looks like from here: reporting
@@ -1783,37 +2253,25 @@ export async function createJavaBom(path, options) {
1783
2253
  jarNSMapping = { ...jarNSMapping, ...tmpjarNSMapping };
1784
2254
  }
1785
2255
  }
1786
-
1787
- // A version catalog declares coordinates without resolving them, so it
1788
- // is used only when Gradle produced no dependency information at all.
1789
- // The emitted components carry cdx:gradle:catalog=true to mark them as
1790
- // declared rather than resolved.
1791
- if (!pkgList.length) {
1792
- const catalogFiles = getAllFiles(
1793
- path,
1794
- `${options.multiProject ? "**/" : ""}gradle/libs.versions.toml`,
1795
- options,
1796
- );
1797
- for (const catalogFile of catalogFiles) {
1798
- if (DEBUG_MODE) {
1799
- console.log(`Parsing ${catalogFile}`);
1800
- }
1801
- const dlist = parseGradleVersionCatalog(catalogFile);
1802
- if (dlist?.length) {
1803
- pkgList = pkgList.concat(dlist);
1804
- }
1805
- }
1806
- }
1807
2256
  }
1808
2257
 
2258
+ // Outside the installDeps block on purpose: a scan with dependency
2259
+ // installation disabled reaches the static sources through the lockfile
2260
+ // path instead of the Gradle run, and a scan whose Gradle run was
2261
+ // skipped entirely needs them here.
2262
+ addGradleStaticFallbackComponents();
2263
+
1809
2264
  // Bazel
1810
2265
  // Look for the BUILD file only in the root directory
1811
2266
  // NOTE: This can match BUILD files used by perl, so could lead to errors in some projects
2267
+ // A workspace under third_party/ or vendor/ is a copied project's: its
2268
+ // bazel_dep entries are that project's build tooling, and building it
2269
+ // would run its build.
1812
2270
  const bazelFiles = getAllFiles(
1813
2271
  path,
1814
2272
  `${options.multiProject ? "**/" : ""}{WORKSPACE{,.bazel},MODULE.bazel}`,
1815
2273
  options,
1816
- );
2274
+ ).filter((f) => !isInVendoredDir(path, f));
1817
2275
  if (
1818
2276
  bazelFiles?.length &&
1819
2277
  !hasAnyProjectType(
@@ -1886,6 +2344,9 @@ export async function createJavaBom(path, options) {
1886
2344
  ) {
1887
2345
  bArgs = ["--bazelrc=.bazelrc", "build", bazelTarget];
1888
2346
  }
2347
+ // Repository rules see the mirror through their own environment,
2348
+ // which only a --repo_env flag reaches.
2349
+ bArgs = bArgs.concat(bazelMirrorArgs());
1889
2350
  console.log("Executing", BAZEL_CMD, "in", basePath);
1890
2351
  let result = safeSpawnSync(BAZEL_CMD, bArgs, {
1891
2352
  cwd: basePath,
@@ -2099,7 +2560,7 @@ export async function createJavaBom(path, options) {
2099
2560
  // sbt resolves into the user's own caches, so a project that has been
2100
2561
  // built resolves without the network. The dependency trees download
2101
2562
  // the jar of every dependency they list, which is all --deep reads.
2102
- const env = { ...process.env };
2563
+ const env = { ...process.env, ...sbtMirrorEnv() };
2103
2564
  for (const i in sbtProjects) {
2104
2565
  if (isBuildToolRateLimited("sbt")) {
2105
2566
  // Every remaining sbt build in this scan resolves from the same
@@ -2450,19 +2911,13 @@ export async function createJavaBom(path, options) {
2450
2911
  const jarNSData = await createJarBom(path, options);
2451
2912
  if (jarNSData?.bomJson?.components) {
2452
2913
  pkgList = pkgList.concat(jarNSData?.bomJson?.components);
2453
- const targetJarNSMapping = {};
2454
- for (const p of jarNSData.bomJson.components) {
2455
- if (!p?.purl || !p?.properties?.length) {
2456
- continue;
2457
- }
2458
- const nsProp = p.properties.filter(
2459
- (prop) => prop.name === "internal:Namespaces",
2460
- );
2461
- if (nsProp.length) {
2462
- targetJarNSMapping[p.purl] = nsProp[0].value;
2463
- }
2464
- }
2465
- jarNSMapping = { ...jarNSMapping, ...targetJarNSMapping };
2914
+ jarNSMapping = {
2915
+ ...jarNSMapping,
2916
+ ...namespaceMappingOfComponents(
2917
+ jarNSData.bomJson.components,
2918
+ jarNSMapping,
2919
+ ),
2920
+ };
2466
2921
  }
2467
2922
  }
2468
2923
  }
@@ -2504,10 +2959,12 @@ export async function createJavaBom(path, options) {
2504
2959
  if (DEBUG_MODE) {
2505
2960
  console.log("Executing", millCmd, "in", millRootPath);
2506
2961
  }
2962
+ const millSpawnEnv = { ...process.env, ...coursierMirrorEnv() };
2507
2963
  let sresult = safeSpawnSync(millCmd, millArgs, {
2508
2964
  cwd: millRootPath,
2509
2965
  shell: isWin,
2510
2966
  maxBuffer: MAX_BUFFER * 10,
2967
+ env: millSpawnEnv,
2511
2968
  });
2512
2969
  noteBuildToolRateLimit("mill", commandOutputText(sresult), {
2513
2970
  command: `${basename(millCmd)} ${millArgs.join(" ")}`,
@@ -2546,6 +3003,7 @@ export async function createJavaBom(path, options) {
2546
3003
  sresult = safeSpawnSync(millCmd, millResolveArgs, {
2547
3004
  cwd: millRootPath,
2548
3005
  shell: isWin,
3006
+ env: millSpawnEnv,
2549
3007
  });
2550
3008
  if (sresult.status !== 0 || sresult.error) {
2551
3009
  if (options.failOnError || DEBUG_MODE) {
@@ -2710,12 +3168,70 @@ export async function createJavaBom(path, options) {
2710
3168
  // Deduplication and metadata belong to whatever was collected, so they run
2711
3169
  // on the deferred path too.
2712
3170
  pkgList = trimComponents(pkgList);
3171
+ // A library the build tool reported and a jar on disk named again is one
3172
+ // component, wherever the jar scan spelled it differently. The jar
3173
+ // namespace map follows the purls the merge settled on.
3174
+ const scalaMerge = mergeScalaDuplicateComponents(pkgList, dependencies);
3175
+ pkgList = scalaMerge.components;
3176
+ dependencies = scalaMerge.dependencies;
3177
+ jarNSMapping = rekeyNamespaceMapping(jarNSMapping, scalaMerge.purlMap);
2713
3178
  // --deep collects namespaces; it does not opt in to remote licence lookups,
2714
3179
  // which FETCH_LICENSE controls. The project's own modules are never looked
2715
3180
  // up remotely: they are not published.
2716
3181
  pkgList = await getMvnMetadata(pkgList, jarNSMapping, false, {
2717
3182
  skipPurls: ownComponentRefs(parentComponent),
2718
3183
  });
3184
+ // The verification file is the last enrichment, after getMvnMetadata has
3185
+ // hashed the jars of the local cache: a digest of the artifact itself is
3186
+ // the stronger claim, so the recorded digests only fill the algorithms a
3187
+ // component still lacks, and a differing digest of the same algorithm is
3188
+ // flagged, never applied. Whatever source found the component keeps the
3189
+ // identity evidence of that source.
3190
+ if (gradleVerificationFile) {
3191
+ const verificationMetadata = gradleVerification();
3192
+ if (verificationMetadata) {
3193
+ const enrichedCount = applyGradleVerificationMetadata(
3194
+ pkgList,
3195
+ verificationMetadata,
3196
+ gradleVerificationFile,
3197
+ );
3198
+ if (enrichedCount) {
3199
+ thoughtLog(
3200
+ `Enriched ${enrichedCount} component(s) with the checksums that ${gradleVerificationFile} records.`,
3201
+ );
3202
+ }
3203
+ // Whether Gradle verified metadata and signatures is a fact about the
3204
+ // whole build, so it lands on the primary component. Only the flags
3205
+ // the file actually writes are emitted.
3206
+ const { configuration } = verificationMetadata;
3207
+ if (parentComponent && Object.keys(parentComponent).length) {
3208
+ const parentProperties = parentComponent.properties || [];
3209
+ if (
3210
+ configuration.verifyMetadata !== undefined &&
3211
+ !parentProperties.some((p) => p.name === "cdx:gradle:verifyMetadata")
3212
+ ) {
3213
+ parentProperties.push({
3214
+ name: "cdx:gradle:verifyMetadata",
3215
+ value: String(configuration.verifyMetadata),
3216
+ });
3217
+ }
3218
+ if (
3219
+ configuration.verifySignatures !== undefined &&
3220
+ !parentProperties.some(
3221
+ (p) => p.name === "cdx:gradle:verifySignatures",
3222
+ )
3223
+ ) {
3224
+ parentProperties.push({
3225
+ name: "cdx:gradle:verifySignatures",
3226
+ value: String(configuration.verifySignatures),
3227
+ });
3228
+ }
3229
+ if (parentProperties.length) {
3230
+ parentComponent.properties = parentProperties;
3231
+ }
3232
+ }
3233
+ }
3234
+ }
2719
3235
  return buildBomNSData(options, pkgList, "maven", {
2720
3236
  src: path,
2721
3237
  nsMapping: jarNSMapping,