@cdxgen/cdxgen 13.5.0 → 13.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (380) hide show
  1. package/README.md +17 -10
  2. package/bin/audit.js +7 -4
  3. package/bin/cdxgen.js +260 -37
  4. package/bin/convert.js +146 -22
  5. package/bin/evinse.js +28 -3
  6. package/bin/repl.js +47 -3
  7. package/bin/sign.js +211 -34
  8. package/bin/tracebom.js +18 -6
  9. package/bin/validate.js +24 -3
  10. package/bin/verify.js +113 -5
  11. package/data/behavior-taxonomy.schema.json +1491 -0
  12. package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
  13. package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
  14. package/data/license-aliases.json +24 -2
  15. package/data/pqc-migration.json +239 -0
  16. package/data/remediations.json +176 -0
  17. package/data/rules/ai-bom.yaml +1 -1
  18. package/data/rules/brotli-decoder.yaml +107 -0
  19. package/data/rules/cbom-security.yaml +4 -3
  20. package/data/rules/package-integrity.yaml +28 -0
  21. package/data/rules/vscode-extensions.yaml +75 -0
  22. package/data/spdx-license-list.json +121 -7
  23. package/data/spdx-licenses.json +69 -0
  24. package/data/spdx.schema.json +845 -2
  25. package/lib/audit/index.js +25 -3
  26. package/lib/cli/bomAssembly.js +172 -4
  27. package/lib/cli/cliOptions.js +121 -17
  28. package/lib/cli/index.js +720 -902
  29. package/lib/cli/jsBom.js +156 -64
  30. package/lib/cli/jvmBom.js +602 -86
  31. package/lib/cli/managedBom.js +161 -19
  32. package/lib/cli/nativeBom.js +52 -5
  33. package/lib/core/boundedFile.js +344 -0
  34. package/lib/core/brotliProbe.js +182 -0
  35. package/lib/core/env.js +34 -0
  36. package/lib/core/fs.js +138 -3
  37. package/lib/core/globPatterns.js +218 -0
  38. package/lib/core/httpClient.js +98 -1
  39. package/lib/core/lockPaths.js +38 -0
  40. package/lib/core/paths.js +24 -1
  41. package/lib/core/processTree.js +100 -8
  42. package/lib/core/projectConfig.js +332 -25
  43. package/lib/core/toolRegistry.js +184 -0
  44. package/lib/ecosystems/cppEvidence.js +263 -106
  45. package/lib/ecosystems/cppIncludeResolver.js +16 -5
  46. package/lib/ecosystems/denoutils.js +2 -2
  47. package/lib/ecosystems/ecosystems.js +25 -10
  48. package/lib/ecosystems/gemHostProbe.js +301 -0
  49. package/lib/ecosystems/golem.js +3 -8
  50. package/lib/ecosystems/gradleutils.js +1909 -229
  51. package/lib/ecosystems/jsEvidence.js +90 -38
  52. package/lib/ecosystems/kosi.js +3 -5
  53. package/lib/ecosystems/npmVersionResolution.js +376 -0
  54. package/lib/ecosystems/parsers-bazel.js +107 -6
  55. package/lib/ecosystems/parsers-js.js +505 -22
  56. package/lib/ecosystems/parsers-misc.js +209 -20
  57. package/lib/ecosystems/parsers-python.js +178 -30
  58. package/lib/ecosystems/parsers-rust.js +117 -15
  59. package/lib/ecosystems/pythonutils.js +128 -2
  60. package/lib/ecosystems/remote/huggingface.js +22 -0
  61. package/lib/ecosystems/rubyutils.js +538 -31
  62. package/lib/ecosystems/rusi.js +2 -4
  63. package/lib/ecosystems/scalacliutils.js +2 -0
  64. package/lib/ecosystems/scalasem.js +798 -152
  65. package/lib/ecosystems/vendoredCode.js +237 -3
  66. package/lib/ecosystems/venvProbe.js +360 -0
  67. package/lib/evinser/evinser.js +536 -71
  68. package/lib/evinser/scalasem.js +5 -1
  69. package/lib/helpers/agenticToolutils.js +525 -94
  70. package/lib/helpers/bomSigner.js +43 -9
  71. package/lib/helpers/jssSigner.js +1276 -0
  72. package/lib/helpers/vsixutils.js +1574 -315
  73. package/lib/inventory/agentBlueprint.js +193 -0
  74. package/lib/inventory/agentToolNames.js +145 -0
  75. package/lib/inventory/aiCollector.js +153 -7
  76. package/lib/inventory/aiInventory.js +44 -8
  77. package/lib/inventory/aiProviders.js +324 -0
  78. package/lib/inventory/atomUtils.js +77 -0
  79. package/lib/inventory/bomUtils.js +8 -1
  80. package/lib/inventory/brotliDecoder.js +142 -0
  81. package/lib/inventory/buildToolRateLimit.js +1 -1
  82. package/lib/inventory/cbomutils.js +56 -7
  83. package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
  84. package/lib/inventory/citations.js +31 -2
  85. package/lib/inventory/communityAiConfigParser.js +978 -80
  86. package/lib/inventory/cyclonedx20.js +152 -0
  87. package/lib/inventory/cyclonedx20Common.js +286 -0
  88. package/lib/inventory/cyclonedx20Derived.js +159 -0
  89. package/lib/inventory/cyclonedx20Parties.js +552 -0
  90. package/lib/inventory/cyclonedx20Read.js +768 -0
  91. package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
  92. package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
  93. package/lib/inventory/cyclonedx20ReadContext.js +380 -0
  94. package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
  95. package/lib/inventory/cyclonedx20ReadSections.js +599 -0
  96. package/lib/inventory/cyclonedx20Render.js +766 -0
  97. package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
  98. package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
  99. package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
  100. package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
  101. package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
  102. package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
  103. package/lib/inventory/cyclonedx20RenderSections.js +499 -0
  104. package/lib/inventory/cyclonedx20Tables.js +594 -0
  105. package/lib/inventory/deps.js +141 -13
  106. package/lib/inventory/dosai.js +375 -6
  107. package/lib/inventory/dosaiParsers.js +3 -6
  108. package/lib/inventory/dotnetRuntime.js +367 -0
  109. package/lib/inventory/ecosystemFormulationParsers.js +979 -0
  110. package/lib/inventory/envcontext.js +436 -48
  111. package/lib/inventory/evidenceUtils.js +13 -1
  112. package/lib/inventory/fetchBatch.js +1 -1
  113. package/lib/inventory/formulationParsers.js +124 -14
  114. package/lib/inventory/gtfobins.js +13 -0
  115. package/lib/inventory/hbom.js +45 -6
  116. package/lib/inventory/hbomLoader.js +166 -0
  117. package/lib/inventory/hostRuntimeProbe.js +686 -0
  118. package/lib/inventory/jvmLocalRepos.js +9 -2
  119. package/lib/inventory/jvmToolEnv.js +29 -0
  120. package/lib/inventory/licenseEnhancer.js +12 -0
  121. package/lib/inventory/mavenCentralMirror.js +534 -0
  122. package/lib/inventory/mcpConfigParser.js +5 -0
  123. package/lib/inventory/nodeBuildInfo.js +218 -0
  124. package/lib/inventory/nodeHostProbe.js +202 -0
  125. package/lib/inventory/occurrenceKey.js +90 -0
  126. package/lib/inventory/osPackageResolver.js +182 -0
  127. package/lib/inventory/phpBuildInfo.js +10 -59
  128. package/lib/inventory/phpHostProbe.js +32 -488
  129. package/lib/inventory/pqcMigration.js +426 -0
  130. package/lib/inventory/rubyBuildInfo.js +972 -0
  131. package/lib/inventory/rubyHostProbe.js +218 -0
  132. package/lib/inventory/rustFormulationParser.js +21 -10
  133. package/lib/inventory/scalaCoords.js +4 -3
  134. package/lib/inventory/scalaDedupe.js +440 -0
  135. package/lib/inventory/source.js +114 -8
  136. package/lib/managers/binary.js +337 -3
  137. package/lib/managers/docker.js +79 -0
  138. package/lib/parsers/huggingfaceManifest.js +9 -1
  139. package/lib/parsers/huggingfaceUtils.js +18 -0
  140. package/lib/parsers/kiroPermissions.js +166 -0
  141. package/lib/parsers/largeJson.js +28 -11
  142. package/lib/parsers/vscodeExtensionId.js +183 -0
  143. package/lib/server/server.js +48 -35
  144. package/lib/stages/postgen/postgen.js +41 -1
  145. package/lib/stages/postgen/ruleEngine.js +13 -5
  146. package/lib/stages/postgen/specVersionCompat.js +76 -0
  147. package/lib/stages/pregen/envAudit.js +1 -0
  148. package/lib/stages/pregen/pregen.js +114 -57
  149. package/lib/validator/bomValidator.js +14 -2
  150. package/lib/validator/complianceEngine.js +30 -3
  151. package/lib/validator/complianceRules.js +1416 -4
  152. package/lib/validator/generated/validate-1.6.mjs +1 -1
  153. package/lib/validator/generated/validate-1.7.mjs +1 -1
  154. package/lib/validator/generated/validate-2.0.mjs +57554 -13851
  155. package/lib/validator/index.js +49 -8
  156. package/lib/validator/reporters/json.js +6 -0
  157. package/package.json +11 -10
  158. package/types/bin/repl.d.ts.map +1 -1
  159. package/types/lib/audit/index.d.ts +6 -2
  160. package/types/lib/audit/index.d.ts.map +1 -1
  161. package/types/lib/cli/bomAssembly.d.ts +29 -1
  162. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  163. package/types/lib/cli/cliOptions.d.ts +6 -0
  164. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  165. package/types/lib/cli/index.d.ts.map +1 -1
  166. package/types/lib/cli/jsBom.d.ts +3 -18
  167. package/types/lib/cli/jsBom.d.ts.map +1 -1
  168. package/types/lib/cli/jvmBom.d.ts +15 -0
  169. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  170. package/types/lib/cli/managedBom.d.ts.map +1 -1
  171. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  172. package/types/lib/core/boundedFile.d.ts +100 -0
  173. package/types/lib/core/boundedFile.d.ts.map +1 -0
  174. package/types/lib/core/brotliProbe.d.ts +37 -0
  175. package/types/lib/core/brotliProbe.d.ts.map +1 -0
  176. package/types/lib/core/env.d.ts +14 -0
  177. package/types/lib/core/env.d.ts.map +1 -1
  178. package/types/lib/core/fs.d.ts +4 -0
  179. package/types/lib/core/fs.d.ts.map +1 -1
  180. package/types/lib/core/globPatterns.d.ts +70 -0
  181. package/types/lib/core/globPatterns.d.ts.map +1 -0
  182. package/types/lib/core/httpClient.d.ts +24 -0
  183. package/types/lib/core/httpClient.d.ts.map +1 -1
  184. package/types/lib/core/lockPaths.d.ts +19 -0
  185. package/types/lib/core/lockPaths.d.ts.map +1 -0
  186. package/types/lib/core/paths.d.ts +9 -0
  187. package/types/lib/core/paths.d.ts.map +1 -1
  188. package/types/lib/core/processTree.d.ts +17 -4
  189. package/types/lib/core/processTree.d.ts.map +1 -1
  190. package/types/lib/core/projectConfig.d.ts +47 -8
  191. package/types/lib/core/projectConfig.d.ts.map +1 -1
  192. package/types/lib/core/toolRegistry.d.ts +36 -0
  193. package/types/lib/core/toolRegistry.d.ts.map +1 -0
  194. package/types/lib/ecosystems/cppEvidence.d.ts +40 -13
  195. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  196. package/types/lib/ecosystems/cppIncludeResolver.d.ts +2 -1
  197. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -1
  198. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  199. package/types/lib/ecosystems/gemHostProbe.d.ts +28 -0
  200. package/types/lib/ecosystems/gemHostProbe.d.ts.map +1 -0
  201. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  202. package/types/lib/ecosystems/gradleutils.d.ts +294 -20
  203. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  204. package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
  205. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  206. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  207. package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
  208. package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
  209. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -1
  210. package/types/lib/ecosystems/parsers-js.d.ts +17 -20
  211. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  212. package/types/lib/ecosystems/parsers-misc.d.ts +19 -0
  213. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  214. package/types/lib/ecosystems/parsers-python.d.ts +10 -0
  215. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  216. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  217. package/types/lib/ecosystems/pythonutils.d.ts +19 -1
  218. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  219. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  220. package/types/lib/ecosystems/rubyutils.d.ts +144 -12
  221. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  222. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  223. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
  224. package/types/lib/ecosystems/scalasem.d.ts +58 -16
  225. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  226. package/types/lib/ecosystems/vendoredCode.d.ts +26 -0
  227. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -1
  228. package/types/lib/ecosystems/venvProbe.d.ts +40 -0
  229. package/types/lib/ecosystems/venvProbe.d.ts.map +1 -0
  230. package/types/lib/evinser/evinser.d.ts +120 -13
  231. package/types/lib/evinser/evinser.d.ts.map +1 -1
  232. package/types/lib/evinser/scalasem.d.ts.map +1 -1
  233. package/types/lib/helpers/agenticToolutils.d.ts +53 -19
  234. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
  235. package/types/lib/helpers/bomSigner.d.ts +20 -3
  236. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  237. package/types/lib/helpers/jssSigner.d.ts +288 -0
  238. package/types/lib/helpers/jssSigner.d.ts.map +1 -0
  239. package/types/lib/helpers/vsixutils.d.ts +303 -19
  240. package/types/lib/helpers/vsixutils.d.ts.map +1 -1
  241. package/types/lib/inventory/agentBlueprint.d.ts +70 -0
  242. package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
  243. package/types/lib/inventory/agentToolNames.d.ts +51 -0
  244. package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
  245. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  246. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  247. package/types/lib/inventory/aiProviders.d.ts +85 -0
  248. package/types/lib/inventory/aiProviders.d.ts.map +1 -0
  249. package/types/lib/inventory/atomUtils.d.ts +19 -0
  250. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  251. package/types/lib/inventory/bomUtils.d.ts.map +1 -1
  252. package/types/lib/inventory/brotliDecoder.d.ts +58 -0
  253. package/types/lib/inventory/brotliDecoder.d.ts.map +1 -0
  254. package/types/lib/inventory/cbomutils.d.ts +17 -0
  255. package/types/lib/inventory/cbomutils.d.ts.map +1 -1
  256. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
  257. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
  258. package/types/lib/inventory/citations.d.ts +5 -0
  259. package/types/lib/inventory/citations.d.ts.map +1 -1
  260. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  261. package/types/lib/inventory/cyclonedx20.d.ts +99 -0
  262. package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
  263. package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
  264. package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
  265. package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
  266. package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
  267. package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
  268. package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
  269. package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
  270. package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
  271. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
  272. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
  273. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
  274. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
  275. package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
  276. package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
  277. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
  278. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
  279. package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
  280. package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
  281. package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
  282. package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
  283. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
  284. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
  285. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
  286. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
  287. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
  288. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
  289. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
  290. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
  291. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
  292. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
  293. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
  294. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
  295. package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
  296. package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
  297. package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
  298. package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
  299. package/types/lib/inventory/deps.d.ts +35 -2
  300. package/types/lib/inventory/deps.d.ts.map +1 -1
  301. package/types/lib/inventory/dosai.d.ts +36 -1
  302. package/types/lib/inventory/dosai.d.ts.map +1 -1
  303. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  304. package/types/lib/inventory/dotnetRuntime.d.ts +91 -0
  305. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -0
  306. package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
  307. package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
  308. package/types/lib/inventory/envcontext.d.ts +74 -12
  309. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  310. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  311. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  312. package/types/lib/inventory/gtfobins.d.ts.map +1 -1
  313. package/types/lib/inventory/hbom.d.ts +11 -0
  314. package/types/lib/inventory/hbom.d.ts.map +1 -1
  315. package/types/lib/inventory/hbomLoader.d.ts +54 -0
  316. package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
  317. package/types/lib/inventory/hostRuntimeProbe.d.ts +44 -0
  318. package/types/lib/inventory/hostRuntimeProbe.d.ts.map +1 -0
  319. package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
  320. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
  321. package/types/lib/inventory/jvmToolEnv.d.ts +12 -0
  322. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
  323. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -1
  324. package/types/lib/inventory/mavenCentralMirror.d.ts +170 -0
  325. package/types/lib/inventory/mavenCentralMirror.d.ts.map +1 -0
  326. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  327. package/types/lib/inventory/nodeBuildInfo.d.ts +95 -0
  328. package/types/lib/inventory/nodeBuildInfo.d.ts.map +1 -0
  329. package/types/lib/inventory/nodeHostProbe.d.ts +15 -0
  330. package/types/lib/inventory/nodeHostProbe.d.ts.map +1 -0
  331. package/types/lib/inventory/occurrenceKey.d.ts +52 -0
  332. package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
  333. package/types/lib/inventory/osPackageResolver.d.ts +38 -0
  334. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  335. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -1
  336. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -1
  337. package/types/lib/inventory/pqcMigration.d.ts +84 -0
  338. package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
  339. package/types/lib/inventory/rubyBuildInfo.d.ts +271 -0
  340. package/types/lib/inventory/rubyBuildInfo.d.ts.map +1 -0
  341. package/types/lib/inventory/rubyHostProbe.d.ts +47 -0
  342. package/types/lib/inventory/rubyHostProbe.d.ts.map +1 -0
  343. package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
  344. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  345. package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
  346. package/types/lib/inventory/scalaDedupe.d.ts +43 -0
  347. package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
  348. package/types/lib/inventory/source.d.ts +25 -0
  349. package/types/lib/inventory/source.d.ts.map +1 -1
  350. package/types/lib/managers/binary.d.ts.map +1 -1
  351. package/types/lib/managers/docker.d.ts +12 -0
  352. package/types/lib/managers/docker.d.ts.map +1 -1
  353. package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
  354. package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
  355. package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
  356. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  357. package/types/lib/parsers/kiroPermissions.d.ts +62 -0
  358. package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
  359. package/types/lib/parsers/largeJson.d.ts +4 -3
  360. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  361. package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
  362. package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
  363. package/types/lib/server/server.d.ts.map +1 -1
  364. package/types/lib/stages/postgen/postgen.d.ts +7 -0
  365. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  366. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  367. package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
  368. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  369. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  370. package/types/lib/stages/pregen/pregen.d.ts +12 -8
  371. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  372. package/types/lib/validator/bomValidator.d.ts +5 -1
  373. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  374. package/types/lib/validator/complianceEngine.d.ts +4 -4
  375. package/types/lib/validator/complianceEngine.d.ts.map +1 -1
  376. package/types/lib/validator/complianceRules.d.ts +14 -2
  377. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  378. package/types/lib/validator/index.d.ts +10 -0
  379. package/types/lib/validator/index.d.ts.map +1 -1
  380. package/types/lib/validator/reporters/json.d.ts.map +1 -1
@@ -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,10 +19,12 @@ import {
19
19
  recordLedgerEvent,
20
20
  } from "../core/buildLedger.js";
21
21
  import {
22
+ BUNDLE_CMD,
22
23
  CARGO_CMD,
23
24
  COMPOSER_CMD,
24
25
  DOTNET_CMD,
25
26
  GCC_CMD,
27
+ GEM_CMD,
26
28
  GO_CMD,
27
29
  getJavaCommand,
28
30
  getPythonCommand,
@@ -44,12 +46,23 @@ import {
44
46
  import { diagnosticStream } from "../core/logger.js";
45
47
  import { dirNameStr, isMac, isWin } from "../core/paths.js";
46
48
  import { isProjectToolingAllowed } from "../core/projectTooling.js";
49
+ import { brotliDecoderProperties, readBrotliDecoder } from "./brotliDecoder.js";
50
+ import {
51
+ createHostDotnetRuntimeComponent,
52
+ parseDotnetListRuntimes,
53
+ } from "./dotnetRuntime.js";
47
54
  import { isValidSdkmanVersion } from "./jvmToolEnv.js";
55
+ import { createNodeRuntimeComponent } from "./nodeBuildInfo.js";
48
56
  import {
49
57
  createPhpRuntimeComponent,
58
+ packageStoreOf,
50
59
  parsePhpReportedVersion,
51
60
  } from "./phpBuildInfo.js";
52
61
  import { applyPurl, tryBuildPurl } from "./purl.js";
62
+ import {
63
+ createRubyRuntimeComponent,
64
+ parseRubyDescription,
65
+ } from "./rubyBuildInfo.js";
53
66
  import {
54
67
  classifySpawnRestriction,
55
68
  describeSpawnRestriction,
@@ -137,6 +150,11 @@ export const SDKMAN_JAVA_TOOL_ALIASES = {
137
150
  java24: readEnvironmentVariable("JAVA24_TOOL") || "24.0.2-tem",
138
151
  java25: readEnvironmentVariable("JAVA25_TOOL") || "25.0.4-tem",
139
152
  java26: readEnvironmentVariable("JAVA26_TOOL") || "26.0.2-tem",
153
+ // The broker serves no Java 27 build yet (every vendor id 404s), so java27
154
+ // has an alias only when JAVA27_TOOL names one that is downloadable.
155
+ ...(readEnvironmentVariable("JAVA27_TOOL")
156
+ ? { java27: readEnvironmentVariable("JAVA27_TOOL") }
157
+ : {}),
140
158
  };
141
159
 
142
160
  /**
@@ -165,7 +183,8 @@ export function getOriginUrl(dir) {
165
183
  * @param {string} configKey Git config key
166
184
  * @param {string} dir repo directory
167
185
  *
168
- * @returns Output from git config or undefined
186
+ * @returns {string|undefined} The branch name, or undefined when git fails,
187
+ * for example when the directory is not a git working tree or has no commit
169
188
  */
170
189
  export function getBranch(_configKey, dir) {
171
190
  return execGitCommand(dir, ["rev-parse", "--abbrev-ref", "HEAD"]);
@@ -198,20 +217,20 @@ export function gitTreeHashes(dir) {
198
217
  }
199
218
 
200
219
  /**
201
- * Retrieves the files list from git
202
- * @param {string} dir repo directory
220
+ * Retrieves the files git tracks under a directory at HEAD. Only the
221
+ * directory's own subtree is listed, with names relative to it, so a scan of
222
+ * one directory of a larger repository does not record the rest of that
223
+ * repository.
203
224
  *
204
- * @returns Output from git config or undefined
225
+ * @param {string} dir directory inside a git working tree
226
+ *
227
+ * @returns {Object[]} files with their blob hash, name, OmniBOR id and SWHID;
228
+ * empty when git fails, for example when the directory is not a git working
229
+ * tree or has no commit
205
230
  */
206
231
  export function listFiles(dir) {
207
232
  const filesList = [];
208
- const output = execGitCommand(dir, [
209
- "ls-tree",
210
- "-l",
211
- "-r",
212
- "--full-tree",
213
- "HEAD",
214
- ]);
233
+ const output = execGitCommand(dir, ["ls-tree", "-l", "-r", "HEAD"]);
215
234
  if (output) {
216
235
  output.split("\n").forEach((l) => {
217
236
  l = l.replaceAll("\r", "");
@@ -239,7 +258,10 @@ export function listFiles(dir) {
239
258
  * @param {string} dir Repo directory
240
259
  * @param {Array} args arguments to git command
241
260
  *
242
- * @returns Output from the git command
261
+ * @returns {string|undefined} git's standard output, or undefined when git
262
+ * could not run or exited with a non-zero status. Text git writes to standard
263
+ * error, including warnings of a successful command, is never part of the
264
+ * result.
243
265
  */
244
266
  export function execGitCommand(dir, args) {
245
267
  // Prepend hardening `-c` overrides before the subcommand and run git with a
@@ -256,6 +278,8 @@ export function execGitCommand(dir, args) {
256
278
  {
257
279
  env: hardenedGitEnv(),
258
280
  shell: false,
281
+ requireSuccess: true,
282
+ includeStderr: false,
259
283
  },
260
284
  );
261
285
  }
@@ -393,6 +417,22 @@ function recordToolProbeFailure(tool, ecosystem, command, detail) {
393
417
  }
394
418
  }
395
419
 
420
+ /**
421
+ * Collapse command output to one line. The `\r` of a Windows line ending goes
422
+ * with the break it belongs to, and the empty lines of trailing output are
423
+ * dropped.
424
+ *
425
+ * @param {string} output command output
426
+ * @param {string} separator what to join the lines with
427
+ * @returns {string} the non-empty lines joined into one line
428
+ */
429
+ function collapseOutputLines(output, separator) {
430
+ return `${output || ""}`
431
+ .split(/\r?\n/)
432
+ .filter((line) => line !== "")
433
+ .join(separator);
434
+ }
435
+
396
436
  /**
397
437
  * Collect Java version and installed modules
398
438
  *
@@ -400,9 +440,9 @@ function recordToolProbeFailure(tool, ecosystem, command, detail) {
400
440
  * @returns Object containing the java details
401
441
  */
402
442
  export function collectJavaInfo(dir) {
403
- const versionDesc = getCommandOutput(getJavaCommand(), dir, ["--version"]);
443
+ const versionDesc = probeCommandOutput(getJavaCommand(), dir, ["--version"]);
404
444
  const moduleDesc =
405
- getCommandOutput(getJavaCommand(), dir, ["--list-modules"]) || "";
445
+ probeCommandOutput(getJavaCommand(), dir, ["--list-modules"]) || "";
406
446
  if (versionDesc) {
407
447
  const info = {
408
448
  type: "platform",
@@ -412,7 +452,7 @@ export function collectJavaInfo(dir) {
412
452
  properties: [
413
453
  {
414
454
  name: "java:modules",
415
- value: moduleDesc.replaceAll("\n", ", "),
455
+ value: collapseOutputLines(moduleDesc, ", "),
416
456
  },
417
457
  ],
418
458
  };
@@ -426,20 +466,41 @@ export function collectJavaInfo(dir) {
426
466
  /**
427
467
  * Collect dotnet version
428
468
  *
469
+ * `version` is the version `dotnet --version` prints, which is the SDK's. .NET
470
+ * advisories name runtime versions, so the component itself carries no purl
471
+ * and no CPE. Each shared framework `dotnet --list-runtimes` reports is a
472
+ * nested component built by createHostDotnetRuntimeComponent, without a
473
+ * bom-ref like the other toolchains.
474
+ *
429
475
  * @param {string} dir Working directory
430
476
  * @returns Object containing dotnet details
431
477
  */
432
478
  export function collectDotnetInfo(dir) {
433
- const versionDesc = getCommandOutput(DOTNET_CMD, dir, ["--version"]);
479
+ const versionDesc = probeCommandOutput(DOTNET_CMD, dir, ["--version"]);
434
480
  const moduleDesc =
435
- getCommandOutput(DOTNET_CMD, dir, ["--list-runtimes"]) || "";
481
+ probeCommandOutput(DOTNET_CMD, dir, ["--list-runtimes"]) || "";
436
482
  if (versionDesc) {
437
483
  const info = {
438
484
  type: "platform",
439
485
  name: "dotnet",
440
486
  version: versionDesc.trim(),
441
- description: moduleDesc.replaceAll("\n", "\\n"),
487
+ description: collapseOutputLines(moduleDesc, "\\n"),
442
488
  };
489
+ const components = [];
490
+ const seen = new Set();
491
+ for (const entry of parseDotnetListRuntimes(moduleDesc)) {
492
+ const key = `${entry.framework} ${entry.version}`;
493
+ if (seen.has(key)) {
494
+ continue;
495
+ }
496
+ seen.add(key);
497
+ components.push(
498
+ formulationToolchain(createHostDotnetRuntimeComponent(entry)),
499
+ );
500
+ }
501
+ if (components.length) {
502
+ info.components = components;
503
+ }
443
504
  recordToolProbeResolved("dotnet", "csharp", info.version);
444
505
  return info;
445
506
  }
@@ -455,15 +516,15 @@ export function collectDotnetInfo(dir) {
455
516
  */
456
517
  export function collectPythonInfo(dir) {
457
518
  const pythonCmd = getPythonCommand();
458
- const versionDesc = getCommandOutput(pythonCmd, dir, ["-S", "--version"]);
519
+ const versionDesc = probeCommandOutput(pythonCmd, dir, ["-S", "--version"]);
459
520
  const moduleDesc =
460
- getCommandOutput(pythonCmd, dir, ["-I", "-m", "pip", "--version"]) || "";
521
+ probeCommandOutput(pythonCmd, dir, ["-I", "-m", "pip", "--version"]) || "";
461
522
  if (versionDesc) {
462
523
  const info = {
463
524
  type: "platform",
464
525
  name: "python",
465
526
  version: versionDesc.replace("Python ", ""),
466
- description: moduleDesc.replaceAll("\n", "\\n"),
527
+ description: collapseOutputLines(moduleDesc, "\\n"),
467
528
  };
468
529
  recordToolProbeResolved("python", "python", info.version);
469
530
  return info;
@@ -486,7 +547,17 @@ export function collectNodeInfo(dir) {
486
547
  version: runtimeInfo.version,
487
548
  components: runtimeInfo.components,
488
549
  };
489
- const moduleDesc = getCommandOutput(NPM_CMD, dir, ["--version"]);
550
+ if (runtimeInfo.runtime === "Node.js" && runtimeInfo.version) {
551
+ // The same identity a scanned image's Node.js gets, without a bom-ref, as
552
+ // for the other toolchains.
553
+ const runtime = formulationToolchain(
554
+ createNodeRuntimeComponent(runtimeInfo.version),
555
+ );
556
+ nodeInfo.purl = runtime.purl;
557
+ nodeInfo.cpe = runtime.cpe;
558
+ annotateNodeBrotli(nodeInfo.components);
559
+ }
560
+ const moduleDesc = probeCommandOutput(NPM_CMD, dir, ["--version"]);
490
561
  if (moduleDesc) {
491
562
  nodeInfo.description = `npm: ${moduleDesc}`;
492
563
  recordToolProbeResolved("npm", "npm", moduleDesc);
@@ -496,6 +567,36 @@ export function collectNodeInfo(dir) {
496
567
  return nodeInfo;
497
568
  }
498
569
 
570
+ /**
571
+ * Record whether the Brotli decoder the running Node.js uses carries the
572
+ * CVE-2026-111497 fix. process.versions.brotli cannot say: Node.js backports
573
+ * the fix without changing it. The decoder is read where it lives, in the node
574
+ * binary or in the shared libbrotlidec a `--shared-brotli` build loads.
575
+ *
576
+ * @param {Object[]|undefined} components bundled components of the runtime
577
+ */
578
+ function annotateNodeBrotli(components) {
579
+ const brotli = (components || []).find(
580
+ (component) => component.name === "brotli",
581
+ );
582
+ if (!brotli) {
583
+ return;
584
+ }
585
+ let decoderFile = globalThis.process?.execPath;
586
+ if (nodeComponentLinkage("brotli") === "shared") {
587
+ decoderFile = (
588
+ globalThis.process?.report?.getReport?.()?.sharedObjects || []
589
+ ).find((path) => basename(path).startsWith("libbrotlidec"));
590
+ }
591
+ const decoder = decoderFile ? readBrotliDecoder(decoderFile) : undefined;
592
+ if (decoder) {
593
+ brotli.properties = [
594
+ ...(brotli.properties || []),
595
+ ...brotliDecoderProperties(decoder),
596
+ ];
597
+ }
598
+ }
599
+
499
600
  /**
500
601
  * Collect gcc version
501
602
  *
@@ -503,14 +604,14 @@ export function collectNodeInfo(dir) {
503
604
  * @returns Object containing gcc details
504
605
  */
505
606
  export function collectGccInfo(dir) {
506
- const versionDesc = getCommandOutput(GCC_CMD, dir, ["--version"]);
507
- const moduleDesc = getCommandOutput(GCC_CMD, dir, ["-print-search-dirs"]);
607
+ const versionDesc = probeCommandOutput(GCC_CMD, dir, ["--version"]);
608
+ const moduleDesc = probeCommandOutput(GCC_CMD, dir, ["-print-search-dirs"]);
508
609
  if (versionDesc) {
509
610
  const info = {
510
611
  type: "platform",
511
612
  name: "gcc",
512
613
  version: versionDesc.split("\n")[0],
513
- description: (moduleDesc || "").replaceAll("\n", "\\n"),
614
+ description: collapseOutputLines(moduleDesc, "\\n"),
514
615
  };
515
616
  recordToolProbeResolved("gcc", "c", info.version);
516
617
  return info;
@@ -526,8 +627,8 @@ export function collectGccInfo(dir) {
526
627
  * @returns Object containing rust details
527
628
  */
528
629
  export function collectRustInfo(dir) {
529
- const versionDesc = getCommandOutput(RUSTC_CMD, dir, ["--version"]);
530
- const moduleDesc = getCommandOutput(CARGO_CMD, dir, ["--version"]);
630
+ const versionDesc = probeCommandOutput(RUSTC_CMD, dir, ["--version"]);
631
+ const moduleDesc = probeCommandOutput(CARGO_CMD, dir, ["--version"]);
531
632
  if (versionDesc) {
532
633
  const info = {
533
634
  type: "platform",
@@ -549,7 +650,7 @@ export function collectRustInfo(dir) {
549
650
  * @returns Object containing go details
550
651
  */
551
652
  export function collectGoInfo(dir) {
552
- const versionDesc = getCommandOutput(GO_CMD, dir, ["version"]);
653
+ const versionDesc = probeCommandOutput(GO_CMD, dir, ["version"]);
553
654
  if (versionDesc) {
554
655
  const info = {
555
656
  type: "platform",
@@ -570,7 +671,7 @@ export function collectGoInfo(dir) {
570
671
  * @returns Object containing swift details
571
672
  */
572
673
  export function collectSwiftInfo(dir) {
573
- const versionDesc = getCommandOutput(SWIFT_CMD, dir, ["--version"]);
674
+ const versionDesc = probeCommandOutput(SWIFT_CMD, dir, ["--version"]);
574
675
  if (versionDesc) {
575
676
  const info = {
576
677
  type: "platform",
@@ -585,23 +686,217 @@ export function collectSwiftInfo(dir) {
585
686
  }
586
687
 
587
688
  /**
588
- * Collect Ruby version
689
+ * The real path of the executable a command name runs, looked up on PATH as
690
+ * the shell would, or undefined when it cannot be found.
691
+ *
692
+ * @param {string} command command name or path
693
+ * @returns {string|undefined}
694
+ */
695
+ function resolveExecutable(command) {
696
+ const name = `${command || ""}`.trim().split(" ")[0];
697
+ if (!name) {
698
+ return undefined;
699
+ }
700
+ const extensions = isWin
701
+ ? ["", ...`${process.env.PATHEXT || ".EXE;.CMD;.BAT"}`.split(";")]
702
+ : [""];
703
+ const directories = isAbsolute(name)
704
+ ? [dirname(name)]
705
+ : `${process.env.PATH || ""}`.split(delimiter).filter(Boolean);
706
+ const base = isAbsolute(name) ? basename(name) : name;
707
+ for (const directory of directories) {
708
+ for (const extension of extensions) {
709
+ const candidate = join(directory, `${base}${extension}`);
710
+ try {
711
+ if (statSync(candidate).isFile()) {
712
+ return realpathSync(candidate);
713
+ }
714
+ } catch {
715
+ // not here
716
+ }
717
+ }
718
+ }
719
+ return undefined;
720
+ }
721
+
722
+ /**
723
+ * Whether a Ruby executable is one the operating system or a package manager
724
+ * that patches it owns. Distros backport fixes under the old upstream version,
725
+ * so its version must not be matched against upstream advisories by CPE.
726
+ *
727
+ * @param {string|undefined} realPath real path of the executable
728
+ * @returns {boolean}
729
+ */
730
+ export function isPackagedRubyPath(realPath) {
731
+ if (!realPath || isWin) {
732
+ return false;
733
+ }
734
+ // Homebrew builds the upstream release, so it keeps the CPE, as it does in
735
+ // an OBOM; Nix and Guix patch theirs.
736
+ const store = packageStoreOf(realPath);
737
+ if (store) {
738
+ return store !== "homebrew";
739
+ }
740
+ return ["/usr/bin/", "/bin/", "/usr/lib/", "/usr/libexec/", "/System/"].some(
741
+ (prefix) => realPath.startsWith(prefix),
742
+ );
743
+ }
744
+
745
+ // The shims of rbenv, asdf and mise run whichever Ruby is selected, which can
746
+ // be the distro's (`rbenv global system`).
747
+ function isVersionManagerShim(realPath) {
748
+ return `${realPath}`.split(/[\\/]/).includes("shims");
749
+ }
750
+
751
+ // Whether the Ruby `ruby --version` described may carry its release's CPE.
752
+ // A shim is followed to the interpreter it runs; one that cannot be followed
753
+ // keeps no CPE, since it may be the distro's.
754
+ function rubyKeepsCpe(dir) {
755
+ let executable = resolveExecutable(RUBY_CMD);
756
+ if (executable && isVersionManagerShim(executable)) {
757
+ const target = isWin
758
+ ? ""
759
+ : firstOutputLine(
760
+ probeCommandOutput(RUBY_CMD, dir, ["-e", "print RbConfig.ruby"]),
761
+ );
762
+ if (!isAbsolute(target)) {
763
+ return false;
764
+ }
765
+ try {
766
+ executable = realpathSync(target);
767
+ } catch {
768
+ executable = target;
769
+ }
770
+ }
771
+ return !isPackagedRubyPath(executable);
772
+ }
773
+
774
+ /**
775
+ * Collect the Ruby on the PATH.
776
+ *
777
+ * `ruby --version` prints the interpreter's description, such as
778
+ * `ruby 3.3.6 (2024-11-05 revision 75015d4c1f) [x86_64-linux]`, which is kept
779
+ * as the component description. The identity is `pkg:generic/ruby@3.3.6`
780
+ * (or jruby, truffleruby), with the CPE of the release unless a package
781
+ * manager owns the executable: a distro backports fixes under the old upstream
782
+ * version.
589
783
  *
590
784
  * @param {string} dir Working directory
591
785
  * @returns Object containing Ruby details
592
786
  */
593
787
  export function collectRubyInfo(dir) {
594
- const versionDesc = getCommandOutput(RUBY_CMD, dir, ["--version"]);
595
- if (versionDesc) {
596
- const info = {
597
- type: "platform",
598
- name: "ruby",
599
- version: versionDesc.trim(),
600
- };
601
- recordToolProbeResolved("ruby", "ruby", info.version);
788
+ const versionDesc = probeCommandOutput(RUBY_CMD, dir, ["--version"]);
789
+ const banner = firstOutputLine(versionDesc);
790
+ const parsed = parseRubyDescription(banner);
791
+ if (parsed) {
792
+ const info = formulationToolchain(
793
+ createRubyRuntimeComponent(parsed.version, {
794
+ engine: parsed.engine,
795
+ withCpe: rubyKeepsCpe(dir),
796
+ }),
797
+ );
798
+ info.description = banner;
799
+ const properties = [];
800
+ if (parsed.patchlevel !== undefined) {
801
+ properties.push({
802
+ name: "cdx:ruby:patchlevel",
803
+ value: parsed.patchlevel,
804
+ });
805
+ }
806
+ if (parsed.rubyVersion) {
807
+ properties.push({
808
+ name: "cdx:ruby:languageVersion",
809
+ value: parsed.rubyVersion,
810
+ });
811
+ }
812
+ if (parsed.platform) {
813
+ properties.push({ name: "cdx:ruby:platform", value: parsed.platform });
814
+ }
815
+ if (properties.length) {
816
+ info.properties = properties;
817
+ }
818
+ recordToolProbeResolved("ruby", "ruby", banner);
602
819
  return info;
603
820
  }
604
- recordToolProbeFailure("ruby", "ruby", RUBY_CMD);
821
+ recordToolProbeFailure(
822
+ "ruby",
823
+ "ruby",
824
+ RUBY_CMD,
825
+ versionDesc
826
+ ? "The ruby version probe did not print a Ruby version banner."
827
+ : undefined,
828
+ );
829
+ return undefined;
830
+ }
831
+
832
+ /**
833
+ * Whether the Ruby collectRubyInfo found is the MRI release a project needs.
834
+ *
835
+ * @param {Object|undefined} info result of collectRubyInfo
836
+ * @param {string} version wanted MRI version
837
+ * @returns {boolean}
838
+ */
839
+ export function isRubyVersionActive(info, version) {
840
+ return Boolean(info && info.name === "ruby" && info.version === version);
841
+ }
842
+
843
+ /**
844
+ * Collect the RubyGems version. `gem --version` prints the bare version.
845
+ *
846
+ * @param {string} dir Working directory
847
+ * @returns {Object|undefined} the RubyGems component
848
+ */
849
+ export function collectRubyGemsInfo(dir) {
850
+ const token = firstOutputLine(
851
+ probeCommandOutput(GEM_CMD, dir, ["--version"]),
852
+ );
853
+ if (/^\d+\.\d+\.\d+$/.test(token)) {
854
+ recordToolProbeResolved("gem", "ruby", token);
855
+ return formulationToolchain(
856
+ applyPurl(
857
+ {
858
+ type: "platform",
859
+ name: "rubygems",
860
+ version: token,
861
+ cpe: `cpe:2.3:a:rubygems:rubygems:${token}:*:*:*:*:*:*:*`,
862
+ },
863
+ tryBuildPurl({ type: "generic", name: "rubygems", version: token }),
864
+ ),
865
+ );
866
+ }
867
+ recordToolProbeFailure("gem", "ruby", GEM_CMD);
868
+ return undefined;
869
+ }
870
+
871
+ /**
872
+ * Collect the Bundler version. `bundle --version` prints `Bundler version
873
+ * 2.5.22`, or the bare version from Bundler 2.6 on.
874
+ *
875
+ * @param {string} dir Working directory
876
+ * @returns {Object|undefined} the Bundler component
877
+ */
878
+ export function collectBundlerInfo(dir) {
879
+ const line = firstOutputLine(
880
+ probeCommandOutput(BUNDLE_CMD, dir, ["--version"]),
881
+ );
882
+ const token = line.startsWith("Bundler version ")
883
+ ? line.slice("Bundler version ".length).trim()
884
+ : line;
885
+ if (/^\d+\.\d+\.\d+$/.test(token)) {
886
+ recordToolProbeResolved("bundler", "ruby", token);
887
+ return formulationToolchain(
888
+ applyPurl(
889
+ {
890
+ type: "platform",
891
+ name: "bundler",
892
+ version: token,
893
+ cpe: `cpe:2.3:a:bundler:bundler:${token}:*:*:*:*:*:*:*`,
894
+ },
895
+ tryBuildPurl({ type: "gem", name: "bundler", version: token }),
896
+ ),
897
+ );
898
+ }
899
+ recordToolProbeFailure("bundler", "ruby", BUNDLE_CMD);
605
900
  return undefined;
606
901
  }
607
902
 
@@ -646,7 +941,7 @@ function formulationToolchain(component) {
646
941
  * @returns Object containing the PHP details
647
942
  */
648
943
  export function collectPhpInfo(dir) {
649
- const output = getCommandOutput(PHP_CMD, dir, ["-n", "-v"]);
944
+ const output = probeCommandOutput(PHP_CMD, dir, ["-n", "-v"]);
650
945
  const banner = firstOutputLine(output);
651
946
  const reported = banner.startsWith("PHP ")
652
947
  ? parsePhpReportedVersion(banner.slice("PHP ".length))
@@ -688,7 +983,7 @@ export function collectPhpInfo(dir) {
688
983
  * @returns Object containing the Composer details
689
984
  */
690
985
  export function collectComposerInfo(dir) {
691
- const output = getCommandOutput(COMPOSER_CMD, dir, [
986
+ const output = probeCommandOutput(COMPOSER_CMD, dir, [
692
987
  "--version",
693
988
  "--no-plugins",
694
989
  "--no-scripts",
@@ -787,6 +1082,14 @@ export function collectEnvInfo(dir, options = undefined) {
787
1082
  cmp = collectRubyInfo(dir);
788
1083
  if (cmp) {
789
1084
  infoComponents.push(cmp);
1085
+ cmp = collectRubyGemsInfo(dir);
1086
+ if (cmp) {
1087
+ infoComponents.push(cmp);
1088
+ }
1089
+ cmp = collectBundlerInfo(dir);
1090
+ if (cmp) {
1091
+ infoComponents.push(cmp);
1092
+ }
790
1093
  }
791
1094
  cmp = collectPhpInfo(dir);
792
1095
  if (cmp) {
@@ -799,16 +1102,55 @@ export function collectEnvInfo(dir, options = undefined) {
799
1102
  return infoComponents;
800
1103
  }
801
1104
 
1105
+ /**
1106
+ * Longest a toolchain version probe may run. A `--version` call answers in
1107
+ * well under a second; a tool that sets itself up on first use, such as
1108
+ * dotnet or gem in a fresh home directory, or one that waits on a prompt,
1109
+ * must not hold up a scan for the general spawn timeout.
1110
+ */
1111
+ const VERSION_PROBE_TIMEOUT_MS = 60000;
1112
+
1113
+ /**
1114
+ * Run a toolchain version probe with VERSION_PROBE_TIMEOUT_MS as its limit.
1115
+ *
1116
+ * A probe that fails gives no output. Its text is then an error message, not
1117
+ * a version: `dotnet --version` under a global.json that pins an SDK the host
1118
+ * lacks exits with an error and lists every installed SDK, which would
1119
+ * otherwise become the version of the dotnet component in the formulation.
1120
+ *
1121
+ * @param {string} cmd Command to execute
1122
+ * @param {string} dir Working directory
1123
+ * @param {string[]} args Arguments
1124
+ * @returns {string|undefined} Output of the command, or undefined when it
1125
+ * could not run or exited with a non-zero status
1126
+ */
1127
+ const probeCommandOutput = (cmd, dir, args) =>
1128
+ getCommandOutput(cmd, dir, args, {
1129
+ requireSuccess: true,
1130
+ timeout: VERSION_PROBE_TIMEOUT_MS,
1131
+ });
1132
+
802
1133
  /**
803
1134
  * Execute any command to retrieve the output
804
1135
  *
805
1136
  * @param {*} cmd Command to execute
806
1137
  * @param {*} dir working directory
807
1138
  * @param {*} args arguments
808
- * @param {Object} [spawnOverrides] extra options merged into spawnSync (e.g. env)
809
- * @returns String output from the command or undefined in case of error
1139
+ * @param {Object} [options] extra options merged into spawnSync (e.g. env),
1140
+ * plus `requireSuccess`, which gives undefined for a command that exits with
1141
+ * a non-zero status, and `includeStderr`, which joins the command's standard
1142
+ * error into the result (the default). With `includeStderr` false only the
1143
+ * standard output is returned, so text a command writes to standard error
1144
+ * cannot reach a parser of its output.
1145
+ * @returns {string|undefined} Output of the command, or undefined when it
1146
+ * could not run or printed nothing
810
1147
  */
811
- const getCommandOutput = (cmd, dir, args, spawnOverrides = {}) => {
1148
+ const getCommandOutput = (cmd, dir, args, options = {}) => {
1149
+ const {
1150
+ requireSuccess = false,
1151
+ includeStderr = true,
1152
+ ...spawnOverrides
1153
+ } = options;
812
1154
  let commandToUse = cmd;
813
1155
  // If the command includes space, automatically move it to the front of the args.
814
1156
  if (cmd?.trim().includes(" ")) {
@@ -837,7 +1179,13 @@ const getCommandOutput = (cmd, dir, args, spawnOverrides = {}) => {
837
1179
  maxBuffer: MAX_BUFFER * 2,
838
1180
  ...spawnOverrides,
839
1181
  });
1182
+ if (requireSuccess && (result.error || result.status !== 0)) {
1183
+ return undefined;
1184
+ }
840
1185
  const stdout = result.stdout ? result.stdout.toString() : "";
1186
+ if (!includeStderr) {
1187
+ return stdout.trim() || undefined;
1188
+ }
841
1189
  const stderr = result.stderr ? result.stderr.toString() : "";
842
1190
  return `${stdout}\n${stderr}`.trim() || undefined;
843
1191
  };
@@ -1218,7 +1566,7 @@ export function installRubyVersion(rubyVersion, filePath) {
1218
1566
  return { fullToolBinDir: undefined, status: false };
1219
1567
  }
1220
1568
  const existingRuby = collectRubyInfo(filePath);
1221
- if (existingRuby?.version?.startsWith(`ruby ${rubyVersion} `)) {
1569
+ if (isRubyVersionActive(existingRuby, rubyVersion)) {
1222
1570
  return { fullToolBinDir: undefined, status: true };
1223
1571
  }
1224
1572
  const fullToolBinDir = rubyVersionDir(rubyVersion);
@@ -1984,6 +2332,36 @@ export function gitRevertsAndHotfixes(dir, maxCount = 20) {
1984
2332
  return entries;
1985
2333
  }
1986
2334
 
2335
+ // process.versions keys whose configure flag is named differently.
2336
+ const NODE_SHARED_FLAG_NAMES = {
2337
+ ares: "cares",
2338
+ libffi: "ffi",
2339
+ llhttp: "http_parser",
2340
+ uv: "libuv",
2341
+ };
2342
+
2343
+ /**
2344
+ * Whether the running Node.js links a bundled dependency as a shared library,
2345
+ * from the `node_shared_<name>` configure variables it reports.
2346
+ *
2347
+ * @param {string} name key of process.versions
2348
+ * @param {Object} [variables] configure variables, process.config.variables
2349
+ * by default
2350
+ * @returns {string|undefined} `shared`, `bundled`, or undefined when the build
2351
+ * does not say
2352
+ */
2353
+ export function nodeComponentLinkage(
2354
+ name,
2355
+ variables = globalThis.process?.config?.variables,
2356
+ ) {
2357
+ const flag = `node_shared_${NODE_SHARED_FLAG_NAMES[name] || name}`;
2358
+ if (!variables || !(flag in variables)) {
2359
+ return undefined;
2360
+ }
2361
+ const value = variables[flag];
2362
+ return value === true || value === "true" ? "shared" : "bundled";
2363
+ }
2364
+
1987
2365
  /**
1988
2366
  * Get information about the runtime.
1989
2367
  *
@@ -2016,12 +2394,22 @@ export function getRuntimeInformation() {
2016
2394
  name,
2017
2395
  version: version || null,
2018
2396
  });
2397
+ // A distro or Homebrew build links some of these as shared libraries
2398
+ // (`configure --shared-brotli`); the version is then the shared
2399
+ // library's, and updating Node.js alone does not update it.
2400
+ const linkage = nodeComponentLinkage(name);
2019
2401
  const apkg = {
2020
2402
  name,
2021
2403
  version,
2022
- description: `Bundled with Node.js ${runtimeInfo.version}`,
2404
+ description:
2405
+ linkage === "shared"
2406
+ ? `Linked by Node.js ${runtimeInfo.version} as a shared library`
2407
+ : `Bundled with Node.js ${runtimeInfo.version}`,
2023
2408
  type: "library",
2024
2409
  scope: "excluded",
2410
+ ...(linkage
2411
+ ? { properties: [{ name: "cdx:node:linkage", value: linkage }] }
2412
+ : {}),
2025
2413
  };
2026
2414
  applyPurl(apkg, bundledPurl);
2027
2415
  if (nodeSourceUrl) {