@cdxgen/cdxgen 13.4.0 → 13.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/README.md +9 -9
  2. package/bin/cdxgen-secure.js +15 -0
  3. package/bin/cdxgen.js +29 -5
  4. package/bin/evinse.js +8 -0
  5. package/bin/hbom.js +15 -7
  6. package/bin/repl.js +29 -0
  7. package/bin/validate.js +6 -0
  8. package/data/cbomosdb-queries.json +13 -1
  9. package/data/component-tags.json +8 -0
  10. package/data/license-aliases.json +24 -2
  11. package/data/queries-darwin.json +25 -1
  12. package/data/queries-win.json +19 -1
  13. package/data/queries.json +41 -0
  14. package/data/remediations.json +407 -1
  15. package/data/rules/ai-bom.yaml +1 -1
  16. package/data/rules/brotli-decoder.yaml +107 -0
  17. package/data/rules/cbom-security.yaml +235 -4
  18. package/data/rules/go-binaries.yaml +46 -0
  19. package/data/rules/golem-go.yaml +80 -0
  20. package/data/rules/hbom-compliance.yaml +40 -22
  21. package/data/rules/hbom-performance.yaml +7 -1
  22. package/data/rules/host-topology.yaml +1 -1
  23. package/data/rules/obom-runtime.yaml +31 -2
  24. package/data/rules/php-runtime.yaml +143 -0
  25. package/data/rules/vscode-extensions.yaml +75 -0
  26. package/data/spdx-license-list.json +121 -7
  27. package/data/spdx-licenses.json +69 -0
  28. package/data/spdx.schema.json +845 -2
  29. package/lib/audit/index.js +14 -0
  30. package/lib/cli/bomAssembly.js +120 -6
  31. package/lib/cli/cliOptions.js +110 -3
  32. package/lib/cli/index.js +182 -6
  33. package/lib/cli/jsBom.js +153 -64
  34. package/lib/cli/jvmBom.js +363 -66
  35. package/lib/cli/managedBom.js +853 -87
  36. package/lib/cli/nativeBom.js +267 -33
  37. package/lib/core/boundedFile.js +344 -0
  38. package/lib/core/brotliProbe.js +182 -0
  39. package/lib/core/env.js +38 -8
  40. package/lib/core/fs.js +40 -19
  41. package/lib/core/httpClient.js +172 -46
  42. package/lib/core/lockPaths.js +38 -0
  43. package/lib/core/paths.js +24 -1
  44. package/lib/core/processTree.js +5 -0
  45. package/lib/core/projectConfig.js +1 -0
  46. package/lib/core/projectTooling.js +97 -0
  47. package/lib/core/propertySanitizer.js +72 -0
  48. package/lib/core/toolRegistry.js +170 -0
  49. package/lib/ecosystems/cppEvidence.js +263 -106
  50. package/lib/ecosystems/cppIncludeResolver.js +16 -5
  51. package/lib/ecosystems/ecosystems.js +25 -10
  52. package/lib/ecosystems/gemHostProbe.js +301 -0
  53. package/lib/ecosystems/golem.js +550 -6
  54. package/lib/ecosystems/gradleutils.js +858 -244
  55. package/lib/ecosystems/parsers-bazel.js +107 -6
  56. package/lib/ecosystems/parsers-dotnet.js +117 -18
  57. package/lib/ecosystems/parsers-go.js +1090 -367
  58. package/lib/ecosystems/parsers-js.js +505 -22
  59. package/lib/ecosystems/parsers-jvm.js +259 -17
  60. package/lib/ecosystems/parsers-misc.js +438 -64
  61. package/lib/ecosystems/parsers-python.js +100 -12
  62. package/lib/ecosystems/pythonutils.js +128 -2
  63. package/lib/ecosystems/rubyutils.js +544 -34
  64. package/lib/ecosystems/rusi.js +10 -0
  65. package/lib/ecosystems/scalacliutils.js +4 -1
  66. package/lib/ecosystems/scalasem.js +798 -151
  67. package/lib/ecosystems/vendoredCode.js +237 -3
  68. package/lib/ecosystems/venvProbe.js +360 -0
  69. package/lib/evinser/evinser.js +472 -61
  70. package/lib/evinser/swiftsem.js +68 -5
  71. package/lib/helpers/agenticToolutils.js +413 -93
  72. package/lib/helpers/vsixutils.js +1574 -315
  73. package/lib/inventory/agentSkillSpec.js +17 -0
  74. package/lib/inventory/apkInstalledDb.js +243 -0
  75. package/lib/inventory/atomUtils.js +110 -0
  76. package/lib/inventory/auditCategories.js +3 -3
  77. package/lib/inventory/brotliDecoder.js +142 -0
  78. package/lib/inventory/buildToolRateLimit.js +1 -1
  79. package/lib/inventory/cacheDir.js +52 -9
  80. package/lib/inventory/cbomutils.js +56 -7
  81. package/lib/inventory/certificates.js +2371 -0
  82. package/lib/inventory/communityAiConfigParser.js +4 -9
  83. package/lib/inventory/deps.js +41 -4
  84. package/lib/inventory/display.js +200 -3
  85. package/lib/inventory/dosai.js +639 -63
  86. package/lib/inventory/dotnetRuntime.js +217 -0
  87. package/lib/inventory/dynamic.js +45 -3
  88. package/lib/inventory/envcontext.js +475 -18
  89. package/lib/inventory/evidenceUtils.js +447 -23
  90. package/lib/inventory/fetchBatch.js +1 -1
  91. package/lib/inventory/formulationParsers.js +32 -3
  92. package/lib/inventory/goBuildInfo.js +140 -0
  93. package/lib/inventory/gtfobins.js +13 -0
  94. package/lib/inventory/hbom.js +67 -7
  95. package/lib/inventory/hbomAnalysis.js +16 -0
  96. package/lib/inventory/hostRuntimeProbe.js +686 -0
  97. package/lib/inventory/hostTopology.js +21 -0
  98. package/lib/inventory/jvmLocalRepos.js +9 -2
  99. package/lib/inventory/jvmToolEnv.js +29 -0
  100. package/lib/inventory/licenseEnhancer.js +12 -0
  101. package/lib/inventory/mavenCentralMirror.js +534 -0
  102. package/lib/inventory/nodeBuildInfo.js +218 -0
  103. package/lib/inventory/nodeHostProbe.js +202 -0
  104. package/lib/inventory/osPackagePurl.js +244 -0
  105. package/lib/inventory/osPackageResolver.js +271 -87
  106. package/lib/inventory/osinfo.js +79 -31
  107. package/lib/inventory/osqueryTransform.js +114 -5
  108. package/lib/inventory/phpBuildInfo.js +438 -0
  109. package/lib/inventory/phpHostProbe.js +185 -0
  110. package/lib/inventory/rootfsCerts.js +426 -0
  111. package/lib/inventory/rubyBuildInfo.js +972 -0
  112. package/lib/inventory/rubyHostProbe.js +218 -0
  113. package/lib/inventory/scalaCoords.js +4 -3
  114. package/lib/inventory/scalaDedupe.js +440 -0
  115. package/lib/inventory/source.js +256 -5
  116. package/lib/inventory/spdx.js +144 -0
  117. package/lib/inventory/traceRunner.js +5 -1
  118. package/lib/inventory/tracedCrypto.js +129 -0
  119. package/lib/managers/binary.js +866 -186
  120. package/lib/managers/docker.js +245 -12
  121. package/lib/parsers/email.js +277 -0
  122. package/lib/parsers/largeJson.js +62 -13
  123. package/lib/parsers/vscodeExtensionId.js +183 -0
  124. package/lib/server/server.js +198 -2
  125. package/lib/stages/postgen/annotator.js +17 -1
  126. package/lib/stages/postgen/introspection/reflect.js +4 -1
  127. package/lib/stages/postgen/postgen.js +5 -5
  128. package/lib/stages/postgen/ruleEngine.js +71 -6
  129. package/lib/stages/postgen/specVersionCompat.js +30 -0
  130. package/lib/stages/pregen/envAudit.js +1 -0
  131. package/lib/stages/pregen/pregen.js +231 -62
  132. package/lib/validator/bomValidator.js +32 -6
  133. package/lib/validator/generated/runtime.mjs +13 -0
  134. package/lib/validator/generated/validate-1.6.mjs +27 -8
  135. package/lib/validator/generated/validate-1.7.mjs +32 -13
  136. package/lib/validator/generated/validate-2.0.mjs +31 -12
  137. package/lib/validator/index.js +1 -1
  138. package/package.json +19 -18
  139. package/types/bin/cdxgen-secure.d.ts +3 -0
  140. package/types/bin/cdxgen-secure.d.ts.map +1 -0
  141. package/types/bin/repl.d.ts.map +1 -1
  142. package/types/lib/audit/index.d.ts.map +1 -1
  143. package/types/lib/cli/bomAssembly.d.ts +22 -3
  144. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  145. package/types/lib/cli/cliOptions.d.ts +43 -0
  146. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  147. package/types/lib/cli/index.d.ts.map +1 -1
  148. package/types/lib/cli/jsBom.d.ts +1 -17
  149. package/types/lib/cli/jsBom.d.ts.map +1 -1
  150. package/types/lib/cli/jvmBom.d.ts +20 -0
  151. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  152. package/types/lib/cli/managedBom.d.ts.map +1 -1
  153. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  154. package/types/lib/core/boundedFile.d.ts +70 -0
  155. package/types/lib/core/boundedFile.d.ts.map +1 -0
  156. package/types/lib/core/brotliProbe.d.ts +37 -0
  157. package/types/lib/core/brotliProbe.d.ts.map +1 -0
  158. package/types/lib/core/env.d.ts +22 -2
  159. package/types/lib/core/env.d.ts.map +1 -1
  160. package/types/lib/core/fs.d.ts.map +1 -1
  161. package/types/lib/core/httpClient.d.ts +24 -0
  162. package/types/lib/core/httpClient.d.ts.map +1 -1
  163. package/types/lib/core/lockPaths.d.ts +19 -0
  164. package/types/lib/core/lockPaths.d.ts.map +1 -0
  165. package/types/lib/core/processTree.d.ts +2 -0
  166. package/types/lib/core/processTree.d.ts.map +1 -1
  167. package/types/lib/core/projectConfig.d.ts.map +1 -1
  168. package/types/lib/core/projectTooling.d.ts +41 -0
  169. package/types/lib/core/projectTooling.d.ts.map +1 -0
  170. package/types/lib/core/propertySanitizer.d.ts +26 -0
  171. package/types/lib/core/propertySanitizer.d.ts.map +1 -1
  172. package/types/lib/core/toolRegistry.d.ts +36 -0
  173. package/types/lib/core/toolRegistry.d.ts.map +1 -0
  174. package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
  175. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  176. package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
  177. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
  178. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  179. package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
  180. package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
  181. package/types/lib/ecosystems/golem.d.ts +81 -1
  182. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  183. package/types/lib/ecosystems/gradleutils.d.ts +57 -21
  184. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  185. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
  186. package/types/lib/ecosystems/parsers-dotnet.d.ts +40 -4
  187. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  188. package/types/lib/ecosystems/parsers-go.d.ts +85 -13
  189. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  190. package/types/lib/ecosystems/parsers-js.d.ts +17 -20
  191. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  192. package/types/lib/ecosystems/parsers-jvm.d.ts +28 -2
  193. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  194. package/types/lib/ecosystems/parsers-misc.d.ts +37 -58
  195. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  196. package/types/lib/ecosystems/parsers-python.d.ts +14 -3
  197. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  198. package/types/lib/ecosystems/pythonutils.d.ts +19 -1
  199. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  200. package/types/lib/ecosystems/rubyutils.d.ts +144 -12
  201. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  202. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  203. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
  204. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  205. package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
  206. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
  207. package/types/lib/ecosystems/venvProbe.d.ts +40 -0
  208. package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
  209. package/types/lib/evinser/evinser.d.ts +88 -8
  210. package/types/lib/evinser/evinser.d.ts.map +1 -1
  211. package/types/lib/evinser/swiftsem.d.ts +10 -0
  212. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  213. package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
  214. package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
  215. package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
  216. package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
  217. package/types/lib/inventory/atomUtils.d.ts +33 -0
  218. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  219. package/types/lib/inventory/brotliDecoder.d.ts +58 -0
  220. package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
  221. package/types/lib/inventory/cacheDir.d.ts +12 -4
  222. package/types/lib/inventory/cacheDir.d.ts.map +1 -1
  223. package/types/lib/inventory/cbomutils.d.ts +17 -0
  224. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  225. package/types/lib/inventory/certificates.d.ts +205 -0
  226. package/types/lib/inventory/certificates.d.ts.map +1 -0
  227. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  228. package/types/lib/inventory/deps.d.ts +2 -1
  229. package/types/lib/inventory/deps.d.ts.map +1 -1
  230. package/types/lib/inventory/display.d.ts +18 -0
  231. package/types/lib/inventory/display.d.ts.map +1 -1
  232. package/types/lib/inventory/dosai.d.ts +162 -7
  233. package/types/lib/inventory/dosai.d.ts.map +1 -1
  234. package/types/lib/inventory/dotnetRuntime.d.ts +44 -0
  235. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
  236. package/types/lib/inventory/dynamic.d.ts +6 -1
  237. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  238. package/types/lib/inventory/envcontext.d.ts +92 -8
  239. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  240. package/types/lib/inventory/evidenceUtils.d.ts +29 -10
  241. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  242. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  243. package/types/lib/inventory/goBuildInfo.d.ts +10 -0
  244. package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
  245. package/types/lib/inventory/gtfobins.d.ts.map +1 -1
  246. package/types/lib/inventory/hbom.d.ts.map +1 -1
  247. package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
  248. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
  249. package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
  250. package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
  251. package/types/lib/inventory/hostTopology.d.ts.map +1 -1
  252. package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
  253. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  254. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
  255. package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
  256. package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
  257. package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
  258. package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
  259. package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
  260. package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
  261. package/types/lib/inventory/osPackagePurl.d.ts +99 -0
  262. package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
  263. package/types/lib/inventory/osPackageResolver.d.ts +44 -2
  264. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  265. package/types/lib/inventory/osinfo.d.ts +31 -6
  266. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  267. package/types/lib/inventory/osqueryTransform.d.ts +51 -0
  268. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
  269. package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
  270. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
  271. package/types/lib/inventory/phpHostProbe.d.ts +35 -0
  272. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
  273. package/types/lib/inventory/rootfsCerts.d.ts +27 -0
  274. package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
  275. package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
  276. package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
  277. package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
  278. package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
  279. package/types/lib/inventory/source.d.ts +38 -0
  280. package/types/lib/inventory/source.d.ts.map +1 -1
  281. package/types/lib/inventory/spdx.d.ts +15 -0
  282. package/types/lib/inventory/spdx.d.ts.map +1 -1
  283. package/types/lib/inventory/traceRunner.d.ts +4 -1
  284. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  285. package/types/lib/inventory/tracedCrypto.d.ts +22 -0
  286. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -1
  287. package/types/lib/managers/binary.d.ts +10 -0
  288. package/types/lib/managers/binary.d.ts.map +1 -1
  289. package/types/lib/managers/docker.d.ts +24 -0
  290. package/types/lib/managers/docker.d.ts.map +1 -1
  291. package/types/lib/parsers/email.d.ts +19 -0
  292. package/types/lib/parsers/email.d.ts.map +1 -0
  293. package/types/lib/parsers/largeJson.d.ts +17 -3
  294. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  295. package/types/lib/server/server.d.ts +8 -0
  296. package/types/lib/server/server.d.ts.map +1 -1
  297. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  298. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
  299. package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
  300. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  301. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  302. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  303. package/types/lib/stages/pregen/pregen.d.ts +26 -9
  304. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  305. package/types/lib/validator/bomValidator.d.ts.map +1 -1
@@ -20,17 +20,61 @@ import { applyPurl } from "../inventory/purl.js";
20
20
  import { parseScalaArtifact } from "../inventory/scalaCoords.js";
21
21
  import { xml2js } from "../parsers/xml.js";
22
22
 
23
+ /** Bound on the `${...}` references one pom value may expand through. */
24
+ const POM_PROPERTY_EXPANSION_LIMIT = 10;
25
+
26
+ /**
27
+ * Expand the `${name}` references in a pom value. The value is returned only
28
+ * when every reference resolves, so a version is never half expanded.
29
+ *
30
+ * @param {string} value Raw pom value
31
+ * @param {Function} lookup Returns the value of a property name, or undefined
32
+ * @returns {string|undefined} Expanded value, or undefined
33
+ */
34
+ function expandPomValue(value, lookup) {
35
+ let expanded = value;
36
+ for (
37
+ let i = 0;
38
+ i < POM_PROPERTY_EXPANSION_LIMIT && expanded?.includes("${");
39
+ i++
40
+ ) {
41
+ const start = expanded.indexOf("${");
42
+ const end = expanded.indexOf("}", start + 2);
43
+ if (end < 0) {
44
+ return undefined;
45
+ }
46
+ const replacement = lookup(expanded.slice(start + 2, end));
47
+ if (typeof replacement !== "string") {
48
+ return undefined;
49
+ }
50
+ expanded = `${expanded.slice(0, start)}${replacement}${expanded.slice(end + 1)}`;
51
+ }
52
+ return expanded?.includes("${") ? undefined : expanded;
53
+ }
54
+
23
55
  /**
24
56
  * Parse pom file
25
57
  *
58
+ * Versions resolve from the pom's own properties and dependencyManagement.
59
+ * A caller that has read the pom's parents passes what they define in
60
+ * `inherited`, so versions the parents manage resolve too. Imported BOMs and
61
+ * parents outside the scanned directory are not read.
62
+ *
26
63
  * @param {string} pomFile pom file to parse
27
- * @returns {Object} Object containing pom properties, modules, and array of dependencies
64
+ * @param {Object} [inherited] Properties and managed versions from parent poms
65
+ * @param {Object} [inherited.properties] Property name to raw value
66
+ * @param {Object} [inherited.managedVersions] `groupId:artifactId` to version
67
+ * @returns {Object} Object containing pom properties, modules, the parent
68
+ * reference, the user properties and managed versions this pom defines, and
69
+ * array of dependencies
28
70
  */
29
- export function parsePom(pomFile) {
71
+ export function parsePom(pomFile, inherited = {}) {
30
72
  const deps = [];
31
73
  let modules;
32
74
  let pomPurl;
33
75
  const properties = {};
76
+ const userProperties = {};
77
+ const managedVersions = {};
34
78
  let isQuarkus = false;
35
79
  const xmlData = readFileSync(pomFile, "utf-8");
36
80
  const project = xml2js(xmlData, {
@@ -63,25 +107,30 @@ export function parsePom(pomFile) {
63
107
  }
64
108
  }
65
109
  // Take the version from the parent if available
66
- if (!properties.version && project.parent) {
110
+ if (!properties.version && project?.parent?.version?._) {
67
111
  properties.version = project.parent.version._;
68
112
  }
69
113
  // Take the groupId from the parent if available
70
- if (!properties.groupId && project.parent) {
114
+ if (!properties.groupId && project?.parent?.groupId?._) {
71
115
  properties.groupId = project.parent.groupId._;
72
116
  }
117
+ let parent;
118
+ if (project?.parent) {
119
+ parent = {
120
+ groupId: project.parent.groupId?._,
121
+ artifactId: project.parent.artifactId?._,
122
+ version: project.parent.version?._,
123
+ // An empty <relativePath/> tells Maven not to look for the parent on
124
+ // disk; a missing one means ../pom.xml.
125
+ relativePath:
126
+ project.parent.relativePath === undefined
127
+ ? "../pom.xml"
128
+ : project.parent.relativePath?._ || "",
129
+ };
130
+ }
73
131
  if (project?.scm?.url?._) {
74
132
  properties.scm = project.scm.url._;
75
133
  }
76
- if (properties.groupId || properties.artifactId) {
77
- pomPurl = build({
78
- type: "maven",
79
- namespace: properties.groupId || "" || null,
80
- name: properties.artifactId,
81
- version: properties.version || null,
82
- qualifiers: { type: properties.packaging || "jar" } || null,
83
- });
84
- }
85
134
  const moduleEntries = [];
86
135
  if (project?.modules?.module) {
87
136
  moduleEntries.push(project.modules.module);
@@ -100,11 +149,80 @@ export function parsePom(pomFile) {
100
149
  if (project?.properties) {
101
150
  for (const aprop of Object.keys(project.properties)) {
102
151
  properties[aprop] = project.properties[aprop]?._;
152
+ userProperties[aprop] = project.properties[aprop]?._;
103
153
  if (!isQuarkus && aprop.startsWith("quarkus.platform")) {
104
154
  isQuarkus = true;
105
155
  }
106
156
  }
107
157
  }
158
+ const inheritedProperties = inherited?.properties || {};
159
+ const lookupProperty = (name) => {
160
+ switch (name) {
161
+ case "project.version":
162
+ case "pom.version":
163
+ case "version":
164
+ return properties.version;
165
+ case "project.groupId":
166
+ case "pom.groupId":
167
+ case "groupId":
168
+ return properties.groupId;
169
+ case "project.artifactId":
170
+ case "pom.artifactId":
171
+ case "artifactId":
172
+ return properties.artifactId;
173
+ case "project.parent.version":
174
+ return parent?.version;
175
+ case "project.parent.groupId":
176
+ return parent?.groupId;
177
+ default:
178
+ return Object.hasOwn(userProperties, name)
179
+ ? userProperties[name]
180
+ : inheritedProperties[name];
181
+ }
182
+ };
183
+ // A property can refer to another, so each lookup expands its value too.
184
+ const expand = (value) =>
185
+ expandPomValue(value, (name) => {
186
+ const raw = lookupProperty(name);
187
+ return raw?.includes("${")
188
+ ? expandPomValue(raw, (inner) =>
189
+ inner === name ? undefined : lookupProperty(inner),
190
+ )
191
+ : raw;
192
+ });
193
+ let managedEntries = project?.dependencyManagement?.dependencies?.dependency;
194
+ if (managedEntries && !Array.isArray(managedEntries)) {
195
+ managedEntries = [managedEntries];
196
+ }
197
+ for (const amanaged of managedEntries || []) {
198
+ // An imported BOM manages versions this pom cannot see without fetching it
199
+ if (amanaged?.scope?._ === "import" || !amanaged?.version?._) {
200
+ continue;
201
+ }
202
+ const group = expand(amanaged.groupId?._);
203
+ const name = expand(amanaged.artifactId?._);
204
+ const version = expand(amanaged.version._);
205
+ if (group && name && version) {
206
+ managedVersions[`${group}:${name}`] = version;
207
+ }
208
+ }
209
+ const inheritedManagedVersions = inherited?.managedVersions || {};
210
+ // CI friendly versions such as ${revision} resolve from the properties
211
+ for (const coordinate of ["groupId", "version"]) {
212
+ if (properties[coordinate]?.includes("${")) {
213
+ properties[coordinate] =
214
+ expand(properties[coordinate]) || properties[coordinate];
215
+ }
216
+ }
217
+ if (properties.groupId || properties.artifactId) {
218
+ pomPurl = build({
219
+ type: "maven",
220
+ namespace: properties.groupId || "" || null,
221
+ name: properties.artifactId,
222
+ version: properties.version || null,
223
+ qualifiers: { type: properties.packaging || "jar" } || null,
224
+ });
225
+ }
108
226
  // Check the plugins for quarkus
109
227
  if (!isQuarkus && project?.build?.plugins?.plugin) {
110
228
  if (Array.isArray(project.build.plugins.plugin)) {
@@ -138,7 +256,18 @@ export function parsePom(pomFile) {
138
256
  versionStr = version._;
139
257
  }
140
258
  if (versionStr?.includes("$")) {
141
- versionStr = properties[versionStr?.replace(/[${}]/g, "")];
259
+ versionStr = expand(versionStr);
260
+ }
261
+ let groupStr = adep.groupId ? adep.groupId._ : "";
262
+ if (groupStr?.includes("${")) {
263
+ groupStr = expand(groupStr) || groupStr;
264
+ }
265
+ const nameStr = adep.artifactId ? adep.artifactId._ : "";
266
+ if (!versionStr && !version?._) {
267
+ const managedKey = `${groupStr}:${nameStr}`;
268
+ versionStr = Object.hasOwn(managedVersions, managedKey)
269
+ ? managedVersions[managedKey]
270
+ : inheritedManagedVersions[managedKey];
142
271
  }
143
272
  const scope = adep.scope?._;
144
273
  const type = adep.type?._ || "jar";
@@ -156,8 +285,8 @@ export function parsePom(pomFile) {
156
285
  }
157
286
  if (includeMavenTestScope || scope !== "test") {
158
287
  deps.push({
159
- group: adep.groupId ? adep.groupId._ : "",
160
- name: adep.artifactId ? adep.artifactId._ : "",
288
+ group: groupStr,
289
+ name: nameStr,
161
290
  version: versionStr,
162
291
  qualifiers,
163
292
  scope: mapMavenScope(scope, optional === "true"),
@@ -179,7 +308,120 @@ export function parsePom(pomFile) {
179
308
  }
180
309
  }
181
310
  }
182
- return { isQuarkus, pomPurl, modules, properties, dependencies: deps };
311
+ return {
312
+ isQuarkus,
313
+ pomPurl,
314
+ modules,
315
+ parent,
316
+ properties,
317
+ userProperties,
318
+ managedVersions,
319
+ dependencies: deps,
320
+ };
321
+ }
322
+
323
+ /** Bound on the chain of parent poms read from disk. */
324
+ const LOCAL_PARENT_DEPTH_LIMIT = 10;
325
+
326
+ /**
327
+ * The pom file a `<parent>` reference points at on disk, when it lies inside
328
+ * the scanned directory and declares the coordinates the reference names.
329
+ *
330
+ * @param {string} pomFile Child pom file
331
+ * @param {Object} parentRef Parent reference from parsePom
332
+ * @param {string} rootDir Scanned directory
333
+ * @returns {string|undefined} Parent pom file
334
+ */
335
+ function locateLocalParentPom(pomFile, parentRef, rootDir) {
336
+ if (!parentRef?.relativePath || !rootDir) {
337
+ return undefined;
338
+ }
339
+ let candidate = resolve(dirname(pomFile), parentRef.relativePath);
340
+ if (!candidate.endsWith(".xml")) {
341
+ candidate = join(candidate, "pom.xml");
342
+ }
343
+ const rel = relative(resolve(rootDir), candidate);
344
+ if (!rel || rel.startsWith("..") || isAbsolute(rel)) {
345
+ return undefined;
346
+ }
347
+ if (!safeExistsSync(candidate)) {
348
+ return undefined;
349
+ }
350
+ try {
351
+ const { groupId, artifactId } = parsePom(candidate).properties || {};
352
+ if (
353
+ artifactId !== parentRef.artifactId ||
354
+ (parentRef.groupId && groupId !== parentRef.groupId)
355
+ ) {
356
+ return undefined;
357
+ }
358
+ } catch (_err) {
359
+ return undefined;
360
+ }
361
+ return candidate;
362
+ }
363
+
364
+ /**
365
+ * What a pom passes on to its children: its user properties and managed
366
+ * versions over those of its own local parents.
367
+ *
368
+ * @param {string} pomFile Pom file
369
+ * @param {string} rootDir Scanned directory
370
+ * @param {Map} cache Results by pom file
371
+ * @param {number} depth Current depth of the parent chain
372
+ * @returns {Object} Inherited properties and managed versions
373
+ */
374
+ function localPomInheritance(pomFile, rootDir, cache, depth) {
375
+ if (cache.has(pomFile)) {
376
+ return cache.get(pomFile);
377
+ }
378
+ const empty = { properties: {}, managedVersions: {} };
379
+ // A parent chain that loops back reads as having no parent
380
+ cache.set(pomFile, empty);
381
+ let inherited = empty;
382
+ try {
383
+ const parentPom =
384
+ depth < LOCAL_PARENT_DEPTH_LIMIT
385
+ ? locateLocalParentPom(pomFile, parsePom(pomFile).parent, rootDir)
386
+ : undefined;
387
+ if (parentPom) {
388
+ inherited = localPomInheritance(parentPom, rootDir, cache, depth + 1);
389
+ }
390
+ const own = parsePom(pomFile, inherited);
391
+ const result = {
392
+ properties: { ...inherited.properties, ...own.userProperties },
393
+ managedVersions: { ...inherited.managedVersions, ...own.managedVersions },
394
+ };
395
+ cache.set(pomFile, result);
396
+ return result;
397
+ } catch (_err) {
398
+ return empty;
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Parse a pom file together with the parent poms it names that are present
404
+ * inside the scanned directory, without running Maven. Properties and
405
+ * dependencyManagement versions from those parents resolve the versions the
406
+ * pom leaves to them. Parents outside the directory and imported BOMs are not
407
+ * read, so the versions they manage stay empty.
408
+ *
409
+ * @param {string} pomFile Pom file to parse
410
+ * @param {string} rootDir Scanned directory; parents outside it are ignored
411
+ * @param {Map} [cache] Reused across the poms of one scan
412
+ * @returns {Object} Same shape as parsePom
413
+ */
414
+ export function parsePomWithLocalParents(pomFile, rootDir, cache = new Map()) {
415
+ const pomPath = resolve(pomFile);
416
+ const parentPom = locateLocalParentPom(
417
+ pomPath,
418
+ parsePom(pomPath).parent,
419
+ rootDir,
420
+ );
421
+ if (!parentPom) {
422
+ return parsePom(pomFile);
423
+ }
424
+ return parsePom(pomFile, localPomInheritance(parentPom, rootDir, cache, 1));
183
425
  }
184
426
 
185
427
  /** Bound on the reactor walk. A pom's module list is untrusted input, so the