@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
@@ -1,6 +1,6 @@
1
- import { readFileSync } from "node:fs";
1
+ import { readFileSync, realpathSync, statSync } from "node:fs";
2
2
  import { arch, homedir } from "node:os";
3
- import { basename, delimiter, dirname, join } from "node:path";
3
+ import { basename, delimiter, dirname, isAbsolute, join } from "node:path";
4
4
  import process from "node:process";
5
5
 
6
6
  import { compareLoose } from "semver";
@@ -19,9 +19,12 @@ import {
19
19
  recordLedgerEvent,
20
20
  } from "../core/buildLedger.js";
21
21
  import {
22
+ BUNDLE_CMD,
22
23
  CARGO_CMD,
24
+ COMPOSER_CMD,
23
25
  DOTNET_CMD,
24
26
  GCC_CMD,
27
+ GEM_CMD,
25
28
  GO_CMD,
26
29
  getJavaCommand,
27
30
  getPythonCommand,
@@ -29,6 +32,7 @@ import {
29
32
  isDeno,
30
33
  isNode,
31
34
  NPM_CMD,
35
+ PHP_CMD,
32
36
  RUBY_CMD,
33
37
  RUSTC_CMD,
34
38
  SWIFT_CMD,
@@ -41,8 +45,20 @@ import {
41
45
  } from "../core/fs.js";
42
46
  import { diagnosticStream } from "../core/logger.js";
43
47
  import { dirNameStr, isMac, isWin } from "../core/paths.js";
48
+ import { isProjectToolingAllowed } from "../core/projectTooling.js";
49
+ import { brotliDecoderProperties, readBrotliDecoder } from "./brotliDecoder.js";
44
50
  import { isValidSdkmanVersion } from "./jvmToolEnv.js";
51
+ import { createNodeRuntimeComponent } from "./nodeBuildInfo.js";
52
+ import {
53
+ createPhpRuntimeComponent,
54
+ packageStoreOf,
55
+ parsePhpReportedVersion,
56
+ } from "./phpBuildInfo.js";
45
57
  import { applyPurl, tryBuildPurl } from "./purl.js";
58
+ import {
59
+ createRubyRuntimeComponent,
60
+ parseRubyDescription,
61
+ } from "./rubyBuildInfo.js";
46
62
  import {
47
63
  classifySpawnRestriction,
48
64
  describeSpawnRestriction,
@@ -62,6 +78,9 @@ export const GIT_COMMAND = readEnvironmentVariable("GIT_CMD") || "git";
62
78
  * - `core.fsmonitor=false` — a repo-set fsmonitor value is a command git runs.
63
79
  * - `core.hooksPath=/dev/null` — prevents any hook script from executing.
64
80
  * - `safe.bareRepository=explicit` — avoids operating on an embedded bare repo.
81
+ * - `gpg.program`, `gpg.ssh.program`, `gpg.x509.program` — `%G?` in a log
82
+ * format verifies signatures by running these, so pin them to the tools
83
+ * on PATH instead of a program named by the repo.
65
84
  * `-c` overrides take precedence over the repo-local config. Diff-producing
66
85
  * commands (`git show`/`git diff`) additionally pass `--no-ext-diff`/
67
86
  * `--no-textconv` to block `.gitattributes`-driven external diff and textconv
@@ -74,6 +93,12 @@ const GIT_HARDENING_CONFIG_ARGS = Object.freeze([
74
93
  "core.hooksPath=/dev/null",
75
94
  "-c",
76
95
  "safe.bareRepository=explicit",
96
+ "-c",
97
+ "gpg.program=gpg",
98
+ "-c",
99
+ "gpg.ssh.program=ssh-keygen",
100
+ "-c",
101
+ "gpg.x509.program=gpgsm",
77
102
  ]);
78
103
 
79
104
  /**
@@ -84,7 +109,15 @@ const GIT_HARDENING_CONFIG_ARGS = Object.freeze([
84
109
  * @returns {Object} environment for spawning git
85
110
  */
86
111
  function hardenedGitEnv() {
87
- const env = { ...process.env, GIT_TERMINAL_PROMPT: "0" };
112
+ // These commands only read the local repository. A partial clone would
113
+ // otherwise fetch missing objects through the repo's own remote and
114
+ // core.sshCommand settings, so turn lazy fetching and every transport off.
115
+ const env = {
116
+ ...process.env,
117
+ GIT_TERMINAL_PROMPT: "0",
118
+ GIT_NO_LAZY_FETCH: "1",
119
+ GIT_ALLOW_PROTOCOL: "cdxgen-no-network",
120
+ };
88
121
  if (isSecureMode) {
89
122
  env.GIT_CONFIG_NOSYSTEM = "1";
90
123
  env.GIT_CONFIG_GLOBAL = "/dev/null";
@@ -113,6 +146,11 @@ export const SDKMAN_JAVA_TOOL_ALIASES = {
113
146
  java24: readEnvironmentVariable("JAVA24_TOOL") || "24.0.2-tem",
114
147
  java25: readEnvironmentVariable("JAVA25_TOOL") || "25.0.4-tem",
115
148
  java26: readEnvironmentVariable("JAVA26_TOOL") || "26.0.2-tem",
149
+ // The broker serves no Java 27 build yet (every vendor id 404s), so java27
150
+ // has an alias only when JAVA27_TOOL names one that is downloadable.
151
+ ...(readEnvironmentVariable("JAVA27_TOOL")
152
+ ? { java27: readEnvironmentVariable("JAVA27_TOOL") }
153
+ : {}),
116
154
  };
117
155
 
118
156
  /**
@@ -337,8 +375,10 @@ function recordToolProbeResolved(tool, ecosystem, found) {
337
375
  * @param {string} tool Tool name
338
376
  * @param {string} ecosystem Ecosystem the tool belongs to
339
377
  * @param {string} command Probe command that failed
378
+ * @param {string} [detail] What to say when the command is not restricted,
379
+ * instead of the default that no command answered
340
380
  */
341
- function recordToolProbeFailure(tool, ecosystem, command) {
381
+ function recordToolProbeFailure(tool, ecosystem, command, detail) {
342
382
  if (!isLedgerEnabled()) {
343
383
  return;
344
384
  }
@@ -362,7 +402,7 @@ function recordToolProbeFailure(tool, ecosystem, command) {
362
402
  ecosystem,
363
403
  tool,
364
404
  source: "PATH",
365
- detail: `No ${tool} command answered the version probe.`,
405
+ detail: detail || `No ${tool} command answered the version probe.`,
366
406
  });
367
407
  }
368
408
  }
@@ -460,6 +500,16 @@ export function collectNodeInfo(dir) {
460
500
  version: runtimeInfo.version,
461
501
  components: runtimeInfo.components,
462
502
  };
503
+ if (runtimeInfo.runtime === "Node.js" && runtimeInfo.version) {
504
+ // The same identity a scanned image's Node.js gets, without a bom-ref, as
505
+ // for the other toolchains.
506
+ const runtime = formulationToolchain(
507
+ createNodeRuntimeComponent(runtimeInfo.version),
508
+ );
509
+ nodeInfo.purl = runtime.purl;
510
+ nodeInfo.cpe = runtime.cpe;
511
+ annotateNodeBrotli(nodeInfo.components);
512
+ }
463
513
  const moduleDesc = getCommandOutput(NPM_CMD, dir, ["--version"]);
464
514
  if (moduleDesc) {
465
515
  nodeInfo.description = `npm: ${moduleDesc}`;
@@ -470,6 +520,36 @@ export function collectNodeInfo(dir) {
470
520
  return nodeInfo;
471
521
  }
472
522
 
523
+ /**
524
+ * Record whether the Brotli decoder the running Node.js uses carries the
525
+ * CVE-2026-111497 fix. process.versions.brotli cannot say: Node.js backports
526
+ * the fix without changing it. The decoder is read where it lives, in the node
527
+ * binary or in the shared libbrotlidec a `--shared-brotli` build loads.
528
+ *
529
+ * @param {Object[]|undefined} components bundled components of the runtime
530
+ */
531
+ function annotateNodeBrotli(components) {
532
+ const brotli = (components || []).find(
533
+ (component) => component.name === "brotli",
534
+ );
535
+ if (!brotli) {
536
+ return;
537
+ }
538
+ let decoderFile = globalThis.process?.execPath;
539
+ if (nodeComponentLinkage("brotli") === "shared") {
540
+ decoderFile = (
541
+ globalThis.process?.report?.getReport?.()?.sharedObjects || []
542
+ ).find((path) => basename(path).startsWith("libbrotlidec"));
543
+ }
544
+ const decoder = decoderFile ? readBrotliDecoder(decoderFile) : undefined;
545
+ if (decoder) {
546
+ brotli.properties = [
547
+ ...(brotli.properties || []),
548
+ ...brotliDecoderProperties(decoder),
549
+ ];
550
+ }
551
+ }
552
+
473
553
  /**
474
554
  * Collect gcc version
475
555
  *
@@ -559,23 +639,333 @@ export function collectSwiftInfo(dir) {
559
639
  }
560
640
 
561
641
  /**
562
- * Collect Ruby version
642
+ * The real path of the executable a command name runs, looked up on PATH as
643
+ * the shell would, or undefined when it cannot be found.
644
+ *
645
+ * @param {string} command command name or path
646
+ * @returns {string|undefined}
647
+ */
648
+ function resolveExecutable(command) {
649
+ const name = `${command || ""}`.trim().split(" ")[0];
650
+ if (!name) {
651
+ return undefined;
652
+ }
653
+ const extensions = isWin
654
+ ? ["", ...`${process.env.PATHEXT || ".EXE;.CMD;.BAT"}`.split(";")]
655
+ : [""];
656
+ const directories = isAbsolute(name)
657
+ ? [dirname(name)]
658
+ : `${process.env.PATH || ""}`.split(delimiter).filter(Boolean);
659
+ const base = isAbsolute(name) ? basename(name) : name;
660
+ for (const directory of directories) {
661
+ for (const extension of extensions) {
662
+ const candidate = join(directory, `${base}${extension}`);
663
+ try {
664
+ if (statSync(candidate).isFile()) {
665
+ return realpathSync(candidate);
666
+ }
667
+ } catch {
668
+ // not here
669
+ }
670
+ }
671
+ }
672
+ return undefined;
673
+ }
674
+
675
+ /**
676
+ * Whether a Ruby executable is one the operating system or a package manager
677
+ * that patches it owns. Distros backport fixes under the old upstream version,
678
+ * so its version must not be matched against upstream advisories by CPE.
679
+ *
680
+ * @param {string|undefined} realPath real path of the executable
681
+ * @returns {boolean}
682
+ */
683
+ export function isPackagedRubyPath(realPath) {
684
+ if (!realPath || isWin) {
685
+ return false;
686
+ }
687
+ // Homebrew builds the upstream release, so it keeps the CPE, as it does in
688
+ // an OBOM; Nix and Guix patch theirs.
689
+ const store = packageStoreOf(realPath);
690
+ if (store) {
691
+ return store !== "homebrew";
692
+ }
693
+ return ["/usr/bin/", "/bin/", "/usr/lib/", "/usr/libexec/", "/System/"].some(
694
+ (prefix) => realPath.startsWith(prefix),
695
+ );
696
+ }
697
+
698
+ // The shims of rbenv, asdf and mise run whichever Ruby is selected, which can
699
+ // be the distro's (`rbenv global system`).
700
+ function isVersionManagerShim(realPath) {
701
+ return `${realPath}`.split(/[\\/]/).includes("shims");
702
+ }
703
+
704
+ // Whether the Ruby `ruby --version` described may carry its release's CPE.
705
+ // A shim is followed to the interpreter it runs; one that cannot be followed
706
+ // keeps no CPE, since it may be the distro's.
707
+ function rubyKeepsCpe(dir) {
708
+ let executable = resolveExecutable(RUBY_CMD);
709
+ if (executable && isVersionManagerShim(executable)) {
710
+ const target = isWin
711
+ ? ""
712
+ : firstOutputLine(
713
+ getCommandOutput(RUBY_CMD, dir, ["-e", "print RbConfig.ruby"]),
714
+ );
715
+ if (!isAbsolute(target)) {
716
+ return false;
717
+ }
718
+ try {
719
+ executable = realpathSync(target);
720
+ } catch {
721
+ executable = target;
722
+ }
723
+ }
724
+ return !isPackagedRubyPath(executable);
725
+ }
726
+
727
+ /**
728
+ * Collect the Ruby on the PATH.
729
+ *
730
+ * `ruby --version` prints the interpreter's description, such as
731
+ * `ruby 3.3.6 (2024-11-05 revision 75015d4c1f) [x86_64-linux]`, which is kept
732
+ * as the component description. The identity is `pkg:generic/ruby@3.3.6`
733
+ * (or jruby, truffleruby), with the CPE of the release unless a package
734
+ * manager owns the executable: a distro backports fixes under the old upstream
735
+ * version.
563
736
  *
564
737
  * @param {string} dir Working directory
565
738
  * @returns Object containing Ruby details
566
739
  */
567
740
  export function collectRubyInfo(dir) {
568
741
  const versionDesc = getCommandOutput(RUBY_CMD, dir, ["--version"]);
569
- if (versionDesc) {
570
- const info = {
571
- type: "platform",
572
- name: "ruby",
573
- version: versionDesc.trim(),
574
- };
575
- recordToolProbeResolved("ruby", "ruby", info.version);
742
+ const banner = firstOutputLine(versionDesc);
743
+ const parsed = parseRubyDescription(banner);
744
+ if (parsed) {
745
+ const info = formulationToolchain(
746
+ createRubyRuntimeComponent(parsed.version, {
747
+ engine: parsed.engine,
748
+ withCpe: rubyKeepsCpe(dir),
749
+ }),
750
+ );
751
+ info.description = banner;
752
+ const properties = [];
753
+ if (parsed.patchlevel !== undefined) {
754
+ properties.push({
755
+ name: "cdx:ruby:patchlevel",
756
+ value: parsed.patchlevel,
757
+ });
758
+ }
759
+ if (parsed.rubyVersion) {
760
+ properties.push({
761
+ name: "cdx:ruby:languageVersion",
762
+ value: parsed.rubyVersion,
763
+ });
764
+ }
765
+ if (parsed.platform) {
766
+ properties.push({ name: "cdx:ruby:platform", value: parsed.platform });
767
+ }
768
+ if (properties.length) {
769
+ info.properties = properties;
770
+ }
771
+ recordToolProbeResolved("ruby", "ruby", banner);
576
772
  return info;
577
773
  }
578
- recordToolProbeFailure("ruby", "ruby", RUBY_CMD);
774
+ recordToolProbeFailure(
775
+ "ruby",
776
+ "ruby",
777
+ RUBY_CMD,
778
+ versionDesc
779
+ ? "The ruby version probe did not print a Ruby version banner."
780
+ : undefined,
781
+ );
782
+ return undefined;
783
+ }
784
+
785
+ /**
786
+ * Whether the Ruby collectRubyInfo found is the MRI release a project needs.
787
+ *
788
+ * @param {Object|undefined} info result of collectRubyInfo
789
+ * @param {string} version wanted MRI version
790
+ * @returns {boolean}
791
+ */
792
+ export function isRubyVersionActive(info, version) {
793
+ return Boolean(info && info.name === "ruby" && info.version === version);
794
+ }
795
+
796
+ /**
797
+ * Collect the RubyGems version. `gem --version` prints the bare version.
798
+ *
799
+ * @param {string} dir Working directory
800
+ * @returns {Object|undefined} the RubyGems component
801
+ */
802
+ export function collectRubyGemsInfo(dir) {
803
+ const token = firstOutputLine(getCommandOutput(GEM_CMD, dir, ["--version"]));
804
+ if (/^\d+\.\d+\.\d+$/.test(token)) {
805
+ recordToolProbeResolved("gem", "ruby", token);
806
+ return formulationToolchain(
807
+ applyPurl(
808
+ {
809
+ type: "platform",
810
+ name: "rubygems",
811
+ version: token,
812
+ cpe: `cpe:2.3:a:rubygems:rubygems:${token}:*:*:*:*:*:*:*`,
813
+ },
814
+ tryBuildPurl({ type: "generic", name: "rubygems", version: token }),
815
+ ),
816
+ );
817
+ }
818
+ recordToolProbeFailure("gem", "ruby", GEM_CMD);
819
+ return undefined;
820
+ }
821
+
822
+ /**
823
+ * Collect the Bundler version. `bundle --version` prints `Bundler version
824
+ * 2.5.22`, or the bare version from Bundler 2.6 on.
825
+ *
826
+ * @param {string} dir Working directory
827
+ * @returns {Object|undefined} the Bundler component
828
+ */
829
+ export function collectBundlerInfo(dir) {
830
+ const line = firstOutputLine(
831
+ getCommandOutput(BUNDLE_CMD, dir, ["--version"]),
832
+ );
833
+ const token = line.startsWith("Bundler version ")
834
+ ? line.slice("Bundler version ".length).trim()
835
+ : line;
836
+ if (/^\d+\.\d+\.\d+$/.test(token)) {
837
+ recordToolProbeResolved("bundler", "ruby", token);
838
+ return formulationToolchain(
839
+ applyPurl(
840
+ {
841
+ type: "platform",
842
+ name: "bundler",
843
+ version: token,
844
+ cpe: `cpe:2.3:a:bundler:bundler:${token}:*:*:*:*:*:*:*`,
845
+ },
846
+ tryBuildPurl({ type: "gem", name: "bundler", version: token }),
847
+ ),
848
+ );
849
+ }
850
+ recordToolProbeFailure("bundler", "ruby", BUNDLE_CMD);
851
+ return undefined;
852
+ }
853
+
854
+ /**
855
+ * The first line of a command's output, without its line end.
856
+ *
857
+ * @param {string|undefined} output Output of getCommandOutput
858
+ * @returns {string} First line, or an empty string
859
+ */
860
+ function firstOutputLine(output) {
861
+ return `${output || ""}`.split(/\r?\n/, 1)[0].trim();
862
+ }
863
+
864
+ /**
865
+ * A toolchain component for the formulation section. Formulation shares the
866
+ * document's bom-ref namespace with the inventory, which can hold the very same
867
+ * `pkg:generic/php@<version>` runtime (an image scanned with
868
+ * --include-formulation), so like the other toolchains it carries no bom-ref.
869
+ *
870
+ * @param {Object} component component built with applyPurl
871
+ * @returns {Object} the component without its bom-ref
872
+ */
873
+ function formulationToolchain(component) {
874
+ delete component["bom-ref"];
875
+ return component;
876
+ }
877
+
878
+ /**
879
+ * Collect the PHP version of the PHP found on the PATH.
880
+ *
881
+ * `-n` stops PHP loading any php.ini, so a PHPRC or an auto_prepend_file cannot
882
+ * run code, and `-v` is used rather than `-r 'echo PHP_VERSION;'` because the
883
+ * shell on Windows does not quote arguments. The first line is
884
+ * `PHP 8.5.11 (cli) (built: Sep 22 2026 13:32:06) (NTS)`.
885
+ *
886
+ * The identity is `pkg:generic/php`: a probe of the PATH cannot tell which
887
+ * package manager, if any, installed this PHP. A banner with a distro revision,
888
+ * such as Ubuntu 22.04's `PHP 8.1.2-1ubuntu2.14`, is recorded as reported but
889
+ * without a CPE: the distro backports fixes under the old upstream number.
890
+ *
891
+ * @param {string} dir Working directory
892
+ * @returns Object containing the PHP details
893
+ */
894
+ export function collectPhpInfo(dir) {
895
+ const output = getCommandOutput(PHP_CMD, dir, ["-n", "-v"]);
896
+ const banner = firstOutputLine(output);
897
+ const reported = banner.startsWith("PHP ")
898
+ ? parsePhpReportedVersion(banner.slice("PHP ".length))
899
+ : undefined;
900
+ if (reported) {
901
+ recordToolProbeResolved("php", "php", reported.version);
902
+ return formulationToolchain(
903
+ createPhpRuntimeComponent(reported.version, {
904
+ withCpe: !reported.distroRevision,
905
+ }),
906
+ );
907
+ }
908
+ recordToolProbeFailure(
909
+ "php",
910
+ "php",
911
+ PHP_CMD,
912
+ output
913
+ ? "The php version probe did not print a PHP version banner."
914
+ : undefined,
915
+ );
916
+ return undefined;
917
+ }
918
+
919
+ /**
920
+ * Collect the Composer version.
921
+ *
922
+ * `--no-plugins` and `--no-scripts` keep the plugins and scripts of the
923
+ * project in the working directory from running, which `composer --version`
924
+ * would otherwise let a plugin do. It needs no network. The banner is
925
+ * `Composer version 2.10.3 2026-08-27 13:34:23`, but an older Composer on a
926
+ * newer PHP prints deprecation notices above it, so the first line that starts
927
+ * with it is read; the line after it names the PHP Composer runs on, with its
928
+ * path, and is never read. The banner must start its line: an indented echo of
929
+ * the project's composer.json in an error block does not count. Only a stable
930
+ * release is recorded: a pre-release or a snapshot has no version a CPE can
931
+ * name.
932
+ *
933
+ * @param {string} dir Working directory
934
+ * @returns Object containing the Composer details
935
+ */
936
+ export function collectComposerInfo(dir) {
937
+ const output = getCommandOutput(COMPOSER_CMD, dir, [
938
+ "--version",
939
+ "--no-plugins",
940
+ "--no-scripts",
941
+ "--no-interaction",
942
+ ]);
943
+ const token =
944
+ /^Composer version (\S+)/m.exec(
945
+ `${output || ""}`.slice(0, 64 * 1024),
946
+ )?.[1] || "";
947
+ if (/^\d+\.\d+\.\d+$/.test(token)) {
948
+ recordToolProbeResolved("composer", "php", token);
949
+ return formulationToolchain(
950
+ applyPurl(
951
+ {
952
+ type: "platform",
953
+ name: "composer",
954
+ version: token,
955
+ cpe: `cpe:2.3:a:getcomposer:composer:${token}:*:*:*:*:*:*:*`,
956
+ },
957
+ tryBuildPurl({ type: "generic", name: "composer", version: token }),
958
+ ),
959
+ );
960
+ }
961
+ recordToolProbeFailure(
962
+ "composer",
963
+ "php",
964
+ COMPOSER_CMD,
965
+ output
966
+ ? "The composer version probe did not print a stable Composer version."
967
+ : undefined,
968
+ );
579
969
  return undefined;
580
970
  }
581
971
 
@@ -594,12 +984,23 @@ export function runSwiftCommand(dir, args) {
594
984
  * Collect toolchain information components for the current environment.
595
985
  *
596
986
  * Probes the working directory for java, dotnet, python, node, gcc, rust, go,
597
- * and ruby toolchains and returns one component per detected tool.
987
+ * ruby, php and composer toolchains and returns one component per detected
988
+ * tool.
598
989
  *
599
990
  * @param {string} dir Working directory to inspect
991
+ * @param {Object} [options] CLI options, which decide whether the project's
992
+ * own toolchain files may choose the tools that run
600
993
  * @returns {Object[]} Array of toolchain component objects; empty when nothing is detected
601
994
  */
602
- export function collectEnvInfo(dir) {
995
+ export function collectEnvInfo(dir, options = undefined) {
996
+ // Version managers pick the toolchain from files in the working directory.
997
+ // rust-toolchain.toml can name an absolute toolchain path and global.json
998
+ // can name SDK paths, so unless the project's tooling may run, the probes
999
+ // run outside the scanned project and report the toolchains installed on
1000
+ // the host.
1001
+ if (!isProjectToolingAllowed(options)) {
1002
+ dir = getTmpDir();
1003
+ }
603
1004
  const infoComponents = [];
604
1005
  let cmp = collectJavaInfo(dir);
605
1006
  if (cmp) {
@@ -630,6 +1031,22 @@ export function collectEnvInfo(dir) {
630
1031
  infoComponents.push(cmp);
631
1032
  }
632
1033
  cmp = collectRubyInfo(dir);
1034
+ if (cmp) {
1035
+ infoComponents.push(cmp);
1036
+ cmp = collectRubyGemsInfo(dir);
1037
+ if (cmp) {
1038
+ infoComponents.push(cmp);
1039
+ }
1040
+ cmp = collectBundlerInfo(dir);
1041
+ if (cmp) {
1042
+ infoComponents.push(cmp);
1043
+ }
1044
+ }
1045
+ cmp = collectPhpInfo(dir);
1046
+ if (cmp) {
1047
+ infoComponents.push(cmp);
1048
+ }
1049
+ cmp = collectComposerInfo(dir);
633
1050
  if (cmp) {
634
1051
  infoComponents.push(cmp);
635
1052
  }
@@ -1055,7 +1472,7 @@ export function installRubyVersion(rubyVersion, filePath) {
1055
1472
  return { fullToolBinDir: undefined, status: false };
1056
1473
  }
1057
1474
  const existingRuby = collectRubyInfo(filePath);
1058
- if (existingRuby?.version?.startsWith(`ruby ${rubyVersion} `)) {
1475
+ if (isRubyVersionActive(existingRuby, rubyVersion)) {
1059
1476
  return { fullToolBinDir: undefined, status: true };
1060
1477
  }
1061
1478
  const fullToolBinDir = rubyVersionDir(rubyVersion);
@@ -1821,6 +2238,36 @@ export function gitRevertsAndHotfixes(dir, maxCount = 20) {
1821
2238
  return entries;
1822
2239
  }
1823
2240
 
2241
+ // process.versions keys whose configure flag is named differently.
2242
+ const NODE_SHARED_FLAG_NAMES = {
2243
+ ares: "cares",
2244
+ libffi: "ffi",
2245
+ llhttp: "http_parser",
2246
+ uv: "libuv",
2247
+ };
2248
+
2249
+ /**
2250
+ * Whether the running Node.js links a bundled dependency as a shared library,
2251
+ * from the `node_shared_<name>` configure variables it reports.
2252
+ *
2253
+ * @param {string} name key of process.versions
2254
+ * @param {Object} [variables] configure variables, process.config.variables
2255
+ * by default
2256
+ * @returns {string|undefined} `shared`, `bundled`, or undefined when the build
2257
+ * does not say
2258
+ */
2259
+ export function nodeComponentLinkage(
2260
+ name,
2261
+ variables = globalThis.process?.config?.variables,
2262
+ ) {
2263
+ const flag = `node_shared_${NODE_SHARED_FLAG_NAMES[name] || name}`;
2264
+ if (!variables || !(flag in variables)) {
2265
+ return undefined;
2266
+ }
2267
+ const value = variables[flag];
2268
+ return value === true || value === "true" ? "shared" : "bundled";
2269
+ }
2270
+
1824
2271
  /**
1825
2272
  * Get information about the runtime.
1826
2273
  *
@@ -1853,12 +2300,22 @@ export function getRuntimeInformation() {
1853
2300
  name,
1854
2301
  version: version || null,
1855
2302
  });
2303
+ // A distro or Homebrew build links some of these as shared libraries
2304
+ // (`configure --shared-brotli`); the version is then the shared
2305
+ // library's, and updating Node.js alone does not update it.
2306
+ const linkage = nodeComponentLinkage(name);
1856
2307
  const apkg = {
1857
2308
  name,
1858
2309
  version,
1859
- description: `Bundled with Node.js ${runtimeInfo.version}`,
2310
+ description:
2311
+ linkage === "shared"
2312
+ ? `Linked by Node.js ${runtimeInfo.version} as a shared library`
2313
+ : `Bundled with Node.js ${runtimeInfo.version}`,
1860
2314
  type: "library",
1861
2315
  scope: "excluded",
2316
+ ...(linkage
2317
+ ? { properties: [{ name: "cdx:node:linkage", value: linkage }] }
2318
+ : {}),
1862
2319
  };
1863
2320
  applyPurl(apkg, bundledPurl);
1864
2321
  if (nodeSourceUrl) {