@cdxgen/cdxgen 13.5.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 (182) hide show
  1. package/README.md +6 -6
  2. package/bin/cdxgen.js +4 -0
  3. package/bin/evinse.js +3 -0
  4. package/bin/validate.js +6 -0
  5. package/data/license-aliases.json +24 -2
  6. package/data/remediations.json +124 -0
  7. package/data/rules/ai-bom.yaml +1 -1
  8. package/data/rules/brotli-decoder.yaml +107 -0
  9. package/data/rules/vscode-extensions.yaml +75 -0
  10. package/data/spdx-license-list.json +121 -7
  11. package/data/spdx-licenses.json +69 -0
  12. package/data/spdx.schema.json +845 -2
  13. package/lib/cli/bomAssembly.js +92 -3
  14. package/lib/cli/index.js +108 -1
  15. package/lib/cli/jsBom.js +153 -64
  16. package/lib/cli/jvmBom.js +237 -46
  17. package/lib/cli/managedBom.js +142 -16
  18. package/lib/cli/nativeBom.js +42 -5
  19. package/lib/core/boundedFile.js +344 -0
  20. package/lib/core/brotliProbe.js +182 -0
  21. package/lib/core/env.js +8 -0
  22. package/lib/core/fs.js +12 -0
  23. package/lib/core/httpClient.js +98 -1
  24. package/lib/core/lockPaths.js +38 -0
  25. package/lib/core/paths.js +24 -1
  26. package/lib/core/processTree.js +5 -0
  27. package/lib/core/toolRegistry.js +170 -0
  28. package/lib/ecosystems/cppEvidence.js +263 -106
  29. package/lib/ecosystems/cppIncludeResolver.js +16 -5
  30. package/lib/ecosystems/ecosystems.js +25 -10
  31. package/lib/ecosystems/gemHostProbe.js +301 -0
  32. package/lib/ecosystems/gradleutils.js +828 -224
  33. package/lib/ecosystems/parsers-bazel.js +107 -6
  34. package/lib/ecosystems/parsers-js.js +505 -22
  35. package/lib/ecosystems/parsers-misc.js +209 -20
  36. package/lib/ecosystems/parsers-python.js +89 -2
  37. package/lib/ecosystems/pythonutils.js +128 -2
  38. package/lib/ecosystems/rubyutils.js +538 -31
  39. package/lib/ecosystems/scalacliutils.js +2 -0
  40. package/lib/ecosystems/scalasem.js +796 -151
  41. package/lib/ecosystems/vendoredCode.js +237 -3
  42. package/lib/ecosystems/venvProbe.js +360 -0
  43. package/lib/evinser/evinser.js +346 -43
  44. package/lib/helpers/agenticToolutils.js +413 -93
  45. package/lib/helpers/vsixutils.js +1574 -315
  46. package/lib/inventory/atomUtils.js +77 -0
  47. package/lib/inventory/brotliDecoder.js +142 -0
  48. package/lib/inventory/buildToolRateLimit.js +1 -1
  49. package/lib/inventory/cbomutils.js +56 -7
  50. package/lib/inventory/deps.js +35 -0
  51. package/lib/inventory/dosai.js +139 -5
  52. package/lib/inventory/dotnetRuntime.js +217 -0
  53. package/lib/inventory/envcontext.js +307 -13
  54. package/lib/inventory/evidenceUtils.js +11 -0
  55. package/lib/inventory/fetchBatch.js +1 -1
  56. package/lib/inventory/formulationParsers.js +31 -3
  57. package/lib/inventory/gtfobins.js +13 -0
  58. package/lib/inventory/hostRuntimeProbe.js +686 -0
  59. package/lib/inventory/jvmLocalRepos.js +9 -2
  60. package/lib/inventory/jvmToolEnv.js +29 -0
  61. package/lib/inventory/licenseEnhancer.js +12 -0
  62. package/lib/inventory/mavenCentralMirror.js +534 -0
  63. package/lib/inventory/nodeBuildInfo.js +218 -0
  64. package/lib/inventory/nodeHostProbe.js +202 -0
  65. package/lib/inventory/osPackageResolver.js +182 -0
  66. package/lib/inventory/phpBuildInfo.js +10 -59
  67. package/lib/inventory/phpHostProbe.js +32 -488
  68. package/lib/inventory/rubyBuildInfo.js +972 -0
  69. package/lib/inventory/rubyHostProbe.js +218 -0
  70. package/lib/inventory/scalaCoords.js +4 -3
  71. package/lib/inventory/scalaDedupe.js +440 -0
  72. package/lib/inventory/source.js +49 -2
  73. package/lib/managers/binary.js +337 -3
  74. package/lib/managers/docker.js +79 -0
  75. package/lib/parsers/largeJson.js +28 -11
  76. package/lib/parsers/vscodeExtensionId.js +183 -0
  77. package/lib/server/server.js +12 -1
  78. package/lib/stages/postgen/ruleEngine.js +13 -5
  79. package/lib/stages/pregen/envAudit.js +1 -0
  80. package/lib/stages/pregen/pregen.js +114 -57
  81. package/lib/validator/bomValidator.js +6 -1
  82. package/lib/validator/generated/validate-1.6.mjs +1 -1
  83. package/lib/validator/generated/validate-1.7.mjs +1 -1
  84. package/lib/validator/generated/validate-2.0.mjs +1 -1
  85. package/lib/validator/index.js +1 -1
  86. package/package.json +3 -2
  87. package/types/lib/cli/bomAssembly.d.ts +13 -1
  88. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  89. package/types/lib/cli/index.d.ts.map +1 -1
  90. package/types/lib/cli/jsBom.d.ts +1 -17
  91. package/types/lib/cli/jsBom.d.ts.map +1 -1
  92. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  93. package/types/lib/cli/managedBom.d.ts.map +1 -1
  94. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  95. package/types/lib/core/boundedFile.d.ts +70 -0
  96. package/types/lib/core/boundedFile.d.ts.map +1 -0
  97. package/types/lib/core/brotliProbe.d.ts +37 -0
  98. package/types/lib/core/brotliProbe.d.ts.map +1 -0
  99. package/types/lib/core/env.d.ts +4 -0
  100. package/types/lib/core/env.d.ts.map +1 -1
  101. package/types/lib/core/fs.d.ts.map +1 -1
  102. package/types/lib/core/httpClient.d.ts +24 -0
  103. package/types/lib/core/httpClient.d.ts.map +1 -1
  104. package/types/lib/core/lockPaths.d.ts +19 -0
  105. package/types/lib/core/lockPaths.d.ts.map +1 -0
  106. package/types/lib/core/processTree.d.ts +2 -0
  107. package/types/lib/core/processTree.d.ts.map +1 -1
  108. package/types/lib/core/toolRegistry.d.ts +36 -0
  109. package/types/lib/core/toolRegistry.d.ts.map +1 -0
  110. package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
  111. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  112. package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
  113. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
  114. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  115. package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
  116. package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
  117. package/types/lib/ecosystems/gradleutils.d.ts +47 -13
  118. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  119. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
  120. package/types/lib/ecosystems/parsers-js.d.ts +17 -20
  121. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  122. package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
  123. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  124. package/types/lib/ecosystems/parsers-python.d.ts +10 -0
  125. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  126. package/types/lib/ecosystems/pythonutils.d.ts +20 -2
  127. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  128. package/types/lib/ecosystems/rubyutils.d.ts +144 -12
  129. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  130. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
  131. package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
  132. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
  133. package/types/lib/ecosystems/venvProbe.d.ts +40 -0
  134. package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
  135. package/types/lib/evinser/evinser.d.ts +63 -8
  136. package/types/lib/evinser/evinser.d.ts.map +1 -1
  137. package/types/lib/inventory/atomUtils.d.ts +19 -0
  138. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  139. package/types/lib/inventory/brotliDecoder.d.ts +58 -0
  140. package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
  141. package/types/lib/inventory/cbomutils.d.ts +17 -0
  142. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  143. package/types/lib/inventory/deps.d.ts.map +1 -1
  144. package/types/lib/inventory/dosai.d.ts +17 -1
  145. package/types/lib/inventory/dosai.d.ts.map +1 -1
  146. package/types/lib/inventory/dotnetRuntime.d.ts +44 -0
  147. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
  148. package/types/lib/inventory/envcontext.d.ts +52 -6
  149. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  150. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  151. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  152. package/types/lib/inventory/gtfobins.d.ts.map +1 -1
  153. package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
  154. package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
  155. package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
  156. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  157. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
  158. package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
  159. package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
  160. package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
  161. package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
  162. package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
  163. package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
  164. package/types/lib/inventory/osPackageResolver.d.ts +38 -0
  165. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  166. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
  167. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
  168. package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
  169. package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
  170. package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
  171. package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
  172. package/types/lib/inventory/source.d.ts +10 -0
  173. package/types/lib/inventory/source.d.ts.map +1 -1
  174. package/types/lib/managers/binary.d.ts.map +1 -1
  175. package/types/lib/managers/docker.d.ts +12 -0
  176. package/types/lib/managers/docker.d.ts.map +1 -1
  177. package/types/lib/parsers/largeJson.d.ts +4 -3
  178. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  179. package/types/lib/server/server.d.ts.map +1 -1
  180. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  181. package/types/lib/stages/pregen/pregen.d.ts +12 -8
  182. package/types/lib/stages/pregen/pregen.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";
@@ -44,19 +44,24 @@ 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,
54
57
  isProjectToolingAllowed,
58
+ loadGradleModulesForReports,
55
59
  parseGradleDep,
56
60
  parseGradleInfoLogsForUrls,
57
61
  parseGradleProperties,
58
62
  parseGradleResolvedDistributions,
59
63
  parseGradleVersionCatalog,
64
+ reconcileGradleComponents,
60
65
  recordGradleInvocationFailure,
61
66
  splitOutputByGradleProjects,
62
67
  stopMillDaemons,
@@ -91,6 +96,7 @@ import {
91
96
  } from "../ecosystems/sbtutils.js";
92
97
  import { collectScalaCliComponents } from "../ecosystems/scalacliutils.js";
93
98
  import { collectScalaJsNpmComponents } from "../ecosystems/scalasem.js";
99
+ import { isInVendoredDir } from "../ecosystems/vendoredCode.js";
94
100
  import {
95
101
  isBuildToolRateLimited,
96
102
  noteBuildToolRateLimit,
@@ -99,6 +105,7 @@ import {
99
105
  collectJarNS,
100
106
  collectMvnDependencies,
101
107
  convertJarNSToPackages,
108
+ namespaceMappingOfComponents,
102
109
  } from "../inventory/deps.js";
103
110
  import { mergeDependencies, trimComponents } from "../inventory/depsUtils.js";
104
111
  import {
@@ -112,8 +119,20 @@ import {
112
119
  projectSettingsArgs,
113
120
  } from "../inventory/jvmLocalRepos.js";
114
121
  import { readGradleWrapperVersion } from "../inventory/jvmToolEnv.js";
115
- 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";
116
131
  import { publishedArtifactId } from "../inventory/scalaCoords.js";
132
+ import {
133
+ mergeScalaDuplicateComponents,
134
+ rekeyNamespaceMapping,
135
+ } from "../inventory/scalaDedupe.js";
117
136
  import {
118
137
  buildBomNSData,
119
138
  createDefaultParentComponent,
@@ -253,6 +272,67 @@ export async function collectSbtDependencyJars(pkgList, known = {}) {
253
272
  return nsMapping;
254
273
  }
255
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
+
256
336
  /**
257
337
  * The purls and bom-refs of a parent component and the module components
258
338
  * nested under it.
@@ -814,6 +894,17 @@ export async function createJavaBom(path, options) {
814
894
  const projectSettings = projectSettingsArgs(basePath, {
815
895
  secureMode: isSecureModeActive(),
816
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() };
817
908
  if (projectSettings.args.length) {
818
909
  console.log(
819
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.`,
@@ -839,9 +930,10 @@ export async function createJavaBom(path, options) {
839
930
  if (!useMavenDepsTree && !isBuildToolRateLimited("maven")) {
840
931
  thoughtLog("The user wants me to use the cyclonedx-maven plugin.");
841
932
  console.log(`Executing '${mavenCmd}' in`, basePath);
842
- result = safeSpawnSync(mavenCmd, mvnArgs, {
933
+ result = safeSpawnSync(mavenCmd, mvnArgs.concat(mirrorSettingsArgs), {
843
934
  cwd: basePath,
844
935
  shell: isWin,
936
+ env: mvnSpawnEnv,
845
937
  });
846
938
  noteBuildToolRateLimit("maven", commandOutputText(result), {
847
939
  command: `${basename(mavenCmd)} ${mvnArgs.join(" ")}`,
@@ -906,7 +998,7 @@ export async function createJavaBom(path, options) {
906
998
  addArgs = parseMavenArgs(readEnvironmentVariable("MVN_ARGS"));
907
999
  mvnTreeArgs = mvnTreeArgs.concat(addArgs);
908
1000
  }
909
- mvnTreeArgs = mvnTreeArgs.concat(projectSettings.args);
1001
+ mvnTreeArgs = mvnTreeArgs.concat(mvnSettingsArgs);
910
1002
  // For the first pom alone, we need to execute first in non-recursive mode to capture
911
1003
  // the parent component. Then, we execute all of them in recursive mode
912
1004
  // A rate limit the --deep copy-dependencies run met ends it too.
@@ -922,11 +1014,12 @@ export async function createJavaBom(path, options) {
922
1014
  ];
923
1015
  findParentComponentArgs = findParentComponentArgs.concat(
924
1016
  addArgs,
925
- projectSettings.args,
1017
+ mvnSettingsArgs,
926
1018
  );
927
1019
  result = safeSpawnSync(mavenCmd, findParentComponentArgs, {
928
1020
  cwd: basePath,
929
1021
  shell: isWin,
1022
+ env: mvnSpawnEnv,
930
1023
  });
931
1024
  noteBuildToolRateLimit("maven", commandOutputText(result), {
932
1025
  command: `${basename(mavenCmd)} ${findParentComponentArgs.join(" ")}`,
@@ -956,10 +1049,11 @@ export async function createJavaBom(path, options) {
956
1049
  "dependency:tree",
957
1050
  "-N",
958
1051
  `-DoutputFile=${tempMvnParentTreeText}`,
959
- ].concat(addArgs, projectSettings.args);
1052
+ ].concat(addArgs, mvnSettingsArgs);
960
1053
  result = safeSpawnSync(mavenCmd, findParentComponentArgs, {
961
1054
  cwd: basePath,
962
1055
  shell: isWin,
1056
+ env: mvnSpawnEnv,
963
1057
  });
964
1058
  }
965
1059
  if (result.status === 0) {
@@ -1004,6 +1098,7 @@ export async function createJavaBom(path, options) {
1004
1098
  result = safeSpawnSync(mavenCmd, mvnTreeArgs, {
1005
1099
  cwd: basePath,
1006
1100
  shell: isWin,
1101
+ env: mvnSpawnEnv,
1007
1102
  });
1008
1103
  noteBuildToolRateLimit("maven", commandOutputText(result), {
1009
1104
  command: `${basename(mavenCmd)} ${mvnTreeArgs.join(" ")}`,
@@ -1029,10 +1124,11 @@ export async function createJavaBom(path, options) {
1029
1124
  mvnTreeArgs = [
1030
1125
  "dependency:tree",
1031
1126
  `-DoutputFile=${tempMvnTreeText}`,
1032
- ].concat(addArgs, projectSettings.args);
1127
+ ].concat(addArgs, mvnSettingsArgs);
1033
1128
  result = safeSpawnSync(mavenCmd, mvnTreeArgs, {
1034
1129
  cwd: basePath,
1035
1130
  shell: isWin,
1131
+ env: mvnSpawnEnv,
1036
1132
  });
1037
1133
  noteBuildToolRateLimit("maven", commandOutputText(result), {
1038
1134
  command: `${basename(mavenCmd)} ${mvnTreeArgs.join(" ")}`,
@@ -1182,6 +1278,12 @@ export async function createJavaBom(path, options) {
1182
1278
  parentComponent = tmpParentComponent;
1183
1279
  parentComponent.components = [];
1184
1280
  } else {
1281
+ separateDuplicateMavenProject(
1282
+ tmpParentComponent,
1283
+ parentComponent,
1284
+ path,
1285
+ parsedList.dependenciesList,
1286
+ );
1185
1287
  parentComponent.components.push(tmpParentComponent);
1186
1288
  }
1187
1289
  if (parsedList?.dependenciesList?.length) {
@@ -1309,9 +1411,14 @@ export async function createJavaBom(path, options) {
1309
1411
  }
1310
1412
  }
1311
1413
  const allProjects = [];
1312
- 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();
1313
1417
  const rootDependsOn = new Set();
1314
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();
1315
1422
  // Determine the root path for gradle
1316
1423
  // Fixes gradle invocation for microservices-demo
1317
1424
  let gradleRootPath = path;
@@ -1413,6 +1520,12 @@ export async function createJavaBom(path, options) {
1413
1520
  const gradleInitArgs = readEnvironmentVariable("GRADLE_INCLUDED_BUILDS")
1414
1521
  ? []
1415
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
+ }
1416
1529
  if (resolveGradleDistribution && gradleInitArgs.length) {
1417
1530
  gradleInitArgs.push("-PcdxgenResolveDistribution=true");
1418
1531
  }
@@ -1484,12 +1597,19 @@ export async function createJavaBom(path, options) {
1484
1597
  );
1485
1598
  if (!includedBuilds.includes(key)) {
1486
1599
  parentComponent = rootComponent;
1600
+ } else if (retMap.gradlePlugin) {
1601
+ gradlePluginBuilds.add(key);
1487
1602
  }
1488
1603
  gradleModules.set(key, rootComponent);
1489
- if (!allProjectsAddedPurls.includes(rootComponent["purl"])) {
1604
+ if (
1605
+ !allProjectsAddedRefs.has(rootComponent["bom-ref"] ?? rootComponent)
1606
+ ) {
1490
1607
  allProjects.push(rootComponent);
1491
1608
  rootDependsOn.add(rootComponent["bom-ref"]);
1492
- allProjectsAddedPurls.push(rootComponent["purl"]);
1609
+ allProjectsAddedRefs.set(
1610
+ rootComponent["bom-ref"] ?? rootComponent,
1611
+ rootComponent,
1612
+ );
1493
1613
  }
1494
1614
  allProjectsStr = allProjectsStr.concat(retMap.projects);
1495
1615
  }
@@ -1522,14 +1642,60 @@ export async function createJavaBom(path, options) {
1522
1642
  );
1523
1643
  const rootSubProject = retMap.rootProject;
1524
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
+ }
1525
1657
  const rootSubProjectObj = await buildObjectForGradleModule(
1526
- rootSubProject === "root" ? subProject : rootSubProject,
1658
+ rootSubProject === "root"
1659
+ ? subProject.split(":").pop()
1660
+ : rootSubProject,
1527
1661
  retMap.metadata,
1528
1662
  );
1529
- 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)) {
1530
1696
  allProjects.push(rootSubProjectObj);
1531
1697
  rootDependsOn.add(rootSubProjectObj["bom-ref"]);
1532
- allProjectsAddedPurls.push(rootSubProjectObj["purl"]);
1698
+ allProjectsAddedRefs.set(moduleKey, rootSubProjectObj);
1533
1699
  }
1534
1700
  gradleModules.set(subProject, rootSubProjectObj);
1535
1701
  }
@@ -1561,7 +1727,6 @@ export async function createJavaBom(path, options) {
1561
1727
  ? readEnvironmentVariable("GRADLE_DEPENDENCY_TASK")
1562
1728
  : "dependencies";
1563
1729
 
1564
- const gradleSubCommands = [];
1565
1730
  let modulesToSkip = readEnvironmentVariable("GRADLE_SKIP_MODULES")
1566
1731
  ? readEnvironmentVariable("GRADLE_SKIP_MODULES").split(",")
1567
1732
  : [];
@@ -1570,23 +1735,16 @@ export async function createJavaBom(path, options) {
1570
1735
  readEnvironmentVariable("GRADLE_SKIP_MODULE_DEPENDENCIES").split(","),
1571
1736
  );
1572
1737
  }
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
- }
1738
+ const gradleSubCommands = gradleDependencyTasks(
1739
+ gradleModules,
1740
+ parentComponent,
1741
+ gradleDepTask,
1742
+ modulesToSkip,
1743
+ );
1582
1744
  const gradleArguments = buildGradleCommandArguments(
1583
- readEnvironmentVariable("GRADLE_ARGS")
1584
- ? readEnvironmentVariable("GRADLE_ARGS").split(" ")
1585
- : [],
1745
+ gradleArgsFromEnv("GRADLE_ARGS"),
1586
1746
  gradleSubCommands,
1587
- readEnvironmentVariable("GRADLE_ARGS_DEPENDENCIES")
1588
- ? readEnvironmentVariable("GRADLE_ARGS_DEPENDENCIES").split(" ")
1589
- : [],
1747
+ gradleArgsFromEnv("GRADLE_ARGS_DEPENDENCIES"),
1590
1748
  gradleCmd.length,
1591
1749
  );
1592
1750
  const allOutputs = [];
@@ -1647,14 +1805,30 @@ export async function createJavaBom(path, options) {
1647
1805
  const perProjectOutput = splitOutputByGradleProjects(cmdOutput, [
1648
1806
  gradleDepTask,
1649
1807
  ]);
1808
+ await loadGradleModulesForReports(
1809
+ perProjectOutput.values(),
1810
+ gradleModules,
1811
+ gradleRootPath,
1812
+ options,
1813
+ );
1814
+ const reportedProjects = new Set(perProjectOutput.keys());
1650
1815
  for (const key of gradleModules.keys()) {
1651
1816
  const parsedList = await parseGradleDep(
1652
1817
  perProjectOutput.has(key) ? perProjectOutput.get(key) : "",
1653
1818
  key,
1654
1819
  gradleModules,
1655
1820
  gradleRootPath,
1821
+ reportedProjects,
1656
1822
  );
1657
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
+ }
1658
1832
  if (parsedList?.dependenciesList?.length) {
1659
1833
  dependencies = mergeDependencies(
1660
1834
  dependencies,
@@ -1672,6 +1846,7 @@ export async function createJavaBom(path, options) {
1672
1846
  pkgList = pkgList.concat(dlist);
1673
1847
  }
1674
1848
  }
1849
+ reconcileGradleComponents(pkgList);
1675
1850
  }
1676
1851
  if (pkgList.length) {
1677
1852
  const fileToUrlMap = parseGradleInfoLogsForUrls(sstdout);
@@ -1685,9 +1860,15 @@ export async function createJavaBom(path, options) {
1685
1860
  if (!p.externalReferences) {
1686
1861
  p.externalReferences = [];
1687
1862
  }
1688
- 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
+ ) {
1689
1870
  const groupPath = p.group.replace(/\./g, "/");
1690
- const matchPath = `${groupPath}/${p.name}/${p.version}/`;
1871
+ const matchPath = `/${groupPath}/${p.name}/${p.version}/`;
1691
1872
  let matchedUrl = Object.values(fileToUrlMap).find((url) =>
1692
1873
  url.includes(matchPath),
1693
1874
  );
@@ -1809,11 +1990,14 @@ export async function createJavaBom(path, options) {
1809
1990
  // Bazel
1810
1991
  // Look for the BUILD file only in the root directory
1811
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.
1812
1996
  const bazelFiles = getAllFiles(
1813
1997
  path,
1814
1998
  `${options.multiProject ? "**/" : ""}{WORKSPACE{,.bazel},MODULE.bazel}`,
1815
1999
  options,
1816
- );
2000
+ ).filter((f) => !isInVendoredDir(path, f));
1817
2001
  if (
1818
2002
  bazelFiles?.length &&
1819
2003
  !hasAnyProjectType(
@@ -1886,6 +2070,9 @@ export async function createJavaBom(path, options) {
1886
2070
  ) {
1887
2071
  bArgs = ["--bazelrc=.bazelrc", "build", bazelTarget];
1888
2072
  }
2073
+ // Repository rules see the mirror through their own environment,
2074
+ // which only a --repo_env flag reaches.
2075
+ bArgs = bArgs.concat(bazelMirrorArgs());
1889
2076
  console.log("Executing", BAZEL_CMD, "in", basePath);
1890
2077
  let result = safeSpawnSync(BAZEL_CMD, bArgs, {
1891
2078
  cwd: basePath,
@@ -2099,7 +2286,7 @@ export async function createJavaBom(path, options) {
2099
2286
  // sbt resolves into the user's own caches, so a project that has been
2100
2287
  // built resolves without the network. The dependency trees download
2101
2288
  // the jar of every dependency they list, which is all --deep reads.
2102
- const env = { ...process.env };
2289
+ const env = { ...process.env, ...sbtMirrorEnv() };
2103
2290
  for (const i in sbtProjects) {
2104
2291
  if (isBuildToolRateLimited("sbt")) {
2105
2292
  // Every remaining sbt build in this scan resolves from the same
@@ -2450,19 +2637,13 @@ export async function createJavaBom(path, options) {
2450
2637
  const jarNSData = await createJarBom(path, options);
2451
2638
  if (jarNSData?.bomJson?.components) {
2452
2639
  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 };
2640
+ jarNSMapping = {
2641
+ ...jarNSMapping,
2642
+ ...namespaceMappingOfComponents(
2643
+ jarNSData.bomJson.components,
2644
+ jarNSMapping,
2645
+ ),
2646
+ };
2466
2647
  }
2467
2648
  }
2468
2649
  }
@@ -2504,10 +2685,12 @@ export async function createJavaBom(path, options) {
2504
2685
  if (DEBUG_MODE) {
2505
2686
  console.log("Executing", millCmd, "in", millRootPath);
2506
2687
  }
2688
+ const millSpawnEnv = { ...process.env, ...coursierMirrorEnv() };
2507
2689
  let sresult = safeSpawnSync(millCmd, millArgs, {
2508
2690
  cwd: millRootPath,
2509
2691
  shell: isWin,
2510
2692
  maxBuffer: MAX_BUFFER * 10,
2693
+ env: millSpawnEnv,
2511
2694
  });
2512
2695
  noteBuildToolRateLimit("mill", commandOutputText(sresult), {
2513
2696
  command: `${basename(millCmd)} ${millArgs.join(" ")}`,
@@ -2546,6 +2729,7 @@ export async function createJavaBom(path, options) {
2546
2729
  sresult = safeSpawnSync(millCmd, millResolveArgs, {
2547
2730
  cwd: millRootPath,
2548
2731
  shell: isWin,
2732
+ env: millSpawnEnv,
2549
2733
  });
2550
2734
  if (sresult.status !== 0 || sresult.error) {
2551
2735
  if (options.failOnError || DEBUG_MODE) {
@@ -2710,6 +2894,13 @@ export async function createJavaBom(path, options) {
2710
2894
  // Deduplication and metadata belong to whatever was collected, so they run
2711
2895
  // on the deferred path too.
2712
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);
2713
2904
  // --deep collects namespaces; it does not opt in to remote licence lookups,
2714
2905
  // which FETCH_LICENSE controls. The project's own modules are never looked
2715
2906
  // up remotely: they are not published.