@cdxgen/cdxgen 0.0.1 → 13.0.1

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 (722) hide show
  1. package/README.md +921 -87
  2. package/bin/audit.js +239 -0
  3. package/bin/cdxgen.js +2335 -0
  4. package/bin/convert.js +220 -0
  5. package/bin/evinse.js +353 -0
  6. package/bin/hbom.js +504 -0
  7. package/bin/repl.js +2224 -0
  8. package/bin/sign.js +195 -0
  9. package/bin/tracebom.js +303 -0
  10. package/bin/validate.js +268 -0
  11. package/bin/verify.js +189 -0
  12. package/data/README.md +307 -0
  13. package/data/bom-1.4.schema.json +1605 -0
  14. package/data/bom-1.5.schema.json +3660 -0
  15. package/data/bom-1.6.schema.json +5511 -0
  16. package/data/bom-1.7.schema.json +6700 -0
  17. package/data/cbomosdb-queries.json +68 -0
  18. package/data/component-tags.json +339 -0
  19. package/data/container-knowledge-index.json +125 -0
  20. package/data/cosdb-queries.json +17 -0
  21. package/data/crypto-oid.json +1998 -0
  22. package/data/cryptography-defs.json +3793 -0
  23. package/data/cryptography-defs.schema.json +576 -0
  24. package/data/cyclonedx-2.0-bundled.schema.json +7182 -0
  25. package/data/frameworks-list.json +219 -0
  26. package/data/glibc-stdlib.json +117 -0
  27. package/data/gtfobins-index.json +6296 -0
  28. package/data/helpers/init.gradle +71 -0
  29. package/data/jsf-0.82.schema.json +211 -0
  30. package/data/known-licenses.json +82 -0
  31. package/data/lic-mapping.json +338 -0
  32. package/data/license-aliases.json +6624 -0
  33. package/data/license-db.json +39059 -0
  34. package/data/license-deprecations.json +302 -0
  35. package/data/lolbas-index.json +150 -0
  36. package/data/predictive-audit-allowlist.json +11 -0
  37. package/data/pypi-pkg-aliases.json +1199 -0
  38. package/data/python-stdlib.json +308 -0
  39. package/data/queries-darwin.json +193 -0
  40. package/data/queries-win.json +257 -0
  41. package/data/queries.json +268 -0
  42. package/data/ruby-known-modules.json +281 -0
  43. package/data/rules/ai-agent-governance.yaml +323 -0
  44. package/data/rules/ai-bom.yaml +268 -0
  45. package/data/rules/ai-oversight.yaml +186 -0
  46. package/data/rules/ai-provenance.yaml +92 -0
  47. package/data/rules/asar-archives.yaml +150 -0
  48. package/data/rules/cbom-security.yaml +165 -0
  49. package/data/rules/chrome-extensions.yaml +248 -0
  50. package/data/rules/ci-permissions.yaml +802 -0
  51. package/data/rules/container-risk.yaml +353 -0
  52. package/data/rules/dependency-sources.yaml +236 -0
  53. package/data/rules/golem-go.yaml +229 -0
  54. package/data/rules/hbom-compliance.yaml +325 -0
  55. package/data/rules/hbom-performance.yaml +307 -0
  56. package/data/rules/hbom-security.yaml +248 -0
  57. package/data/rules/host-topology.yaml +165 -0
  58. package/data/rules/mcp-servers.yaml +388 -0
  59. package/data/rules/obom-runtime.yaml +1776 -0
  60. package/data/rules/package-integrity.yaml +364 -0
  61. package/data/rules/rootfs-hardening.yaml +179 -0
  62. package/data/rules/vscode-extensions.yaml +237 -0
  63. package/data/spdx-export.schema.json +6794 -0
  64. package/data/spdx-license-list.json +7449 -0
  65. package/data/spdx-licenses.json +759 -0
  66. package/data/spdx.schema.json +819 -0
  67. package/data/templates/README.md +3 -0
  68. package/data/templates/asvs-4.0.3.cdx.json +2943 -0
  69. package/data/templates/asvs-5.0.cdx.json +3046 -0
  70. package/data/templates/bsimm-v13.cdx.json +1008 -0
  71. package/data/templates/masvs-2.0.0.cdx.json +225 -0
  72. package/data/templates/nist-ssdf-1.1.cdx.json +828 -0
  73. package/data/templates/pcissc-secure-slc-1.1.cdx.json +1027 -0
  74. package/data/templates/scvs-1.0.0.cdx.json +813 -0
  75. package/data/templates/ssaf-DRAFT-2023-11.cdx.json +127 -0
  76. package/data/vendor-alias.json +41967 -0
  77. package/data/wrapdb-releases.json +4227 -0
  78. package/index.cjs +15 -0
  79. package/lib/audit/index.js +2800 -0
  80. package/lib/audit/progress.js +114 -0
  81. package/lib/audit/reporters.js +937 -0
  82. package/lib/audit/scoring.js +560 -0
  83. package/lib/audit/targets.js +798 -0
  84. package/lib/cli/bomAssembly.js +1882 -0
  85. package/lib/cli/cliOptions.js +543 -0
  86. package/lib/cli/index.js +2759 -0
  87. package/lib/cli/jsBom.js +2087 -0
  88. package/lib/cli/jvmBom.js +1858 -0
  89. package/lib/cli/managedBom.js +2083 -0
  90. package/lib/cli/nativeBom.js +2125 -0
  91. package/lib/core/activity.js +1087 -0
  92. package/lib/core/env.js +719 -0
  93. package/lib/core/fs.js +1058 -0
  94. package/lib/core/httpClient.js +730 -0
  95. package/lib/core/logger.js +280 -0
  96. package/lib/core/parallel.js +323 -0
  97. package/lib/core/paths.js +70 -0
  98. package/lib/core/projectConfig.js +122 -0
  99. package/lib/core/propertySanitizer.js +174 -0
  100. package/lib/core/spdx.js +5 -0
  101. package/lib/core/state.js +59 -0
  102. package/lib/core/text.js +207 -0
  103. package/lib/core/ui.js +920 -0
  104. package/lib/core/urls.js +84 -0
  105. package/lib/ecosystems/asarutils.js +1636 -0
  106. package/lib/ecosystems/bunutils.js +402 -0
  107. package/lib/ecosystems/caxa.js +77 -0
  108. package/lib/ecosystems/chromextutils.js +1174 -0
  109. package/lib/ecosystems/cmakeResolver.js +587 -0
  110. package/lib/ecosystems/cppEvidence.js +305 -0
  111. package/lib/ecosystems/denoutils.js +1094 -0
  112. package/lib/ecosystems/dotnetutils.js +132 -0
  113. package/lib/ecosystems/ecosystems.js +2772 -0
  114. package/lib/ecosystems/golem.js +1807 -0
  115. package/lib/ecosystems/gradleutils.js +1048 -0
  116. package/lib/ecosystems/jsEvidence.js +317 -0
  117. package/lib/ecosystems/npmutils.js +586 -0
  118. package/lib/ecosystems/parsers-bazel.js +875 -0
  119. package/lib/ecosystems/parsers-dotnet.js +1430 -0
  120. package/lib/ecosystems/parsers-gleam.js +302 -0
  121. package/lib/ecosystems/parsers-go.js +818 -0
  122. package/lib/ecosystems/parsers-js.js +4222 -0
  123. package/lib/ecosystems/parsers-jvm.js +948 -0
  124. package/lib/ecosystems/parsers-misc.js +3191 -0
  125. package/lib/ecosystems/parsers-python.js +2070 -0
  126. package/lib/ecosystems/parsers-rust.js +1294 -0
  127. package/lib/ecosystems/parsers-zig.js +861 -0
  128. package/lib/ecosystems/pep770.js +341 -0
  129. package/lib/ecosystems/pylockutils.js +427 -0
  130. package/lib/ecosystems/pythonutils.js +1281 -0
  131. package/lib/ecosystems/registryProvenance.js +1003 -0
  132. package/lib/ecosystems/remote/huggingface.js +1343 -0
  133. package/lib/ecosystems/rubyutils.js +1629 -0
  134. package/lib/ecosystems/rusi.js +820 -0
  135. package/lib/ecosystems/sbtutils.js +972 -0
  136. package/lib/ecosystems/tea.js +607 -0
  137. package/lib/ecosystems/utils.js +583 -0
  138. package/lib/ecosystems/zigResolver.js +410 -0
  139. package/lib/evinser/db.js +145 -0
  140. package/lib/evinser/evinser.js +2362 -0
  141. package/lib/evinser/scalasem.js +65 -0
  142. package/lib/evinser/swiftsem.js +767 -0
  143. package/lib/helpers/annotationFormatter.js +84 -0
  144. package/lib/helpers/bomSigner.js +312 -0
  145. package/lib/helpers/exportUtils.js +160 -0
  146. package/lib/helpers/remote/dependency-track.js +117 -0
  147. package/lib/helpers/versutils.js +202 -0
  148. package/lib/helpers/vsixutils.js +1069 -0
  149. package/lib/inventory/agentFormulationParser.js +322 -0
  150. package/lib/inventory/aiCollector.js +2140 -0
  151. package/lib/inventory/aiInventory.js +414 -0
  152. package/lib/inventory/aiModelVariants.js +121 -0
  153. package/lib/inventory/aiOversightCollector.js +661 -0
  154. package/lib/inventory/aiProvenanceCollector.js +822 -0
  155. package/lib/inventory/analyzer.js +6089 -0
  156. package/lib/inventory/analyzerScope.js +807 -0
  157. package/lib/inventory/astWorker.js +20 -0
  158. package/lib/inventory/atomUtils.js +1092 -0
  159. package/lib/inventory/auditCategories.js +192 -0
  160. package/lib/inventory/bomUtils.js +369 -0
  161. package/lib/inventory/cacheCommands.js +182 -0
  162. package/lib/inventory/cacheDir.js +104 -0
  163. package/lib/inventory/cbomutils.js +590 -0
  164. package/lib/inventory/cdxrs.js +434 -0
  165. package/lib/inventory/ciParsers/azurePipelines.js +295 -0
  166. package/lib/inventory/ciParsers/circleCi.js +286 -0
  167. package/lib/inventory/ciParsers/common.js +24 -0
  168. package/lib/inventory/ciParsers/githubActions.js +2434 -0
  169. package/lib/inventory/ciParsers/gitlabCi.js +213 -0
  170. package/lib/inventory/ciParsers/jenkins.js +180 -0
  171. package/lib/inventory/citations.js +234 -0
  172. package/lib/inventory/communityAiConfigParser.js +756 -0
  173. package/lib/inventory/containerRisk.js +204 -0
  174. package/lib/inventory/cryptoAlgorithmFamily.js +258 -0
  175. package/lib/inventory/deps.js +807 -0
  176. package/lib/inventory/depsUtils.js +588 -0
  177. package/lib/inventory/display.js +2128 -0
  178. package/lib/inventory/dosai.js +601 -0
  179. package/lib/inventory/dosaiParsers.js +154 -0
  180. package/lib/inventory/dynamic.js +145 -0
  181. package/lib/inventory/envcontext.js +1793 -0
  182. package/lib/inventory/evidenceUtils.js +774 -0
  183. package/lib/inventory/fetchBatch.js +779 -0
  184. package/lib/inventory/fetchRate.js +255 -0
  185. package/lib/inventory/forgeEnricher.js +300 -0
  186. package/lib/inventory/formulationParsers.js +422 -0
  187. package/lib/inventory/gtfobins.js +339 -0
  188. package/lib/inventory/hbom.js +719 -0
  189. package/lib/inventory/hbomAnalysis.js +310 -0
  190. package/lib/inventory/hbomLoader.js +35 -0
  191. package/lib/inventory/hostTopology.js +835 -0
  192. package/lib/inventory/inventoryStats.js +100 -0
  193. package/lib/inventory/jsonLike.js +121 -0
  194. package/lib/inventory/licenseEnhancer.js +918 -0
  195. package/lib/inventory/lolbas.js +285 -0
  196. package/lib/inventory/mcp.js +253 -0
  197. package/lib/inventory/mcpConfigParser.js +683 -0
  198. package/lib/inventory/mcpDiscovery.js +112 -0
  199. package/lib/inventory/mcpPinning.js +209 -0
  200. package/lib/inventory/osPackageResolver.js +493 -0
  201. package/lib/inventory/osinfo.js +383 -0
  202. package/lib/inventory/osqueryTransform.js +274 -0
  203. package/lib/inventory/plugins.js +466 -0
  204. package/lib/inventory/protobom.js +231 -0
  205. package/lib/inventory/protobomLoader.js +43 -0
  206. package/lib/inventory/provenanceUtils.js +224 -0
  207. package/lib/inventory/purl.js +749 -0
  208. package/lib/inventory/rustFormulationParser.js +339 -0
  209. package/lib/inventory/sbomDocument.js +340 -0
  210. package/lib/inventory/source.js +1488 -0
  211. package/lib/inventory/spdx.js +361 -0
  212. package/lib/inventory/spdxExpression.js +512 -0
  213. package/lib/inventory/spdxUtils.js +107 -0
  214. package/lib/inventory/table.js +267 -0
  215. package/lib/inventory/traceRunner.js +439 -0
  216. package/lib/inventory/unicodeScan.js +147 -0
  217. package/lib/managers/binary.js +3459 -0
  218. package/lib/managers/containerutils.js +125 -0
  219. package/lib/managers/docker.js +2145 -0
  220. package/lib/managers/dockerConnection.js +162 -0
  221. package/lib/managers/oci.js +704 -0
  222. package/lib/managers/piptree.js +267 -0
  223. package/lib/parsers/cmakeCache.js +199 -0
  224. package/lib/parsers/gguf.js +492 -0
  225. package/lib/parsers/gitAiNotes.js +233 -0
  226. package/lib/parsers/gitmodules.js +239 -0
  227. package/lib/parsers/htmlExtract.js +610 -0
  228. package/lib/parsers/huggingfaceManifest.js +551 -0
  229. package/lib/parsers/huggingfaceUtils.js +506 -0
  230. package/lib/parsers/iri.js +618 -0
  231. package/lib/parsers/npmrc.js +99 -0
  232. package/lib/parsers/ollama.js +101 -0
  233. package/lib/parsers/xml.js +841 -0
  234. package/lib/server/openapi.yaml +433 -0
  235. package/lib/server/server.js +616 -0
  236. package/lib/stages/postgen/annotator.js +977 -0
  237. package/lib/stages/postgen/auditBom.js +468 -0
  238. package/lib/stages/postgen/postgen.js +1239 -0
  239. package/lib/stages/postgen/ruleEngine.js +731 -0
  240. package/lib/stages/postgen/sortBom.js +195 -0
  241. package/lib/stages/postgen/spdxConverter.js +796 -0
  242. package/lib/stages/postgen/specVersionCompat.js +703 -0
  243. package/lib/stages/pregen/envAudit.js +388 -0
  244. package/lib/stages/pregen/pregen.js +637 -0
  245. package/lib/third-party/README.md +9 -0
  246. package/lib/third-party/arborist/CHANGELOG.md +1109 -0
  247. package/lib/third-party/arborist/LICENSE.md +20 -0
  248. package/lib/third-party/arborist/README.md +315 -0
  249. package/lib/third-party/arborist/lib/arborist/index.js +258 -0
  250. package/lib/third-party/arborist/lib/arborist/load-actual.js +605 -0
  251. package/lib/third-party/arborist/lib/arborist/load-virtual.js +300 -0
  252. package/lib/third-party/arborist/lib/bin-links.js +120 -0
  253. package/lib/third-party/arborist/lib/calc-dep-flags.js +130 -0
  254. package/lib/third-party/arborist/lib/case-insensitive-map.js +51 -0
  255. package/lib/third-party/arborist/lib/consistent-resolve.js +42 -0
  256. package/lib/third-party/arborist/lib/debug.js +50 -0
  257. package/lib/third-party/arborist/lib/dep-valid.js +190 -0
  258. package/lib/third-party/arborist/lib/edge.js +417 -0
  259. package/lib/third-party/arborist/lib/from-path.js +36 -0
  260. package/lib/third-party/arborist/lib/gather-dep-set.js +43 -0
  261. package/lib/third-party/arborist/lib/index.js +22 -0
  262. package/lib/third-party/arborist/lib/inventory.js +138 -0
  263. package/lib/third-party/arborist/lib/link.js +169 -0
  264. package/lib/third-party/arborist/lib/node.js +1673 -0
  265. package/lib/third-party/arborist/lib/npm-extension.js +45 -0
  266. package/lib/third-party/arborist/lib/override-resolves.js +11 -0
  267. package/lib/third-party/arborist/lib/override-set.js +319 -0
  268. package/lib/third-party/arborist/lib/package-extensions.js +289 -0
  269. package/lib/third-party/arborist/lib/printable.js +210 -0
  270. package/lib/third-party/arborist/lib/proc-log.js +30 -0
  271. package/lib/third-party/arborist/lib/realpath.js +100 -0
  272. package/lib/third-party/arborist/lib/relpath.js +4 -0
  273. package/lib/third-party/arborist/lib/shrinkwrap.js +1291 -0
  274. package/lib/third-party/arborist/lib/spec-from-lock.js +34 -0
  275. package/lib/third-party/arborist/lib/tree-check.js +165 -0
  276. package/lib/third-party/arborist/lib/validate-npm-package-name.js +107 -0
  277. package/lib/third-party/arborist/lib/version-from-tgz.js +51 -0
  278. package/lib/third-party/arborist/lib/yarn-lock.js +397 -0
  279. package/lib/validator/bomValidator.js +985 -0
  280. package/lib/validator/complianceEngine.js +241 -0
  281. package/lib/validator/complianceRules.js +1680 -0
  282. package/lib/validator/generated/runtime.mjs +17 -0
  283. package/lib/validator/generated/validate-1.6.mjs +21258 -0
  284. package/lib/validator/generated/validate-1.7.mjs +24803 -0
  285. package/lib/validator/generated/validate-2.0.mjs +22931 -0
  286. package/lib/validator/index.js +246 -0
  287. package/lib/validator/reporters/annotations.js +142 -0
  288. package/lib/validator/reporters/console.js +160 -0
  289. package/lib/validator/reporters/index.js +41 -0
  290. package/lib/validator/reporters/json.js +37 -0
  291. package/lib/validator/reporters/sarif.js +184 -0
  292. package/package.json +209 -26
  293. package/types/bin/audit.d.ts +3 -0
  294. package/types/bin/audit.d.ts.map +1 -0
  295. package/types/bin/cdxgen.d.ts +3 -0
  296. package/types/bin/cdxgen.d.ts.map +1 -0
  297. package/types/bin/convert.d.ts +3 -0
  298. package/types/bin/convert.d.ts.map +1 -0
  299. package/types/bin/evinse.d.ts +3 -0
  300. package/types/bin/evinse.d.ts.map +1 -0
  301. package/types/bin/hbom.d.ts +3 -0
  302. package/types/bin/hbom.d.ts.map +1 -0
  303. package/types/bin/repl.d.ts +3 -0
  304. package/types/bin/repl.d.ts.map +1 -0
  305. package/types/bin/sign.d.ts +3 -0
  306. package/types/bin/sign.d.ts.map +1 -0
  307. package/types/bin/tracebom.d.ts +3 -0
  308. package/types/bin/tracebom.d.ts.map +1 -0
  309. package/types/bin/validate.d.ts +14 -0
  310. package/types/bin/validate.d.ts.map +1 -0
  311. package/types/bin/verify.d.ts +3 -0
  312. package/types/bin/verify.d.ts.map +1 -0
  313. package/types/lib/audit/index.d.ts +153 -0
  314. package/types/lib/audit/index.d.ts.map +1 -0
  315. package/types/lib/audit/progress.d.ts +31 -0
  316. package/types/lib/audit/progress.d.ts.map +1 -0
  317. package/types/lib/audit/reporters.d.ts +62 -0
  318. package/types/lib/audit/reporters.d.ts.map +1 -0
  319. package/types/lib/audit/scoring.d.ts +36 -0
  320. package/types/lib/audit/scoring.d.ts.map +1 -0
  321. package/types/lib/audit/targets.d.ts +77 -0
  322. package/types/lib/audit/targets.d.ts.map +1 -0
  323. package/types/lib/cli/bomAssembly.d.ts +329 -0
  324. package/types/lib/cli/bomAssembly.d.ts.map +1 -0
  325. package/types/lib/cli/cliOptions.d.ts +156 -0
  326. package/types/lib/cli/cliOptions.d.ts.map +1 -0
  327. package/types/lib/cli/index.d.ts +107 -0
  328. package/types/lib/cli/index.d.ts.map +1 -0
  329. package/types/lib/cli/jsBom.d.ts +94 -0
  330. package/types/lib/cli/jsBom.d.ts.map +1 -0
  331. package/types/lib/cli/jvmBom.d.ts +54 -0
  332. package/types/lib/cli/jvmBom.d.ts.map +1 -0
  333. package/types/lib/cli/managedBom.d.ts +105 -0
  334. package/types/lib/cli/managedBom.d.ts.map +1 -0
  335. package/types/lib/cli/nativeBom.d.ts +143 -0
  336. package/types/lib/cli/nativeBom.d.ts.map +1 -0
  337. package/types/lib/core/activity.d.ts +178 -0
  338. package/types/lib/core/activity.d.ts.map +1 -0
  339. package/types/lib/core/env.d.ts +192 -0
  340. package/types/lib/core/env.d.ts.map +1 -0
  341. package/types/lib/core/fs.d.ts +158 -0
  342. package/types/lib/core/fs.d.ts.map +1 -0
  343. package/types/lib/core/httpClient.d.ts +149 -0
  344. package/types/lib/core/httpClient.d.ts.map +1 -0
  345. package/types/lib/core/logger.d.ts +50 -0
  346. package/types/lib/core/logger.d.ts.map +1 -0
  347. package/types/lib/core/parallel.d.ts +95 -0
  348. package/types/lib/core/parallel.d.ts.map +1 -0
  349. package/types/lib/core/paths.d.ts +36 -0
  350. package/types/lib/core/paths.d.ts.map +1 -0
  351. package/types/lib/core/projectConfig.d.ts +23 -0
  352. package/types/lib/core/projectConfig.d.ts.map +1 -0
  353. package/types/lib/core/propertySanitizer.d.ts +23 -0
  354. package/types/lib/core/propertySanitizer.d.ts.map +1 -0
  355. package/types/lib/core/spdx.d.ts +5 -0
  356. package/types/lib/core/spdx.d.ts.map +1 -0
  357. package/types/lib/core/state.d.ts +20 -0
  358. package/types/lib/core/state.d.ts.map +1 -0
  359. package/types/lib/core/text.d.ts +78 -0
  360. package/types/lib/core/text.d.ts.map +1 -0
  361. package/types/lib/core/ui.d.ts +87 -0
  362. package/types/lib/core/ui.d.ts.map +1 -0
  363. package/types/lib/core/urls.d.ts +43 -0
  364. package/types/lib/core/urls.d.ts.map +1 -0
  365. package/types/lib/ecosystems/asarutils.d.ts +71 -0
  366. package/types/lib/ecosystems/asarutils.d.ts.map +1 -0
  367. package/types/lib/ecosystems/bunutils.d.ts +38 -0
  368. package/types/lib/ecosystems/bunutils.d.ts.map +1 -0
  369. package/types/lib/ecosystems/caxa.d.ts +8 -0
  370. package/types/lib/ecosystems/caxa.d.ts.map +1 -0
  371. package/types/lib/ecosystems/chromextutils.d.ts +97 -0
  372. package/types/lib/ecosystems/chromextutils.d.ts.map +1 -0
  373. package/types/lib/ecosystems/cmakeResolver.d.ts +176 -0
  374. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -0
  375. package/types/lib/ecosystems/cppEvidence.d.ts +35 -0
  376. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -0
  377. package/types/lib/ecosystems/denoutils.d.ts +76 -0
  378. package/types/lib/ecosystems/denoutils.d.ts.map +1 -0
  379. package/types/lib/ecosystems/dotnetutils.d.ts +45 -0
  380. package/types/lib/ecosystems/dotnetutils.d.ts.map +1 -0
  381. package/types/lib/ecosystems/ecosystems.d.ts +279 -0
  382. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -0
  383. package/types/lib/ecosystems/golem.d.ts +60 -0
  384. package/types/lib/ecosystems/golem.d.ts.map +1 -0
  385. package/types/lib/ecosystems/gradleutils.d.ts +139 -0
  386. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -0
  387. package/types/lib/ecosystems/jsEvidence.d.ts +17 -0
  388. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -0
  389. package/types/lib/ecosystems/npmutils.d.ts +183 -0
  390. package/types/lib/ecosystems/npmutils.d.ts.map +1 -0
  391. package/types/lib/ecosystems/parsers-bazel.d.ts +36 -0
  392. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -0
  393. package/types/lib/ecosystems/parsers-dotnet.d.ts +116 -0
  394. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -0
  395. package/types/lib/ecosystems/parsers-gleam.d.ts +33 -0
  396. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -0
  397. package/types/lib/ecosystems/parsers-go.d.ts +92 -0
  398. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -0
  399. package/types/lib/ecosystems/parsers-js.d.ts +242 -0
  400. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -0
  401. package/types/lib/ecosystems/parsers-jvm.d.ts +100 -0
  402. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -0
  403. package/types/lib/ecosystems/parsers-misc.d.ts +450 -0
  404. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -0
  405. package/types/lib/ecosystems/parsers-python.d.ts +145 -0
  406. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -0
  407. package/types/lib/ecosystems/parsers-rust.d.ts +56 -0
  408. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -0
  409. package/types/lib/ecosystems/parsers-zig.d.ts +83 -0
  410. package/types/lib/ecosystems/parsers-zig.d.ts.map +1 -0
  411. package/types/lib/ecosystems/pep770.d.ts +38 -0
  412. package/types/lib/ecosystems/pep770.d.ts.map +1 -0
  413. package/types/lib/ecosystems/pylockutils.d.ts +68 -0
  414. package/types/lib/ecosystems/pylockutils.d.ts.map +1 -0
  415. package/types/lib/ecosystems/pythonutils.d.ts +70 -0
  416. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -0
  417. package/types/lib/ecosystems/registryProvenance.d.ts +26 -0
  418. package/types/lib/ecosystems/registryProvenance.d.ts.map +1 -0
  419. package/types/lib/ecosystems/remote/huggingface.d.ts +31 -0
  420. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -0
  421. package/types/lib/ecosystems/rubyutils.d.ts +182 -0
  422. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -0
  423. package/types/lib/ecosystems/rusi.d.ts +40 -0
  424. package/types/lib/ecosystems/rusi.d.ts.map +1 -0
  425. package/types/lib/ecosystems/sbtutils.d.ts +179 -0
  426. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -0
  427. package/types/lib/ecosystems/tea.d.ts +180 -0
  428. package/types/lib/ecosystems/tea.d.ts.map +1 -0
  429. package/types/lib/ecosystems/utils.d.ts +41 -0
  430. package/types/lib/ecosystems/utils.d.ts.map +1 -0
  431. package/types/lib/ecosystems/zigResolver.d.ts +70 -0
  432. package/types/lib/ecosystems/zigResolver.d.ts.map +1 -0
  433. package/types/lib/evinser/db.d.ts +17 -0
  434. package/types/lib/evinser/db.d.ts.map +1 -0
  435. package/types/lib/evinser/evinser.d.ts +354 -0
  436. package/types/lib/evinser/evinser.d.ts.map +1 -0
  437. package/types/lib/evinser/scalasem.d.ts +15 -0
  438. package/types/lib/evinser/scalasem.d.ts.map +1 -0
  439. package/types/lib/evinser/swiftsem.d.ts +103 -0
  440. package/types/lib/evinser/swiftsem.d.ts.map +1 -0
  441. package/types/lib/helpers/annotationFormatter.d.ts +23 -0
  442. package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
  443. package/types/lib/helpers/bomSigner.d.ts +28 -0
  444. package/types/lib/helpers/bomSigner.d.ts.map +1 -0
  445. package/types/lib/helpers/exportUtils.d.ts +52 -0
  446. package/types/lib/helpers/exportUtils.d.ts.map +1 -0
  447. package/types/lib/helpers/remote/dependency-track.d.ts +23 -0
  448. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
  449. package/types/lib/helpers/versutils.d.ts +8 -0
  450. package/types/lib/helpers/versutils.d.ts.map +1 -0
  451. package/types/lib/helpers/vsixutils.d.ts +130 -0
  452. package/types/lib/helpers/vsixutils.d.ts.map +1 -0
  453. package/types/lib/inventory/agentFormulationParser.d.ts +21 -0
  454. package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -0
  455. package/types/lib/inventory/aiCollector.d.ts +61 -0
  456. package/types/lib/inventory/aiCollector.d.ts.map +1 -0
  457. package/types/lib/inventory/aiInventory.d.ts +103 -0
  458. package/types/lib/inventory/aiInventory.d.ts.map +1 -0
  459. package/types/lib/inventory/aiModelVariants.d.ts +31 -0
  460. package/types/lib/inventory/aiModelVariants.d.ts.map +1 -0
  461. package/types/lib/inventory/aiOversightCollector.d.ts +28 -0
  462. package/types/lib/inventory/aiOversightCollector.d.ts.map +1 -0
  463. package/types/lib/inventory/aiProvenanceCollector.d.ts +13 -0
  464. package/types/lib/inventory/aiProvenanceCollector.d.ts.map +1 -0
  465. package/types/lib/inventory/analyzer.d.ts +157 -0
  466. package/types/lib/inventory/analyzer.d.ts.map +1 -0
  467. package/types/lib/inventory/analyzerScope.d.ts +106 -0
  468. package/types/lib/inventory/analyzerScope.d.ts.map +1 -0
  469. package/types/lib/inventory/astWorker.d.ts +9 -0
  470. package/types/lib/inventory/astWorker.d.ts.map +1 -0
  471. package/types/lib/inventory/atomUtils.d.ts +159 -0
  472. package/types/lib/inventory/atomUtils.d.ts.map +1 -0
  473. package/types/lib/inventory/auditCategories.d.ts +80 -0
  474. package/types/lib/inventory/auditCategories.d.ts.map +1 -0
  475. package/types/lib/inventory/bomUtils.d.ts +145 -0
  476. package/types/lib/inventory/bomUtils.d.ts.map +1 -0
  477. package/types/lib/inventory/cacheCommands.d.ts +28 -0
  478. package/types/lib/inventory/cacheCommands.d.ts.map +1 -0
  479. package/types/lib/inventory/cacheDir.d.ts +55 -0
  480. package/types/lib/inventory/cacheDir.d.ts.map +1 -0
  481. package/types/lib/inventory/cbomutils.d.ts +37 -0
  482. package/types/lib/inventory/cbomutils.d.ts.map +1 -0
  483. package/types/lib/inventory/cdxrs.d.ts +90 -0
  484. package/types/lib/inventory/cdxrs.d.ts.map +1 -0
  485. package/types/lib/inventory/ciParsers/azurePipelines.d.ts +27 -0
  486. package/types/lib/inventory/ciParsers/azurePipelines.d.ts.map +1 -0
  487. package/types/lib/inventory/ciParsers/circleCi.d.ts +27 -0
  488. package/types/lib/inventory/ciParsers/circleCi.d.ts.map +1 -0
  489. package/types/lib/inventory/ciParsers/common.d.ts +11 -0
  490. package/types/lib/inventory/ciParsers/common.d.ts.map +1 -0
  491. package/types/lib/inventory/ciParsers/githubActions.d.ts +38 -0
  492. package/types/lib/inventory/ciParsers/githubActions.d.ts.map +1 -0
  493. package/types/lib/inventory/ciParsers/gitlabCi.d.ts +26 -0
  494. package/types/lib/inventory/ciParsers/gitlabCi.d.ts.map +1 -0
  495. package/types/lib/inventory/ciParsers/jenkins.d.ts +26 -0
  496. package/types/lib/inventory/ciParsers/jenkins.d.ts.map +1 -0
  497. package/types/lib/inventory/citations.d.ts +90 -0
  498. package/types/lib/inventory/citations.d.ts.map +1 -0
  499. package/types/lib/inventory/communityAiConfigParser.d.ts +14 -0
  500. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -0
  501. package/types/lib/inventory/containerRisk.d.ts +20 -0
  502. package/types/lib/inventory/containerRisk.d.ts.map +1 -0
  503. package/types/lib/inventory/cryptoAlgorithmFamily.d.ts +37 -0
  504. package/types/lib/inventory/cryptoAlgorithmFamily.d.ts.map +1 -0
  505. package/types/lib/inventory/deps.d.ts +140 -0
  506. package/types/lib/inventory/deps.d.ts.map +1 -0
  507. package/types/lib/inventory/depsUtils.d.ts +74 -0
  508. package/types/lib/inventory/depsUtils.d.ts.map +1 -0
  509. package/types/lib/inventory/display.d.ts +227 -0
  510. package/types/lib/inventory/display.d.ts.map +1 -0
  511. package/types/lib/inventory/dosai.d.ts +130 -0
  512. package/types/lib/inventory/dosai.d.ts.map +1 -0
  513. package/types/lib/inventory/dosaiParsers.d.ts +59 -0
  514. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -0
  515. package/types/lib/inventory/dynamic.d.ts +25 -0
  516. package/types/lib/inventory/dynamic.d.ts.map +1 -0
  517. package/types/lib/inventory/envcontext.d.ts +377 -0
  518. package/types/lib/inventory/envcontext.d.ts.map +1 -0
  519. package/types/lib/inventory/evidenceUtils.d.ts +74 -0
  520. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -0
  521. package/types/lib/inventory/fetchBatch.d.ts +127 -0
  522. package/types/lib/inventory/fetchBatch.d.ts.map +1 -0
  523. package/types/lib/inventory/fetchRate.d.ts +118 -0
  524. package/types/lib/inventory/fetchRate.d.ts.map +1 -0
  525. package/types/lib/inventory/forgeEnricher.d.ts +31 -0
  526. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -0
  527. package/types/lib/inventory/formulationParsers.d.ts +50 -0
  528. package/types/lib/inventory/formulationParsers.d.ts.map +1 -0
  529. package/types/lib/inventory/gtfobins.d.ts +27 -0
  530. package/types/lib/inventory/gtfobins.d.ts.map +1 -0
  531. package/types/lib/inventory/hbom.d.ts +58 -0
  532. package/types/lib/inventory/hbom.d.ts.map +1 -0
  533. package/types/lib/inventory/hbomAnalysis.d.ts +56 -0
  534. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -0
  535. package/types/lib/inventory/hbomLoader.d.ts +7 -0
  536. package/types/lib/inventory/hbomLoader.d.ts.map +1 -0
  537. package/types/lib/inventory/hostTopology.d.ts +47 -0
  538. package/types/lib/inventory/hostTopology.d.ts.map +1 -0
  539. package/types/lib/inventory/inventoryStats.d.ts +42 -0
  540. package/types/lib/inventory/inventoryStats.d.ts.map +1 -0
  541. package/types/lib/inventory/jsonLike.d.ts +23 -0
  542. package/types/lib/inventory/jsonLike.d.ts.map +1 -0
  543. package/types/lib/inventory/licenseEnhancer.d.ts +70 -0
  544. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -0
  545. package/types/lib/inventory/lolbas.d.ts +16 -0
  546. package/types/lib/inventory/lolbas.d.ts.map +1 -0
  547. package/types/lib/inventory/mcp.d.ts +29 -0
  548. package/types/lib/inventory/mcp.d.ts.map +1 -0
  549. package/types/lib/inventory/mcpConfigParser.d.ts +20 -0
  550. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -0
  551. package/types/lib/inventory/mcpDiscovery.d.ts +33 -0
  552. package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -0
  553. package/types/lib/inventory/mcpPinning.d.ts +11 -0
  554. package/types/lib/inventory/mcpPinning.d.ts.map +1 -0
  555. package/types/lib/inventory/osPackageResolver.d.ts +49 -0
  556. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -0
  557. package/types/lib/inventory/osinfo.d.ts +201 -0
  558. package/types/lib/inventory/osinfo.d.ts.map +1 -0
  559. package/types/lib/inventory/osqueryTransform.d.ts +84 -0
  560. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -0
  561. package/types/lib/inventory/plugins.d.ts +58 -0
  562. package/types/lib/inventory/plugins.d.ts.map +1 -0
  563. package/types/lib/inventory/protobom.d.ts +71 -0
  564. package/types/lib/inventory/protobom.d.ts.map +1 -0
  565. package/types/lib/inventory/protobomLoader.d.ts +17 -0
  566. package/types/lib/inventory/protobomLoader.d.ts.map +1 -0
  567. package/types/lib/inventory/provenanceUtils.d.ts +98 -0
  568. package/types/lib/inventory/provenanceUtils.d.ts.map +1 -0
  569. package/types/lib/inventory/purl.d.ts +313 -0
  570. package/types/lib/inventory/purl.d.ts.map +1 -0
  571. package/types/lib/inventory/rustFormulationParser.d.ts +17 -0
  572. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -0
  573. package/types/lib/inventory/sbomDocument.d.ts +59 -0
  574. package/types/lib/inventory/sbomDocument.d.ts.map +1 -0
  575. package/types/lib/inventory/source.d.ts +163 -0
  576. package/types/lib/inventory/source.d.ts.map +1 -0
  577. package/types/lib/inventory/spdx.d.ts +66 -0
  578. package/types/lib/inventory/spdx.d.ts.map +1 -0
  579. package/types/lib/inventory/spdxExpression.d.ts +58 -0
  580. package/types/lib/inventory/spdxExpression.d.ts.map +1 -0
  581. package/types/lib/inventory/spdxUtils.d.ts +12 -0
  582. package/types/lib/inventory/spdxUtils.d.ts.map +1 -0
  583. package/types/lib/inventory/table.d.ts +22 -0
  584. package/types/lib/inventory/table.d.ts.map +1 -0
  585. package/types/lib/inventory/traceRunner.d.ts +80 -0
  586. package/types/lib/inventory/traceRunner.d.ts.map +1 -0
  587. package/types/lib/inventory/unicodeScan.d.ts +46 -0
  588. package/types/lib/inventory/unicodeScan.d.ts.map +1 -0
  589. package/types/lib/managers/binary.d.ts +147 -0
  590. package/types/lib/managers/binary.d.ts.map +1 -0
  591. package/types/lib/managers/containerutils.d.ts +30 -0
  592. package/types/lib/managers/containerutils.d.ts.map +1 -0
  593. package/types/lib/managers/docker.d.ts +157 -0
  594. package/types/lib/managers/docker.d.ts.map +1 -0
  595. package/types/lib/managers/dockerConnection.d.ts +42 -0
  596. package/types/lib/managers/dockerConnection.d.ts.map +1 -0
  597. package/types/lib/managers/oci.d.ts +21 -0
  598. package/types/lib/managers/oci.d.ts.map +1 -0
  599. package/types/lib/managers/piptree.d.ts +13 -0
  600. package/types/lib/managers/piptree.d.ts.map +1 -0
  601. package/types/lib/parsers/cmakeCache.d.ts +70 -0
  602. package/types/lib/parsers/cmakeCache.d.ts.map +1 -0
  603. package/types/lib/parsers/gguf.d.ts +39 -0
  604. package/types/lib/parsers/gguf.d.ts.map +1 -0
  605. package/types/lib/parsers/gitAiNotes.d.ts +15 -0
  606. package/types/lib/parsers/gitAiNotes.d.ts.map +1 -0
  607. package/types/lib/parsers/gitmodules.d.ts +88 -0
  608. package/types/lib/parsers/gitmodules.d.ts.map +1 -0
  609. package/types/lib/parsers/htmlExtract.d.ts +34 -0
  610. package/types/lib/parsers/htmlExtract.d.ts.map +1 -0
  611. package/types/lib/parsers/huggingfaceManifest.d.ts +153 -0
  612. package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -0
  613. package/types/lib/parsers/huggingfaceUtils.d.ts +155 -0
  614. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -0
  615. package/types/lib/parsers/iri.d.ts +68 -0
  616. package/types/lib/parsers/iri.d.ts.map +1 -0
  617. package/types/lib/parsers/npmrc.d.ts +33 -0
  618. package/types/lib/parsers/npmrc.d.ts.map +1 -0
  619. package/types/lib/parsers/ollama.d.ts +22 -0
  620. package/types/lib/parsers/ollama.d.ts.map +1 -0
  621. package/types/lib/parsers/xml.d.ts +47 -0
  622. package/types/lib/parsers/xml.d.ts.map +1 -0
  623. package/types/lib/server/server.d.ts +51 -0
  624. package/types/lib/server/server.d.ts.map +1 -0
  625. package/types/lib/stages/postgen/annotator.d.ts +36 -0
  626. package/types/lib/stages/postgen/annotator.d.ts.map +1 -0
  627. package/types/lib/stages/postgen/auditBom.d.ts +92 -0
  628. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -0
  629. package/types/lib/stages/postgen/postgen.d.ts +59 -0
  630. package/types/lib/stages/postgen/postgen.d.ts.map +1 -0
  631. package/types/lib/stages/postgen/ruleEngine.d.ts +18 -0
  632. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -0
  633. package/types/lib/stages/postgen/sortBom.d.ts +34 -0
  634. package/types/lib/stages/postgen/sortBom.d.ts.map +1 -0
  635. package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
  636. package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
  637. package/types/lib/stages/postgen/specVersionCompat.d.ts +71 -0
  638. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -0
  639. package/types/lib/stages/pregen/envAudit.d.ts +14 -0
  640. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -0
  641. package/types/lib/stages/pregen/pregen.d.ts +59 -0
  642. package/types/lib/stages/pregen/pregen.d.ts.map +1 -0
  643. package/types/lib/third-party/arborist/lib/arborist/index.d.ts +47 -0
  644. package/types/lib/third-party/arborist/lib/arborist/index.d.ts.map +1 -0
  645. package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts +4 -0
  646. package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts.map +1 -0
  647. package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts +4 -0
  648. package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts.map +1 -0
  649. package/types/lib/third-party/arborist/lib/bin-links.d.ts +18 -0
  650. package/types/lib/third-party/arborist/lib/bin-links.d.ts.map +1 -0
  651. package/types/lib/third-party/arborist/lib/calc-dep-flags.d.ts +3 -0
  652. package/types/lib/third-party/arborist/lib/calc-dep-flags.d.ts.map +1 -0
  653. package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts +9 -0
  654. package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts.map +1 -0
  655. package/types/lib/third-party/arborist/lib/consistent-resolve.d.ts +3 -0
  656. package/types/lib/third-party/arborist/lib/consistent-resolve.d.ts.map +1 -0
  657. package/types/lib/third-party/arborist/lib/debug.d.ts +3 -0
  658. package/types/lib/third-party/arborist/lib/debug.d.ts.map +1 -0
  659. package/types/lib/third-party/arborist/lib/dep-valid.d.ts +3 -0
  660. package/types/lib/third-party/arborist/lib/dep-valid.d.ts.map +1 -0
  661. package/types/lib/third-party/arborist/lib/edge.d.ts +46 -0
  662. package/types/lib/third-party/arborist/lib/edge.d.ts.map +1 -0
  663. package/types/lib/third-party/arborist/lib/from-path.d.ts +3 -0
  664. package/types/lib/third-party/arborist/lib/from-path.d.ts.map +1 -0
  665. package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts +3 -0
  666. package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts.map +1 -0
  667. package/types/lib/third-party/arborist/lib/index.d.ts +3 -0
  668. package/types/lib/third-party/arborist/lib/index.d.ts.map +1 -0
  669. package/types/lib/third-party/arborist/lib/inventory.d.ts +14 -0
  670. package/types/lib/third-party/arborist/lib/inventory.d.ts.map +1 -0
  671. package/types/lib/third-party/arborist/lib/link.d.ts +18 -0
  672. package/types/lib/third-party/arborist/lib/link.d.ts.map +1 -0
  673. package/types/lib/third-party/arborist/lib/node.d.ts +112 -0
  674. package/types/lib/third-party/arborist/lib/node.d.ts.map +1 -0
  675. package/types/lib/third-party/arborist/lib/npm-extension.d.ts +18 -0
  676. package/types/lib/third-party/arborist/lib/npm-extension.d.ts.map +1 -0
  677. package/types/lib/third-party/arborist/lib/override-resolves.d.ts +3 -0
  678. package/types/lib/third-party/arborist/lib/override-resolves.d.ts.map +1 -0
  679. package/types/lib/third-party/arborist/lib/override-set.d.ts +27 -0
  680. package/types/lib/third-party/arborist/lib/override-set.d.ts.map +1 -0
  681. package/types/lib/third-party/arborist/lib/package-extensions.d.ts +36 -0
  682. package/types/lib/third-party/arborist/lib/package-extensions.d.ts.map +1 -0
  683. package/types/lib/third-party/arborist/lib/printable.d.ts +3 -0
  684. package/types/lib/third-party/arborist/lib/printable.d.ts.map +1 -0
  685. package/types/lib/third-party/arborist/lib/proc-log.d.ts +20 -0
  686. package/types/lib/third-party/arborist/lib/proc-log.d.ts.map +1 -0
  687. package/types/lib/third-party/arborist/lib/realpath.d.ts +3 -0
  688. package/types/lib/third-party/arborist/lib/realpath.d.ts.map +1 -0
  689. package/types/lib/third-party/arborist/lib/relpath.d.ts +3 -0
  690. package/types/lib/third-party/arborist/lib/relpath.d.ts.map +1 -0
  691. package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts +54 -0
  692. package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts.map +1 -0
  693. package/types/lib/third-party/arborist/lib/spec-from-lock.d.ts +3 -0
  694. package/types/lib/third-party/arborist/lib/spec-from-lock.d.ts.map +1 -0
  695. package/types/lib/third-party/arborist/lib/tree-check.d.ts +3 -0
  696. package/types/lib/third-party/arborist/lib/tree-check.d.ts.map +1 -0
  697. package/types/lib/third-party/arborist/lib/validate-npm-package-name.d.ts +13 -0
  698. package/types/lib/third-party/arborist/lib/validate-npm-package-name.d.ts.map +1 -0
  699. package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts +6 -0
  700. package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts.map +1 -0
  701. package/types/lib/third-party/arborist/lib/yarn-lock.d.ts +35 -0
  702. package/types/lib/third-party/arborist/lib/yarn-lock.d.ts.map +1 -0
  703. package/types/lib/validator/bomValidator.d.ts +40 -0
  704. package/types/lib/validator/bomValidator.d.ts.map +1 -0
  705. package/types/lib/validator/complianceEngine.d.ts +72 -0
  706. package/types/lib/validator/complianceEngine.d.ts.map +1 -0
  707. package/types/lib/validator/complianceRules.d.ts +61 -0
  708. package/types/lib/validator/complianceRules.d.ts.map +1 -0
  709. package/types/lib/validator/index.d.ts +101 -0
  710. package/types/lib/validator/index.d.ts.map +1 -0
  711. package/types/lib/validator/reporters/annotations.d.ts +37 -0
  712. package/types/lib/validator/reporters/annotations.d.ts.map +1 -0
  713. package/types/lib/validator/reporters/console.d.ts +34 -0
  714. package/types/lib/validator/reporters/console.d.ts.map +1 -0
  715. package/types/lib/validator/reporters/index.d.ts +30 -0
  716. package/types/lib/validator/reporters/index.d.ts.map +1 -0
  717. package/types/lib/validator/reporters/json.d.ts +11 -0
  718. package/types/lib/validator/reporters/json.d.ts.map +1 -0
  719. package/types/lib/validator/reporters/sarif.d.ts +22 -0
  720. package/types/lib/validator/reporters/sarif.d.ts.map +1 -0
  721. package/mod.d.ts +0 -7
  722. package/mod.js +0 -22
@@ -0,0 +1,1629 @@
1
+ import { readdirSync, readFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { basename, dirname, join } from "node:path";
4
+ import process from "node:process";
5
+
6
+ import { build, Purl } from "@cdxgen/cdx-purl";
7
+
8
+ // utils.js does not import rubyutils.js, so importing from it here is safe and
9
+ // does not introduce a cyclic dependency.
10
+ import {
11
+ cdxgenAgent,
12
+ DEBUG_MODE,
13
+ readEnvironmentVariable,
14
+ } from "../core/activity.js";
15
+ import { shouldFetchLicense } from "../core/env.js";
16
+ import { getAllFiles, safeExistsSync, safeSpawnSync } from "../core/fs.js";
17
+ import { dirNameStr, isWin } from "../core/paths.js";
18
+ import {
19
+ prefetchEnabled,
20
+ prefetchedResponse,
21
+ prefetchJson,
22
+ } from "../inventory/fetchBatch.js";
23
+
24
+ // FIXME. This has to get removed, once we improve the module detection one-liner.
25
+ // If you're a Rubyist, please help us improve this code.
26
+ const RUBY_KNOWN_MODULES = JSON.parse(
27
+ readFileSync(join(dirNameStr, "data", "ruby-known-modules.json"), "utf-8"),
28
+ );
29
+
30
+ // `Gem::Platform#to_s` is `[cpu, os, os_version].compact.join("-")`, which
31
+ // Bundler writes as a `-` separated suffix to the version in the `specs:`
32
+ // section of Gemfile.lock. Examples of the suffix: `x86_64-linux`,
33
+ // `x86_64-linux-musl`, `aarch64-linux-gnu`, `arm64-darwin`, `x64-mingw-ucrt`,
34
+ // `x86-mswin32-60`, `universal-darwin-20`, `java`, `universal-java-11`.
35
+ // The cpu is an open token, so there is no exhaustive list to match against.
36
+ // The os component, however, comes from a fixed table in `Gem::Platform`.
37
+ const RUBY_PLATFORM_OSES = new Set([
38
+ "aix",
39
+ "cygwin",
40
+ "dalvik",
41
+ "darwin",
42
+ "dotnet",
43
+ "freebsd",
44
+ "java",
45
+ "jruby",
46
+ "linux",
47
+ "macruby",
48
+ "mingw",
49
+ "mingw32",
50
+ "mswin32",
51
+ "mswin64",
52
+ "netbsdelf",
53
+ "openbsd",
54
+ "ruby",
55
+ "solaris",
56
+ "wasi",
57
+ ]);
58
+
59
+ /**
60
+ * Is the given string a `Gem::Platform` such as `x86_64-linux` or `java`?
61
+ * The cpu component is an open token in RubyGems, so we validate by looking
62
+ * for a known os name in any position.
63
+ *
64
+ * Note that `truffleruby` is deliberately absent from the os table.
65
+ * `Gem::Platform.new("truffleruby").to_s` is `"unknown"`: TruffleRuby reports a
66
+ * conventional local platform such as `x86_64-linux`, and reuses plain ruby
67
+ * gems via an allowlist rather than through a platform of its own. So
68
+ * `truffleruby` never appears as a platform suffix.
69
+ *
70
+ * @param {string} value Candidate platform string
71
+ * @returns {boolean} true if the value looks like a gem platform
72
+ */
73
+ export function isRubyPlatform(value) {
74
+ if (!value?.length) {
75
+ return false;
76
+ }
77
+ return value.split("-").some((segment) => RUBY_PLATFORM_OSES.has(segment));
78
+ }
79
+
80
+ /**
81
+ * Normalize a gem platform the way `Gem::Platform` does.
82
+ *
83
+ * The only alias that matters for a lockfile or a gemspec is `jruby`, which
84
+ * `Gem::Platform` maps to the os `java`, so `Gem::Platform.new("jruby").to_s`
85
+ * is `"java"`. Normalizing keeps a JRuby gem from being reported under two
86
+ * different purls depending on which spelling the source used.
87
+ *
88
+ * @param {string | undefined} platform Platform to normalize
89
+ * @returns {string | undefined} Normalized platform
90
+ */
91
+ export function normalizeGemPlatform(platform) {
92
+ if (!platform?.length) {
93
+ return platform;
94
+ }
95
+ return platform === "jruby" ? "java" : platform;
96
+ }
97
+
98
+ /**
99
+ * Split a gem version string into its version and optional native platform.
100
+ *
101
+ * Bundler writes native gems as `name (version-platform)`, for example
102
+ * `google-protobuf (3.25.1-x86_64-linux)`. This mirrors how Bundler itself
103
+ * parses the lockfile: its `NAME_VERSION` regex captures the version as
104
+ * `([^-]*)` followed by an optional `-(.*)` platform, i.e. it splits at the
105
+ * *first* hyphen. That is unambiguous because `Gem::Version` rewrites any `-`
106
+ * to `.pre.` on construction, so a canonical version string, which is what
107
+ * Bundler writes to the lockfile, never contains a hyphen.
108
+ *
109
+ * @param {string | undefined} version Version that may include a platform suffix
110
+ * @returns {{version: (string | undefined), platform: (string | undefined)}} Version and platform
111
+ */
112
+ export function splitRubyVersionPlatform(version) {
113
+ if (!version?.length) {
114
+ return { version, platform: undefined };
115
+ }
116
+ const hyphenIndex = version.indexOf("-");
117
+ // A leading hyphen, or nothing after it, is not a platform suffix
118
+ if (hyphenIndex < 1 || hyphenIndex === version.length - 1) {
119
+ return { version, platform: undefined };
120
+ }
121
+ return {
122
+ version: version.slice(0, hyphenIndex),
123
+ platform: version.slice(hyphenIndex + 1),
124
+ };
125
+ }
126
+
127
+ /**
128
+ * Simplify the ruby version by removing platform suffixes
129
+ *
130
+ * @param {string} version Version to simplify
131
+ * @returns {string} Simplified version
132
+ */
133
+ export function simplifyRubyVersion(version) {
134
+ return splitRubyVersionPlatform(version).version;
135
+ }
136
+
137
+ /**
138
+ * Construct a gem purl. Per the purl specification, the native platform is
139
+ * represented with the `platform` qualifier and not as part of the version.
140
+ * `ruby` is the implied default platform, so it is left out.
141
+ *
142
+ * @param {string} name Gem name
143
+ * @param {string | undefined} version Gem version without any platform suffix
144
+ * @param {string | undefined} platform Gem platform such as `x86_64-linux`
145
+ * @returns {string} purl string
146
+ */
147
+ export function toGemPurl(name, version, platform) {
148
+ const gemPlatform = normalizeGemPlatform(platform);
149
+ return build({
150
+ type: "gem",
151
+ namespace: "" || null,
152
+ name: name,
153
+ version: version || null,
154
+ qualifiers:
155
+ gemPlatform && gemPlatform !== "ruby"
156
+ ? { platform: gemPlatform }
157
+ : null || null,
158
+ });
159
+ }
160
+
161
+ // Well known `Gem::Specification#metadata` keys, mapped to the CycloneDX
162
+ // external reference types. See
163
+ // https://guides.rubygems.org/specification-reference/#metadata
164
+ const GEM_METADATA_REFERENCE_TYPES = {
165
+ bug_tracker_uri: "issue-tracker",
166
+ changelog_uri: "release-notes",
167
+ documentation_uri: "documentation",
168
+ funding_uri: "other",
169
+ homepage_uri: "website",
170
+ mailing_list_uri: "other",
171
+ source_code_uri: "vcs",
172
+ wiki_uri: "documentation",
173
+ };
174
+
175
+ /**
176
+ * Parse a ruby array literal as found in a gemspec, such as
177
+ * `["ext/nokogiri/extconf.rb".freeze]` or `%w[a b]`.
178
+ *
179
+ * @param {string} value Ruby array literal
180
+ * @returns {Array<string>} Parsed entries
181
+ */
182
+ function parseGemspecArray(value) {
183
+ if (!value) {
184
+ return [];
185
+ }
186
+ return value
187
+ .replaceAll(".freeze", "")
188
+ .replace(/^%w?[[(]/, "")
189
+ .replace(/^\[/, "")
190
+ .replace(/[\])].*$/, "")
191
+ .split(",")
192
+ .map((s) => s.trim().replace(/["']/g, ""))
193
+ .filter((s) => s.length);
194
+ }
195
+
196
+ /**
197
+ * Parse a `Gem::Requirement` literal as found in a gemspec, such as
198
+ * `Gem::Requirement.new([">= 2.2".freeze, "< 4.0".freeze])` or
199
+ * `Gem::Requirement.new(">= 3.0".freeze)`.
200
+ *
201
+ * @param {string} value Requirement literal
202
+ * @returns {string | undefined} Comma separated requirement string
203
+ */
204
+ function parseGemRequirement(value) {
205
+ if (!value) {
206
+ return undefined;
207
+ }
208
+ const inner = value.includes("Gem::Requirement.new")
209
+ ? value.replace(/.*Gem::Requirement\.new\(/, "").replace(/\).*$/, "")
210
+ : value;
211
+ const parts = inner
212
+ .replaceAll(".freeze", "")
213
+ .replace(/^\[/, "")
214
+ .replace(/].*$/, "")
215
+ .split(",")
216
+ .map((s) => s.trim().replace(/["';]/g, ""))
217
+ .filter((s) => s.length && s !== "nil");
218
+ return parts.length ? parts.join(", ") : undefined;
219
+ }
220
+
221
+ // Digest algorithms Bundler may record in the CHECKSUMS section, mapped to the
222
+ // CycloneDX hash algorithm names.
223
+ const GEM_CHECKSUM_ALGOS = {
224
+ sha1: "SHA-1",
225
+ sha256: "SHA-256",
226
+ sha512: "SHA-512",
227
+ };
228
+
229
+ /**
230
+ * Parse a single line from the `CHECKSUMS` section of a Gemfile.lock. Bundler
231
+ * 2.5 onwards writes `name (version[-platform]) algo=digest[,algo=digest]`,
232
+ * where the `name (version[-platform])` token is identical to the one used in
233
+ * the `specs:` section. The digest is lowercase hex. An entry may carry no
234
+ * checksum at all when Bundler could not obtain one.
235
+ *
236
+ * @param {string} line Trimmed line from the CHECKSUMS section
237
+ * @returns {{lockName: string, hashes: Array<object>} | undefined} Lock name and CycloneDX hashes
238
+ */
239
+ export function parseGemChecksumLine(line) {
240
+ const match = line?.match(/^(\S+) \(([^)]+)\)(?:\s+(\S+))?$/);
241
+ if (!match) {
242
+ return undefined;
243
+ }
244
+ const [, name, version, checksums] = match;
245
+ const hashes = [];
246
+ for (const achecksum of (checksums || "").split(",")) {
247
+ const [algo, digest] = achecksum.split("=");
248
+ const alg = GEM_CHECKSUM_ALGOS[algo?.toLowerCase()];
249
+ // Bundler always writes hex, but guard against anything else
250
+ if (alg && /^[0-9a-f]+$/.test(digest || "")) {
251
+ hashes.push({ alg, content: digest });
252
+ }
253
+ }
254
+ if (!hashes.length) {
255
+ return undefined;
256
+ }
257
+ return { lockName: `${name} (${version})`, hashes };
258
+ }
259
+
260
+ /**
261
+ * Default location of Bundler's compact index cache. This is the protocol
262
+ * Bundler itself uses to resolve, so any gem the developer has ever installed
263
+ * has a cached `info/<gem>` file here.
264
+ *
265
+ * @returns {string} Path to the compact index cache directory
266
+ */
267
+ export function getCompactIndexCacheDir() {
268
+ return (
269
+ readEnvironmentVariable("CDXGEN_COMPACT_INDEX_CACHE_DIR") ||
270
+ join(
271
+ readEnvironmentVariable("BUNDLE_USER_CACHE") ||
272
+ join(homedir(), ".bundle", "cache"),
273
+ "compact_index",
274
+ )
275
+ );
276
+ }
277
+
278
+ /**
279
+ * Parse the contents of a Bundler compact index `info/<gem>` file. Each line
280
+ * describes one release:
281
+ *
282
+ * `VERSION[-PLATFORM] <deps>|checksum:<sha256>,ruby:<req>,rubygems:<req>,...`
283
+ *
284
+ * This is the cheapest source of gem metadata available: a single local file
285
+ * carries the sha256, the runtime dependencies and the required ruby and
286
+ * rubygems versions for every release of a gem.
287
+ *
288
+ * @param {string} infoData Contents of an info file
289
+ * @returns {object} Map of `version[-platform]` to release metadata
290
+ */
291
+ export function parseCompactIndexInfo(infoData) {
292
+ const releases = {};
293
+ if (!infoData) {
294
+ return releases;
295
+ }
296
+ for (const aline of infoData.split("\n")) {
297
+ const line = aline.trim();
298
+ if (!line.length || line === "---") {
299
+ continue;
300
+ }
301
+ const pipeIndex = line.lastIndexOf("|");
302
+ const spec = (pipeIndex > -1 ? line.slice(0, pipeIndex) : line).trim();
303
+ const fullVersion = spec.split(" ")[0];
304
+ if (!fullVersion?.length) {
305
+ continue;
306
+ }
307
+ const release = {};
308
+ if (pipeIndex > -1) {
309
+ for (const requirement of line.slice(pipeIndex + 1).split(",")) {
310
+ const separator = requirement.indexOf(":");
311
+ if (separator < 1) {
312
+ continue;
313
+ }
314
+ release[requirement.slice(0, separator)] = requirement
315
+ .slice(separator + 1)
316
+ .trim();
317
+ }
318
+ }
319
+ releases[fullVersion] = release;
320
+ }
321
+ return releases;
322
+ }
323
+
324
+ /**
325
+ * Parse a `.bundle/config` file. Bundler writes a small YAML document of
326
+ * `BUNDLE_<SETTING>: "value"` pairs. Two settings matter for an SBOM:
327
+ * `BUNDLE_PATH`, which tells us where the gems actually live, and
328
+ * `BUNDLE_WITHOUT`, which tells us that some groups were never installed and
329
+ * that the SBOM is therefore incomplete by construction.
330
+ *
331
+ * @param {string} configFile Path to a .bundle/config file
332
+ * @returns {object} Map of setting name to value
333
+ */
334
+ export function parseBundleConfig(configFile) {
335
+ const settings = {};
336
+ if (!configFile || !safeExistsSync(configFile)) {
337
+ return settings;
338
+ }
339
+ let configData;
340
+ try {
341
+ configData = readFileSync(configFile, { encoding: "utf-8" });
342
+ } catch (_err) {
343
+ return settings;
344
+ }
345
+ for (const aline of configData.split("\n")) {
346
+ const line = aline.trim();
347
+ if (!line.length || line.startsWith("#") || line.startsWith("---")) {
348
+ continue;
349
+ }
350
+ const separator = line.indexOf(":");
351
+ if (separator < 1) {
352
+ continue;
353
+ }
354
+ const key = line.slice(0, separator).trim();
355
+ if (!key.startsWith("BUNDLE_")) {
356
+ continue;
357
+ }
358
+ settings[key] = line
359
+ .slice(separator + 1)
360
+ .trim()
361
+ .replace(/^["']|["']$/g, "");
362
+ }
363
+ return settings;
364
+ }
365
+
366
+ /**
367
+ * Enrich gem components from the caches present on the machine, without making
368
+ * any network calls. Two sources are consulted:
369
+ *
370
+ * 1. Bundler's compact index cache, for the sha256 and the required ruby and
371
+ * rubygems versions of the exact release, including native variants.
372
+ * 2. The installed gemspecs under `GEM_HOME/specifications`, for licenses,
373
+ * description, project URIs and the native extension list.
374
+ *
375
+ * Because this only reads files it also works in dry-run mode, where the
376
+ * registry lookups performed by `getRubyGemsMetadata` are blocked.
377
+ *
378
+ * @param {Array} pkgList List of gem components to enrich in place
379
+ * @param {object} options Options
380
+ * @param {string} options.gemHome GEM_HOME to read installed gemspecs from
381
+ * @param {string} options.compactIndexCacheDir Bundler compact index cache directory
382
+ * @returns {Promise<Array>} The enriched package list
383
+ */
384
+ export async function enrichGemsFromLocalCache(pkgList, options = {}) {
385
+ if (!pkgList?.length) {
386
+ return pkgList;
387
+ }
388
+ const compactIndexCacheDir =
389
+ options.compactIndexCacheDir || getCompactIndexCacheDir();
390
+ const gemHome = options.gemHome;
391
+ // A gem may be cached under several remotes. The cache lays out one
392
+ // `<host>.<port>.<hash>/info/<gem>` directory per remote, so a shallow listing
393
+ // of the remotes is enough to build the candidate paths for a given gem. That
394
+ // is far cheaper than walking every cached info file.
395
+ let infoDirs = [];
396
+ if (safeExistsSync(compactIndexCacheDir)) {
397
+ try {
398
+ infoDirs = readdirSync(compactIndexCacheDir, { withFileTypes: true })
399
+ .filter((anentry) => anentry.isDirectory())
400
+ .map((anentry) => join(compactIndexCacheDir, anentry.name, "info"))
401
+ .filter((adir) => safeExistsSync(adir));
402
+ } catch (_err) {
403
+ // pass
404
+ }
405
+ }
406
+ // Installed gemspecs are named `<name>-<version>[-<platform>].gemspec`
407
+ const gemspecsByFullName = {};
408
+ if (gemHome && safeExistsSync(gemHome)) {
409
+ for (const gemspecFile of getAllFiles(
410
+ gemHome,
411
+ "**/specifications/**/*.gemspec",
412
+ { noIgnore: true },
413
+ )) {
414
+ gemspecsByFullName[basename(gemspecFile).replace(".gemspec", "")] =
415
+ gemspecFile;
416
+ }
417
+ }
418
+ if (!infoDirs.length && !Object.keys(gemspecsByFullName).length) {
419
+ return pkgList;
420
+ }
421
+ const releasesCache = {};
422
+ for (const p of pkgList) {
423
+ if (!p.name || !p.version) {
424
+ continue;
425
+ }
426
+ const platform = p.purl
427
+ ? Purl.parse(p.purl).qualifiers?.platform
428
+ : undefined;
429
+ const fullVersion = platform ? `${p.version}-${platform}` : p.version;
430
+ const fullName = `${p.name}-${fullVersion}`;
431
+ // 1. Compact index cache
432
+ if (!releasesCache[p.name]) {
433
+ releasesCache[p.name] = {};
434
+ for (const infoDir of infoDirs) {
435
+ const infoFile = join(infoDir, p.name);
436
+ if (!safeExistsSync(infoFile)) {
437
+ continue;
438
+ }
439
+ try {
440
+ Object.assign(
441
+ releasesCache[p.name],
442
+ parseCompactIndexInfo(
443
+ readFileSync(infoFile, { encoding: "utf-8" }),
444
+ ),
445
+ );
446
+ } catch (_err) {
447
+ // pass
448
+ }
449
+ }
450
+ }
451
+ const release = releasesCache[p.name]?.[fullVersion];
452
+ if (release) {
453
+ if (release.checksum && !p.hashes?.length && !p._integrity) {
454
+ p._integrity = `sha256-${release.checksum}`;
455
+ }
456
+ p.properties = p.properties || [];
457
+ for (const [key, propName] of [
458
+ ["ruby", "cdx:gem:rubyVersionSpecifiers"],
459
+ ["rubygems", "cdx:gem:rubygemsVersionSpecifiers"],
460
+ ]) {
461
+ // Compact index joins alternative requirements with `&`
462
+ const value = release[key]?.replaceAll("&", ", ");
463
+ if (
464
+ value?.length &&
465
+ value !== ">= 0" &&
466
+ !p.properties.some((prop) => prop.name === propName)
467
+ ) {
468
+ p.properties.push({ name: propName, value });
469
+ }
470
+ }
471
+ }
472
+ // 2. Installed gemspec
473
+ const gemspecFile = gemspecsByFullName[fullName];
474
+ if (gemspecFile) {
475
+ let installedPkgs = [];
476
+ try {
477
+ installedPkgs = await parseGemspecData(
478
+ readFileSync(gemspecFile, { encoding: "utf-8" }),
479
+ gemspecFile,
480
+ );
481
+ } catch (_err) {
482
+ // pass
483
+ }
484
+ const installed = installedPkgs?.[0];
485
+ if (installed) {
486
+ if (!p.description?.length && installed.description?.length) {
487
+ p.description = installed.description;
488
+ }
489
+ if (!p.license && installed.licenses?.length) {
490
+ // parseGemspecData returns the CycloneDX license shape, but the
491
+ // license of an unfinished component is a plain list of names
492
+ const licenseNames = installed.licenses
493
+ .map((alicense) => alicense?.license?.name || alicense?.license?.id)
494
+ .filter((aname) => aname?.length);
495
+ if (licenseNames.length) {
496
+ p.license = licenseNames;
497
+ }
498
+ }
499
+ if (installed.externalReferences?.length) {
500
+ p.externalReferences = p.externalReferences || [];
501
+ for (const aref of installed.externalReferences) {
502
+ if (
503
+ !p.externalReferences.some(
504
+ (existing) =>
505
+ existing.type === aref.type && existing.url === aref.url,
506
+ )
507
+ ) {
508
+ p.externalReferences.push(aref);
509
+ }
510
+ }
511
+ }
512
+ p.properties = p.properties || [];
513
+ for (const prop of installed.properties || []) {
514
+ if (
515
+ prop.name.startsWith("cdx:gem:") &&
516
+ !p.properties.some((existing) => existing.name === prop.name)
517
+ ) {
518
+ p.properties.push(prop);
519
+ }
520
+ }
521
+ }
522
+ }
523
+ }
524
+ return pkgList;
525
+ }
526
+
527
+ /**
528
+ * Apply a RubyGems API metadata payload to a component. Shared by the single
529
+ * version lookup and the bulk versions lookup.
530
+ *
531
+ * @param {object} p Component to enrich in place
532
+ * @param {object} body Metadata payload for one gem version
533
+ */
534
+ function applyGemMetadata(p, body) {
535
+ if (!body) {
536
+ return;
537
+ }
538
+ p.properties = p.properties || [];
539
+ const hasProperty = (name) => p.properties.some((prop) => prop.name === name);
540
+ const addProperty = (name, value) => {
541
+ if (value !== undefined && value !== null && !hasProperty(name)) {
542
+ p.properties.push({ name, value: `${value}` });
543
+ }
544
+ };
545
+ p.description = body.description || body.summary || "";
546
+ if (body.licenses) {
547
+ p.license = body.licenses;
548
+ }
549
+ if (body.metadata) {
550
+ if (body.metadata.source_code_uri) {
551
+ p.repository = { url: body.metadata.source_code_uri };
552
+ if (
553
+ body.homepage_uri &&
554
+ body.homepage_uri !== body.metadata.source_code_uri
555
+ ) {
556
+ p.homepage = { url: body.homepage_uri };
557
+ }
558
+ }
559
+ if (body.metadata.bug_tracker_uri) {
560
+ p.bugs = { url: body.metadata.bug_tracker_uri };
561
+ }
562
+ // Whether the gem requires MFA to publish is a supply chain signal
563
+ if (body.metadata.rubygems_mfa_required) {
564
+ addProperty("cdx:gem:mfaRequired", body.metadata.rubygems_mfa_required);
565
+ }
566
+ }
567
+ // The well known project URIs are reported at the top level by the v1 gems
568
+ // and v2 versions endpoints, and inside `metadata` by the versions listing.
569
+ p.externalReferences = p.externalReferences || [];
570
+ for (const [key, refType] of Object.entries(GEM_METADATA_REFERENCE_TYPES)) {
571
+ const url = body[key] || body.metadata?.[key];
572
+ if (
573
+ url?.length &&
574
+ !p.externalReferences.some(
575
+ (aref) => aref.type === refType && aref.url === url,
576
+ )
577
+ ) {
578
+ p.externalReferences.push({ type: refType, url, comment: key });
579
+ }
580
+ }
581
+ // The .gem tarball is the distribution artifact for the component
582
+ if (body.gem_uri?.length) {
583
+ if (
584
+ !p.externalReferences.some(
585
+ (aref) => aref.type === "distribution" && aref.url === body.gem_uri,
586
+ )
587
+ ) {
588
+ p.externalReferences.push({ type: "distribution", url: body.gem_uri });
589
+ }
590
+ addProperty("cdx:gem:gemUri", body.gem_uri);
591
+ }
592
+ if (!p.externalReferences.length) {
593
+ p.externalReferences = undefined;
594
+ }
595
+ if (body.sha) {
596
+ p._integrity = `sha256-${body.sha}`;
597
+ }
598
+ if (body.authors) {
599
+ p.author = body.authors;
600
+ }
601
+ // `ruby_version` is the `required_ruby_version` requirement. Note that the
602
+ // sibling `rubygems_version` field is the version of RubyGems that packaged
603
+ // the gem, not a requirement, so it is deliberately not recorded as one.
604
+ if (body.ruby_version?.length && body.ruby_version !== ">= 0") {
605
+ addProperty("cdx:gem:rubyVersionSpecifiers", body.ruby_version);
606
+ }
607
+ if (body.yanked) {
608
+ addProperty("cdx:gem:yanked", body.yanked);
609
+ }
610
+ if (body.prerelease) {
611
+ addProperty("cdx:gem:prerelease", body.prerelease);
612
+ }
613
+ // Use the latest version if none specified
614
+ if (!p.version) {
615
+ p.version = body.number;
616
+ }
617
+ }
618
+
619
+ /**
620
+ * The platform of a gem component, taken from the purl qualifier. `ruby` is the
621
+ * implied default that the RubyGems API reports for a pure ruby gem.
622
+ *
623
+ * @param {object} p Component
624
+ * @returns {string} Platform such as `x86_64-linux`, or `ruby`
625
+ */
626
+ function gemComponentPlatform(p) {
627
+ if (!p?.purl) {
628
+ return "ruby";
629
+ }
630
+ try {
631
+ return Purl.parse(p.purl).qualifiers?.platform || "ruby";
632
+ } catch (_err) {
633
+ return "ruby";
634
+ }
635
+ }
636
+
637
+ /**
638
+ * The URL for a single gem's direct lookup, or `undefined` when the component
639
+ * lacks the minimum fields. Shared by the batch prefetch and the per-package
640
+ * fallback so the two cannot construct different URLs.
641
+ *
642
+ * @param {object} p Component with `name`, optional `version`, and purl.
643
+ * @param {string} v2Url Base url of the v2 rubygems endpoint.
644
+ * @param {string} v1Url Base url of the v1 gems endpoint.
645
+ * @returns {string|undefined}
646
+ */
647
+ function gemDirectUrl(p, v2Url, v1Url) {
648
+ if (!p?.name) {
649
+ return undefined;
650
+ }
651
+ const platform = gemComponentPlatform(p);
652
+ const platformQuery =
653
+ platform && platform !== "ruby"
654
+ ? `?platform=${encodeURIComponent(platform)}`
655
+ : "";
656
+ return p.version
657
+ ? `${v2Url}${p.name}/versions/${simplifyRubyVersion(p.version)}.json${platformQuery}`
658
+ : `${v1Url}${p.name}.json`;
659
+ }
660
+
661
+ /**
662
+ * Method to query rubygems api for gems details
663
+ *
664
+ * A gem that ships several native builds appears in the BOM once per platform.
665
+ * Rather than making one request per variant, the versions listing endpoint is
666
+ * used to fetch every version and platform of such a gem in a single request.
667
+ *
668
+ * @param {Array} pkgList List of packages with metadata
669
+ */
670
+ export async function getRubyGemsMetadata(pkgList) {
671
+ const RUBYGEMS_V2_URL =
672
+ readEnvironmentVariable("RUBYGEMS_V2_URL") ||
673
+ "https://rubygems.org/api/v2/rubygems/";
674
+ const RUBYGEMS_V1_URL =
675
+ readEnvironmentVariable("RUBYGEMS_V1_URL") ||
676
+ "https://rubygems.org/api/v1/gems/";
677
+ const RUBYGEMS_V1_VERSIONS_URL =
678
+ readEnvironmentVariable("RUBYGEMS_V1_VERSIONS_URL") ||
679
+ "https://rubygems.org/api/v1/versions/";
680
+ const rdepList = [];
681
+ const apiOptions = {
682
+ responseType: "json",
683
+ };
684
+ if (readEnvironmentVariable("GEM_HOST_API_KEY")) {
685
+ apiOptions.headers = {
686
+ Authorization: readEnvironmentVariable("GEM_HOST_API_KEY"),
687
+ };
688
+ }
689
+ // Group the components by gem name so that the multi platform gems can be
690
+ // resolved with a single request each.
691
+ const pkgsByName = {};
692
+ for (const p of pkgList) {
693
+ if (!pkgsByName[p.name]) {
694
+ pkgsByName[p.name] = [];
695
+ }
696
+ pkgsByName[p.name].push(p);
697
+ }
698
+
699
+ // Collect every URL the loop below is going to need — the per-name versions
700
+ // listing for multi-variant gems, and the per-package direct lookup for
701
+ // everything else — and hand them to one batched round. The derivation in
702
+ // applyGemMetadata is unchanged; only where the body comes from differs.
703
+ const batchRequests = [];
704
+ if (prefetchEnabled()) {
705
+ const seenUrls = new Set();
706
+ const gemAuthRealm = readEnvironmentVariable("GEM_HOST_API_KEY")
707
+ ? "gem-auth"
708
+ : undefined;
709
+ const addBatchUrl = (url) => {
710
+ if (url && !seenUrls.has(url)) {
711
+ seenUrls.add(url);
712
+ batchRequests.push({
713
+ url,
714
+ authRealm: gemAuthRealm,
715
+ headers: apiOptions.headers,
716
+ });
717
+ }
718
+ };
719
+ for (const [name, pkgs] of Object.entries(pkgsByName)) {
720
+ const versionedPkgs = pkgs.filter((p) => p.version);
721
+ if (versionedPkgs.length > 1) {
722
+ addBatchUrl(`${RUBYGEMS_V1_VERSIONS_URL}${name}.json`);
723
+ }
724
+ for (const p of pkgs) {
725
+ addBatchUrl(gemDirectUrl(p, RUBYGEMS_V2_URL, RUBYGEMS_V1_URL));
726
+ }
727
+ }
728
+ }
729
+ const prefetched = await prefetchJson(batchRequests);
730
+
731
+ for (const [name, pkgs] of Object.entries(pkgsByName)) {
732
+ // The versions listing only helps when there are several versioned
733
+ // components to satisfy from the one response.
734
+ const versionedPkgs = pkgs.filter((p) => p.version);
735
+ if (versionedPkgs.length > 1) {
736
+ try {
737
+ const versionsUrl = `${RUBYGEMS_V1_VERSIONS_URL}${name}.json`;
738
+ let versions;
739
+ const prefetchedEntry = prefetched.get(versionsUrl);
740
+ if (prefetchedEntry?.ok) {
741
+ versions = prefetchedEntry.body;
742
+ } else {
743
+ if (DEBUG_MODE) {
744
+ console.log(
745
+ `Querying rubygems.org for all ${versionedPkgs.length} variants of ${name}`,
746
+ );
747
+ }
748
+ const res =
749
+ prefetchedResponse(prefetched, versionsUrl) ||
750
+ (await cdxgenAgent.get(versionsUrl, apiOptions));
751
+ versions = res.body;
752
+ }
753
+ if (Array.isArray(versions) && versions.length) {
754
+ const remaining = [];
755
+ for (const p of pkgs) {
756
+ const platform = gemComponentPlatform(p);
757
+ const match = versions.find(
758
+ (aversion) =>
759
+ aversion.number === p.version &&
760
+ (aversion.platform || "ruby") === platform,
761
+ );
762
+ if (match) {
763
+ applyGemMetadata(p, match);
764
+ rdepList.push(p);
765
+ } else {
766
+ remaining.push(p);
767
+ }
768
+ }
769
+ // Anything the listing did not cover falls through to a direct lookup
770
+ for (const p of remaining) {
771
+ await enrichGemFromVersionEndpoint(
772
+ p,
773
+ RUBYGEMS_V2_URL,
774
+ RUBYGEMS_V1_URL,
775
+ apiOptions,
776
+ prefetched,
777
+ );
778
+ rdepList.push(p);
779
+ }
780
+ continue;
781
+ }
782
+ } catch (err) {
783
+ if (DEBUG_MODE) {
784
+ console.error(name, err);
785
+ }
786
+ }
787
+ }
788
+ for (const p of pkgs) {
789
+ await enrichGemFromVersionEndpoint(
790
+ p,
791
+ RUBYGEMS_V2_URL,
792
+ RUBYGEMS_V1_URL,
793
+ apiOptions,
794
+ prefetched,
795
+ );
796
+ rdepList.push(p);
797
+ }
798
+ }
799
+ return rdepList;
800
+ }
801
+
802
+ /**
803
+ * Look up a single gem version and enrich the component in place.
804
+ *
805
+ * @param {object} p Component to enrich
806
+ * @param {string} v2Url Base url of the v2 rubygems endpoint
807
+ * @param {string} v1Url Base url of the v1 gems endpoint
808
+ * @param {object} apiOptions Request options
809
+ * @param {Map} [prefetched] Results from a prior prefetchJson round.
810
+ */
811
+ async function enrichGemFromVersionEndpoint(
812
+ p,
813
+ v2Url,
814
+ v1Url,
815
+ apiOptions,
816
+ prefetched,
817
+ ) {
818
+ try {
819
+ if (DEBUG_MODE) {
820
+ console.log(`Querying rubygems.org for ${p.name}`);
821
+ }
822
+ const fullUrl = gemDirectUrl(p, v2Url, v1Url);
823
+ if (!fullUrl) {
824
+ return;
825
+ }
826
+ const res =
827
+ prefetchedResponse(prefetched, fullUrl) ||
828
+ (await cdxgenAgent.get(fullUrl, apiOptions));
829
+ let body = res.body;
830
+ if (body?.length) {
831
+ body = body[0];
832
+ }
833
+ applyGemMetadata(p, body);
834
+ } catch (err) {
835
+ if (DEBUG_MODE) {
836
+ console.error(p, err);
837
+ }
838
+ }
839
+ }
840
+
841
+ function _upperFirst(string) {
842
+ return string.slice(0, 1).toUpperCase() + string.slice(1, string.length);
843
+ }
844
+
845
+ /**
846
+ * Utility method to convert a gem package name to a CamelCased module name. Low accuracy.
847
+ *
848
+ * @param name Package name
849
+ */
850
+ export function toGemModuleNames(name) {
851
+ const modList = name.split("-").map((s) => {
852
+ return s
853
+ .split("_")
854
+ .map((str) => {
855
+ return _upperFirst(str.split("/").map(_upperFirst).join("/"));
856
+ })
857
+ .join("");
858
+ });
859
+ const moduleNames = [];
860
+ let prefix = "";
861
+ for (const amod of modList) {
862
+ if (amod !== "Ruby") {
863
+ moduleNames.push(`${prefix}${amod}`);
864
+ }
865
+ prefix = prefix?.length ? `${prefix}${amod}::` : `${amod}::`;
866
+ // ruby-prof is RubyProf
867
+ if (prefix === "Ruby::") {
868
+ prefix = "Ruby";
869
+ }
870
+ }
871
+ return moduleNames;
872
+ }
873
+
874
+ /**
875
+ * Collect all namespaces for a given gem present at the given gemHome
876
+ *
877
+ * @param {String} rubyCommand Ruby command to use if bundle is not available
878
+ * @param {String} bundleCommand Bundle command to use
879
+ * @param {String} gemHome Value to use as GEM_HOME env variable
880
+ * @param {String} gemName Name of the gem
881
+ * @param {String} filePath File path to the directory containing the Gemfile or .bundle directory
882
+ *
883
+ * @returns {Array<string>} List of module names
884
+ */
885
+ export function collectGemModuleNames(
886
+ rubyCommand,
887
+ bundleCommand,
888
+ gemHome,
889
+ gemName,
890
+ filePath,
891
+ ) {
892
+ gemHome =
893
+ gemHome ||
894
+ readEnvironmentVariable("CDXGEN_GEM_HOME") ||
895
+ readEnvironmentVariable("GEM_HOME");
896
+ if (!gemHome) {
897
+ console.log(
898
+ "Set the environment variable CDXGEN_GEM_HOME or GEM_HOME to collect the gem module names.",
899
+ );
900
+ return [];
901
+ }
902
+ if (!gemName || gemName.startsWith("/") || gemName === ".") {
903
+ return [];
904
+ }
905
+ gemName = gemName.replace(/["']/g, "");
906
+ // Module names for some gems cannot be obtained with our one-liner
907
+ // So we keep a hard-coded list of such problematic ones.
908
+ if (RUBY_KNOWN_MODULES[gemName]) {
909
+ return RUBY_KNOWN_MODULES[gemName];
910
+ }
911
+ const moduleNames = new Set();
912
+ const commandToUse = bundleCommand || rubyCommand;
913
+ let args = bundleCommand ? ["exec", "ruby"] : [];
914
+ args = args.concat([
915
+ "-e",
916
+ `initial = ObjectSpace.each_object(Module).map { |m| m.respond_to?(:name) ? m.name : nil }.compact;
917
+ require '${gemName}';
918
+ begin
919
+ afterwards = ObjectSpace.each_object(Module).map { |m| m.respond_to?(:name) ? m.name : nil }.compact;
920
+ added = afterwards - initial;
921
+ puts added.sort
922
+ rescue NoMethodError => e
923
+ puts ""
924
+ end
925
+ `,
926
+ ]);
927
+ const result = safeSpawnSync(commandToUse, args, {
928
+ shell: isWin,
929
+ timeout: 5000,
930
+ cwd: filePath,
931
+ env: {
932
+ ...process.env,
933
+ GEM_HOME: gemHome,
934
+ },
935
+ });
936
+ if (result.error || result.status !== 0) {
937
+ if (result?.stderr?.includes("Could not locate Gemfile or .bundle")) {
938
+ console.log(
939
+ `${filePath} must be a directory containing the Gemfile. This appears like a bug in cdxgen.`,
940
+ );
941
+ return [];
942
+ }
943
+ // Let's retry for simple mismatches
944
+ if (gemName?.includes("-")) {
945
+ return collectGemModuleNames(
946
+ rubyCommand,
947
+ bundleCommand,
948
+ gemHome,
949
+ gemName.replaceAll("-", "/"),
950
+ filePath,
951
+ );
952
+ }
953
+ // bundle can sometimes offer suggestions for simple mismatches. Let's try that.
954
+ if (result?.stderr?.includes("Did you mean?")) {
955
+ const altGemName = result.stderr
956
+ .split("Did you mean? ")[1]
957
+ .split("\n")[0]
958
+ .trim();
959
+ if (
960
+ altGemName?.length &&
961
+ !altGemName.startsWith("/") &&
962
+ altGemName !== "." &&
963
+ gemName.replace(/[-_/]/g, "").toLowerCase() ===
964
+ altGemName.replace(/[-_/]/g, "").toLowerCase()
965
+ ) {
966
+ if (DEBUG_MODE) {
967
+ console.log("Retrying", gemName, "with", altGemName);
968
+ }
969
+ return collectGemModuleNames(
970
+ rubyCommand,
971
+ bundleCommand,
972
+ gemHome,
973
+ altGemName,
974
+ filePath,
975
+ );
976
+ }
977
+ if (DEBUG_MODE) {
978
+ console.log(
979
+ `Is ${altGemName} an alternative gem name for '${gemName}' package? Please let us know if this is correct.`,
980
+ );
981
+ }
982
+ }
983
+ // Gem wasn't installed or the GEM_HOME was not set correctly.
984
+ if (
985
+ result?.stderr?.includes("Bundler::GemNotFound") ||
986
+ result?.stderr?.includes("(LoadError)")
987
+ ) {
988
+ return [];
989
+ }
990
+ if (
991
+ !result?.stderr?.includes("(NameError)") &&
992
+ !result?.stderr?.includes("(NoMethodError)") &&
993
+ !result?.stderr?.includes("(ArgumentError)") &&
994
+ DEBUG_MODE
995
+ ) {
996
+ console.log(
997
+ `Unable to collect the module names exported by the gem ${gemName}.`,
998
+ );
999
+ console.log(result.stderr);
1000
+ }
1001
+ // Let's guess the module name based on common naming convention.
1002
+ return toGemModuleNames(gemName);
1003
+ }
1004
+ const simpleModuleNames = new Set();
1005
+ for (const aline of result.stdout.split("\n")) {
1006
+ if (
1007
+ !aline?.length ||
1008
+ aline.startsWith("Ignoring ") ||
1009
+ aline.includes("cannot load such file") ||
1010
+ aline.startsWith("#<")
1011
+ ) {
1012
+ continue;
1013
+ }
1014
+ if (!aline.includes("::")) {
1015
+ simpleModuleNames.add(aline.trim());
1016
+ continue;
1017
+ }
1018
+ moduleNames.add(aline.trim());
1019
+ }
1020
+ return moduleNames.size
1021
+ ? Array.from(moduleNames).sort()
1022
+ : Array.from(simpleModuleNames).sort();
1023
+ }
1024
+
1025
+ /**
1026
+ * Method to parse Gemspec file contents
1027
+ *
1028
+ * @param {string} gemspecData Gemspec data
1029
+ * @param {string} gemspecFile File name for evidence.
1030
+ */
1031
+ export async function parseGemspecData(gemspecData, gemspecFile) {
1032
+ let pkgList = [];
1033
+ const pkg = { properties: [] };
1034
+ if (gemspecFile) {
1035
+ pkg.name = basename(gemspecFile).replace(".gemspec", "");
1036
+ }
1037
+ if (!gemspecData) {
1038
+ return pkgList;
1039
+ }
1040
+ let versionHackMatch = false;
1041
+ let gemPlatform;
1042
+ const gemMetadata = {};
1043
+ let metadataBlock = false;
1044
+ gemspecData.split("\n").forEach((l) => {
1045
+ versionHackMatch = false;
1046
+ l = l.replaceAll("\r", "");
1047
+ l = l.replace(/\s+/g, " ").replaceAll("%q{", "").trim().replace(/}$/, "");
1048
+ if (l.startsWith("#")) {
1049
+ return;
1050
+ }
1051
+ // Native gems declare a platform. Installed gemspecs use either a plain
1052
+ // string (`s.platform = "java".freeze`) or the marshalled array form
1053
+ // (`s.platform = Gem::Platform.new(["x86_64", "linux", nil])`).
1054
+ if (l.includes(".platform = ")) {
1055
+ const platformValue = l
1056
+ .split(".platform = ")[1]
1057
+ .replaceAll(".freeze", "");
1058
+ if (platformValue.includes("Gem::Platform.new")) {
1059
+ const platformParts = platformValue
1060
+ .replace(/.*\[/, "")
1061
+ .replace(/].*/, "")
1062
+ .split(",")
1063
+ .map((s) => s.trim().replace(/["']/g, ""))
1064
+ .filter((s) => s.length && s !== "nil");
1065
+ if (platformParts.length) {
1066
+ gemPlatform = platformParts.join("-");
1067
+ }
1068
+ } else if (!platformValue.includes("Gem::Platform::RUBY")) {
1069
+ const platformString = platformValue.replace(/["';]/g, "").trim();
1070
+ if (platformString.length && isRubyPlatform(platformString)) {
1071
+ gemPlatform = platformString;
1072
+ }
1073
+ }
1074
+ }
1075
+ for (const aprop of ["name", "version", "description", "homepage"]) {
1076
+ if (l.includes(`.${aprop} = `)) {
1077
+ let value = l
1078
+ .split(`.${aprop} = `)[1]
1079
+ .replace(".freeze", "")
1080
+ .replaceAll("''', ", "")
1081
+ .replace(/"/g, "");
1082
+ if (["name", "version"].includes(aprop)) {
1083
+ value = value.replace(/["']/g, "");
1084
+ }
1085
+ // Do not set name=name or version=version
1086
+ if (value !== aprop) {
1087
+ pkg[aprop] = value;
1088
+ break;
1089
+ }
1090
+ }
1091
+ }
1092
+ // Handle common problems
1093
+ if (pkg.name === "name") {
1094
+ console.log(
1095
+ "Unable to identify the package name by parsing the file",
1096
+ gemspecFile,
1097
+ );
1098
+ return;
1099
+ }
1100
+ if (
1101
+ pkg?.version === "version" ||
1102
+ pkg?.version?.includes("$") ||
1103
+ pkg?.version?.includes("gem_version") ||
1104
+ pkg?.version?.includes("File.") ||
1105
+ pkg?.version?.includes("::")
1106
+ ) {
1107
+ const origVersion = pkg.version;
1108
+ pkg.version = undefined;
1109
+ // Can we find the version from the directory name?
1110
+ if (gemspecFile) {
1111
+ const parentDir = basename(dirname(gemspecFile));
1112
+ const prefix = `${pkg.name}-`;
1113
+ if (parentDir.startsWith(prefix)) {
1114
+ const versionFromDir = parentDir.slice(prefix.length).split("-")[0];
1115
+ if (/\d/.test(versionFromDir)) {
1116
+ pkg.version = versionFromDir;
1117
+ versionHackMatch = true;
1118
+ }
1119
+ }
1120
+ }
1121
+ if (!versionHackMatch && !pkg.version) {
1122
+ if (origVersion?.toLowerCase().includes("version")) {
1123
+ if (DEBUG_MODE) {
1124
+ console.log(
1125
+ `Unable to identify the version for '${pkg.name}' from the string '${origVersion}'. Spec file: ${gemspecFile}`,
1126
+ );
1127
+ }
1128
+ } else {
1129
+ console.log(
1130
+ `Unable to identify the version for '${pkg.name}'. Spec file: ${gemspecFile}`,
1131
+ );
1132
+ }
1133
+ }
1134
+ }
1135
+ for (const aprop of ["authors", "licenses"]) {
1136
+ if (l.includes(`.${aprop} = `)) {
1137
+ try {
1138
+ const pline = l
1139
+ .split(`.${aprop} = `)
1140
+ .pop()
1141
+ .replaceAll(".freeze", "")
1142
+ .replaceAll("%w", "")
1143
+ .replaceAll("'", '"')
1144
+ .replaceAll(']"', "");
1145
+ const apropList = JSON.parse(pline);
1146
+ if (apropList) {
1147
+ if (Array.isArray(apropList)) {
1148
+ pkg[aprop] = apropList;
1149
+ } else if (
1150
+ typeof apropList === "string" ||
1151
+ apropList instanceof String
1152
+ ) {
1153
+ pkg[aprop] = apropList.split(",");
1154
+ }
1155
+ }
1156
+ } catch (_err) {
1157
+ const alist = l
1158
+ .replace(/[[\]'"]/g, "")
1159
+ .replaceAll("%w", "")
1160
+ .split(", ");
1161
+ if (alist?.length) {
1162
+ pkg[aprop] = alist;
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1167
+ if (l.includes(".executables = ")) {
1168
+ try {
1169
+ const exeList = JSON.parse(
1170
+ l
1171
+ .split(".executables = ")
1172
+ .pop()
1173
+ .replaceAll(".freeze", "")
1174
+ .replaceAll("'", '"')
1175
+ .replaceAll(']"', ""),
1176
+ );
1177
+ if (exeList && Array.isArray(exeList)) {
1178
+ pkg.properties.push({
1179
+ name: "cdx:gem:executables",
1180
+ value: exeList.join(", "),
1181
+ });
1182
+ }
1183
+ } catch (_err) {
1184
+ // pass
1185
+ }
1186
+ }
1187
+ // A gem with extensions compiles native code at install time, which is a
1188
+ // meaningful execution surface for a supply chain review.
1189
+ if (l.includes(".extensions = ")) {
1190
+ const extList = parseGemspecArray(l.split(".extensions = ").pop());
1191
+ if (extList.length) {
1192
+ pkg.properties.push({
1193
+ name: "cdx:gem:extensions",
1194
+ value: extList.join(", "),
1195
+ });
1196
+ }
1197
+ }
1198
+ // `required_ruby_version` and `required_rubygems_version` are
1199
+ // Gem::Requirement strings such as `>= 3.0` or `>= 2.2, < 4.0`
1200
+ for (const [aprop, propName] of [
1201
+ ["required_ruby_version", "cdx:gem:rubyVersionSpecifiers"],
1202
+ ["required_rubygems_version", "cdx:gem:rubygemsVersionSpecifiers"],
1203
+ ]) {
1204
+ if (l.includes(`.${aprop} = `)) {
1205
+ const requirement = parseGemRequirement(l.split(`.${aprop} = `).pop());
1206
+ // `>= 0` carries no information
1207
+ if (requirement && requirement !== ">= 0") {
1208
+ pkg.properties.push({ name: propName, value: requirement });
1209
+ }
1210
+ }
1211
+ }
1212
+ // The free form metadata hash carries the well known project URIs and the
1213
+ // `rubygems_mfa_required` supply chain signal. It may be written inline as
1214
+ // `s.metadata = { "a" => "b", "c" => "d" }`, spread over several lines, or
1215
+ // assigned a key at a time with `s.metadata["a"] = "b"`.
1216
+ if (l.includes(".metadata")) {
1217
+ metadataBlock = true;
1218
+ }
1219
+ if (metadataBlock) {
1220
+ for (const [, key, value] of l.matchAll(
1221
+ /["']([a-z_]+)["']\s*(?:=>|\]\s*=)\s*["']([^"']*)["']/g,
1222
+ )) {
1223
+ gemMetadata[key] = value;
1224
+ }
1225
+ if (l.includes("}") || (!l.includes(".metadata") && !l.includes("=>"))) {
1226
+ metadataBlock = false;
1227
+ }
1228
+ }
1229
+ });
1230
+ for (const [key, value] of Object.entries(gemMetadata)) {
1231
+ if (key === "rubygems_mfa_required") {
1232
+ pkg.properties.push({
1233
+ name: "cdx:gem:mfaRequired",
1234
+ value: `${value}`,
1235
+ });
1236
+ continue;
1237
+ }
1238
+ const refType = GEM_METADATA_REFERENCE_TYPES[key];
1239
+ if (refType) {
1240
+ pkg.externalReferences = pkg.externalReferences || [];
1241
+ pkg.externalReferences.push({ type: refType, url: value, comment: key });
1242
+ }
1243
+ }
1244
+ if (pkg.name) {
1245
+ const purlString = toGemPurl(pkg.name, pkg.version, gemPlatform);
1246
+ pkg.purl = purlString;
1247
+ pkg["bom-ref"] = decodeURIComponent(purlString);
1248
+ }
1249
+ if (gemspecFile) {
1250
+ pkg.properties.push({ name: "internal:SrcFile", value: gemspecFile });
1251
+ // Did we find the version number from the directory name? Let's reduce the confidence and set the correct technique
1252
+ pkg.evidence = {
1253
+ identity: {
1254
+ field: "purl",
1255
+ confidence: !pkg.version || versionHackMatch ? 0.2 : 0.5,
1256
+ methods: [
1257
+ {
1258
+ technique: versionHackMatch ? "filename" : "manifest-analysis",
1259
+ confidence: !pkg.version || versionHackMatch ? 0.2 : 0.5,
1260
+ value: gemspecFile,
1261
+ },
1262
+ ],
1263
+ },
1264
+ };
1265
+ }
1266
+ if (pkg.authors) {
1267
+ pkg.authors = pkg.authors.map((a) => {
1268
+ return { name: a };
1269
+ });
1270
+ }
1271
+ if (pkg.licenses) {
1272
+ pkg.licenses = pkg.licenses.map((l) => {
1273
+ return { license: { name: l } };
1274
+ });
1275
+ }
1276
+ if (pkg.name) {
1277
+ pkgList = [pkg];
1278
+ } else {
1279
+ console.log("Unable to parse", gemspecData, gemspecFile);
1280
+ }
1281
+ if (shouldFetchLicense()) {
1282
+ return await getRubyGemsMetadata(pkgList);
1283
+ }
1284
+ return pkgList;
1285
+ }
1286
+
1287
+ /**
1288
+ * Method to parse Gemfile.lock
1289
+ *
1290
+ * @param {object} gemLockData Gemfile.lock data
1291
+ * @param {string} lockFile Lock file
1292
+ */
1293
+ export async function parseGemfileLockData(gemLockData, lockFile) {
1294
+ let pkgList = [];
1295
+ const pkgnames = {};
1296
+ const dependenciesList = [];
1297
+ const dependenciesMap = {};
1298
+ const pkgVersionMap = {};
1299
+ const pkgVersionPlatformMap = {};
1300
+ const pkgNameRef = {};
1301
+ if (!gemLockData) {
1302
+ return pkgList;
1303
+ }
1304
+ const checksumsMap = {};
1305
+ let specsFound = false;
1306
+ let checksumsFound = false;
1307
+ // We need two passes to identify components and resolve dependencies
1308
+ // In the first pass, we capture package name and version
1309
+ gemLockData.split("\n").forEach((l) => {
1310
+ l = l.trim();
1311
+ l = l.replaceAll("\r", "");
1312
+ if (checksumsFound) {
1313
+ const gemHashes = parseGemChecksumLine(l);
1314
+ if (gemHashes) {
1315
+ checksumsMap[gemHashes.lockName] = gemHashes.hashes;
1316
+ }
1317
+ }
1318
+ if (specsFound) {
1319
+ const tmpA = l.split(" ");
1320
+ if (tmpA && tmpA.length === 2) {
1321
+ const name = tmpA[0];
1322
+ if (name === "remote:") {
1323
+ return;
1324
+ }
1325
+ let version = tmpA[1];
1326
+ // We only allow bracket characters ()
1327
+ if (version.search(/[,><~ ]/) < 0) {
1328
+ version = version.replace(/[=()]/g, "");
1329
+ // Sometimes, the version number could include the platform
1330
+ // Examples:
1331
+ // bcrypt_pbkdf (1.1.0)
1332
+ // bcrypt_pbkdf (1.1.0-x64-mingw32)
1333
+ // bcrypt_pbkdf (1.1.0-x86-mingw32)
1334
+ // In such cases, we need to track all of them to improve precision
1335
+ const { platform } = splitRubyVersionPlatform(version);
1336
+ if (platform) {
1337
+ pkgVersionMap[`${name}-${platform}`] = version;
1338
+ if (!pkgVersionPlatformMap[name]) {
1339
+ pkgVersionPlatformMap[name] = new Set();
1340
+ }
1341
+ pkgVersionPlatformMap[name].add(version);
1342
+ } else {
1343
+ pkgVersionMap[name] = version;
1344
+ }
1345
+ }
1346
+ }
1347
+ }
1348
+ if (l === "specs:") {
1349
+ specsFound = true;
1350
+ }
1351
+ if (l === l.toUpperCase()) {
1352
+ specsFound = false;
1353
+ checksumsFound = l === "CHECKSUMS";
1354
+ }
1355
+ });
1356
+ specsFound = false;
1357
+ let lastParent;
1358
+ let lastRemote;
1359
+ let lastRevision;
1360
+ let lastBranch;
1361
+ let lastTag;
1362
+ let lastParentPlatform;
1363
+ // Dependencies block would begin with DEPENDENCIES
1364
+ let dependenciesBlock = false;
1365
+ const rootList = [];
1366
+ // In the second pass, we use the space in the prefix to figure out the dependency tree
1367
+ gemLockData.split("\n").forEach((l) => {
1368
+ l = l.replaceAll("\r", "");
1369
+ if (l.trim().startsWith("remote:")) {
1370
+ lastRemote = l.trim().split(" ")[1];
1371
+ if (lastRemote.length < 3) {
1372
+ lastRemote = undefined;
1373
+ }
1374
+ }
1375
+ if (l.trim().startsWith("revision:")) {
1376
+ lastRevision = l.trim().split(" ")[1];
1377
+ }
1378
+ if (l.trim().startsWith("branch:")) {
1379
+ lastBranch = l.trim().split(" ")[1];
1380
+ }
1381
+ if (l.trim().startsWith("tag:")) {
1382
+ lastTag = l.trim().split(" ")[1];
1383
+ }
1384
+ if (l.trim() === l.trim().toUpperCase()) {
1385
+ if (l.trim() === "DEPENDENCIES") {
1386
+ dependenciesBlock = true;
1387
+ return;
1388
+ }
1389
+ dependenciesBlock = false;
1390
+ specsFound = false;
1391
+ lastRemote = undefined;
1392
+ lastRevision = undefined;
1393
+ lastBranch = undefined;
1394
+ lastTag = undefined;
1395
+ lastParentPlatform = undefined;
1396
+ }
1397
+ if (l.trim() === "specs:") {
1398
+ specsFound = true;
1399
+ return;
1400
+ }
1401
+ if (specsFound) {
1402
+ const tmpA = l.split(" (");
1403
+ const nameWithPrefix = tmpA[0];
1404
+ const name = tmpA[0].replace(/["']/g, "").trim();
1405
+ const level = nameWithPrefix.replace(name, "").split(" ").length % 2;
1406
+ if (
1407
+ !name.length ||
1408
+ ["remote:", "bundler", name.toUpperCase()].includes(name)
1409
+ ) {
1410
+ return;
1411
+ }
1412
+ let mayBeVersion = l
1413
+ .trim()
1414
+ .replace(name, "")
1415
+ .replace(" (", "")
1416
+ .replace(")", "");
1417
+ if (mayBeVersion.search(/[,><~ ]/) < 0) {
1418
+ // Reset the platform
1419
+ if (level === 1) {
1420
+ lastParentPlatform = undefined;
1421
+ }
1422
+ // Extract the platform. Child gems without an explicit version inherit
1423
+ // the platform of their platform-specific parent, so we must only
1424
+ // overwrite the tracked platform when this line declares one.
1425
+ const { platform } = splitRubyVersionPlatform(mayBeVersion);
1426
+ if (platform) {
1427
+ lastParentPlatform = platform;
1428
+ }
1429
+ } else {
1430
+ mayBeVersion = undefined;
1431
+ }
1432
+ // Resolve this line to one or more concrete gem releases.
1433
+ //
1434
+ // A line that declares its own version is a spec and resolves to exactly
1435
+ // one release. A dependency reference has to be looked up, and when the
1436
+ // gem ships only as native builds we cannot tell which one Bundler would
1437
+ // install, so every variant becomes a candidate. Emitting the variants
1438
+ // that the lockfile really contains is more accurate than inventing a
1439
+ // phantom versionless component, which is what used to happen.
1440
+ const variants = pkgVersionPlatformMap[name]
1441
+ ? Array.from(pkgVersionPlatformMap[name])
1442
+ : [];
1443
+ let resolvedVersions = [];
1444
+ if (mayBeVersion) {
1445
+ resolvedVersions = [mayBeVersion];
1446
+ } else if (pkgVersionMap[name]) {
1447
+ // Identifying the resolved version for a given dependency requires multiple lookups
1448
+ resolvedVersions = [pkgVersionMap[name]];
1449
+ } else if (lastParentPlatform) {
1450
+ // Is there a platform specific alias?
1451
+ const alias = pkgVersionMap[`${name}-${lastParentPlatform}`];
1452
+ if (alias) {
1453
+ resolvedVersions = [alias];
1454
+ } else {
1455
+ // Is there a match based on the last parent platform?
1456
+ const fuzzyMatch = variants.find((aver) =>
1457
+ aver.includes(lastParentPlatform.replace("-gnu", "")),
1458
+ );
1459
+ if (fuzzyMatch) {
1460
+ resolvedVersions = [fuzzyMatch];
1461
+ }
1462
+ }
1463
+ }
1464
+ if (!resolvedVersions.length && variants.length) {
1465
+ resolvedVersions = variants;
1466
+ }
1467
+ if (!resolvedVersions.length) {
1468
+ resolvedVersions = [undefined];
1469
+ }
1470
+ for (const resolvedVersion of resolvedVersions) {
1471
+ // RubyGems models the version and the native platform separately, so we
1472
+ // must not leave the platform suffix in the version. The purl
1473
+ // specification represents it with the `platform` qualifier.
1474
+ const { version, platform } = splitRubyVersionPlatform(resolvedVersion);
1475
+ const purlString = toGemPurl(name, version, platform);
1476
+ const bomRef = decodeURIComponent(purlString);
1477
+ if (level === 1) {
1478
+ lastParent = bomRef;
1479
+ }
1480
+ const properties = [
1481
+ {
1482
+ name: "internal:SrcFile",
1483
+ value: lockFile,
1484
+ },
1485
+ ];
1486
+ if (lastRemote) {
1487
+ properties.push({
1488
+ name: "cdx:gem:remote",
1489
+ value: lastRemote,
1490
+ });
1491
+ }
1492
+ if (lastRevision) {
1493
+ properties.push({
1494
+ name: "cdx:gem:remoteRevision",
1495
+ value: lastRevision,
1496
+ });
1497
+ }
1498
+ if (lastBranch) {
1499
+ properties.push({
1500
+ name: "cdx:gem:remoteBranch",
1501
+ value: lastBranch,
1502
+ });
1503
+ }
1504
+ if (lastTag) {
1505
+ properties.push({
1506
+ name: "cdx:gem:remoteTag",
1507
+ value: lastTag,
1508
+ });
1509
+ }
1510
+ const apkg = {
1511
+ name,
1512
+ version,
1513
+ purl: purlString,
1514
+ "bom-ref": bomRef,
1515
+ properties,
1516
+ evidence: {
1517
+ identity: {
1518
+ field: "purl",
1519
+ confidence: 0.8,
1520
+ methods: [
1521
+ {
1522
+ technique: "manifest-analysis",
1523
+ confidence: 0.8,
1524
+ value: lockFile,
1525
+ },
1526
+ ],
1527
+ },
1528
+ },
1529
+ };
1530
+ // Bundler 2.5 onwards records the sha256 of each gem in a CHECKSUMS
1531
+ // section, keyed by the same `name (version[-platform])` token.
1532
+ const gemHashes = checksumsMap[`${name} (${resolvedVersion})`];
1533
+ if (gemHashes) {
1534
+ apkg.hashes = gemHashes;
1535
+ }
1536
+ if (lastParent && lastParent !== bomRef) {
1537
+ if (!dependenciesMap[lastParent]) {
1538
+ dependenciesMap[lastParent] = new Set();
1539
+ }
1540
+ dependenciesMap[lastParent].add(bomRef);
1541
+ }
1542
+ if (!dependenciesMap[bomRef]) {
1543
+ dependenciesMap[bomRef] = new Set();
1544
+ }
1545
+ // A gem with several native variants has several bom-refs. Track all of
1546
+ // them so that a direct dependency on such a gem does not resolve to an
1547
+ // arbitrary variant.
1548
+ if (!pkgNameRef[name]) {
1549
+ pkgNameRef[name] = [];
1550
+ }
1551
+ if (!pkgNameRef[name].includes(bomRef)) {
1552
+ pkgNameRef[name].push(bomRef);
1553
+ }
1554
+ // Allow duplicate packages if the version number includes platform
1555
+ if (!pkgnames[purlString]) {
1556
+ pkgList.push(apkg);
1557
+ pkgnames[purlString] = true;
1558
+ }
1559
+ }
1560
+ } else if (dependenciesBlock) {
1561
+ const rootDepName = l.trim().split(" ")[0].replace("!", "");
1562
+ if (pkgNameRef[rootDepName]?.length) {
1563
+ // Every native variant of a direct dependency is a direct dependency
1564
+ for (const aref of pkgNameRef[rootDepName]) {
1565
+ if (!rootList.includes(aref)) {
1566
+ rootList.push(aref);
1567
+ }
1568
+ }
1569
+ } else {
1570
+ // We are dealing with an optional platform-dependent import
1571
+ // create a placeholder component to track this
1572
+ let specifier;
1573
+ if (l.includes("(")) {
1574
+ specifier = l.trim().split(" (").pop().replace(")", "").trim();
1575
+ }
1576
+ const untrackedPurl = toGemPurl(rootDepName, null, null);
1577
+ const untrackedBomRef = decodeURIComponent(untrackedPurl);
1578
+ const untrackedProps = [
1579
+ {
1580
+ name: "internal:SrcFile",
1581
+ value: lockFile,
1582
+ },
1583
+ ];
1584
+ if (specifier) {
1585
+ untrackedProps.push({
1586
+ name: "cdx:gem:versionSpecifiers",
1587
+ value: specifier,
1588
+ });
1589
+ }
1590
+ const untrackedRootDep = {
1591
+ name: rootDepName,
1592
+ version: undefined,
1593
+ purl: untrackedPurl,
1594
+ "bom-ref": untrackedBomRef,
1595
+ properties: untrackedProps,
1596
+ evidence: {
1597
+ identity: {
1598
+ field: "purl",
1599
+ confidence: 0.3,
1600
+ methods: [
1601
+ {
1602
+ technique: "manifest-analysis",
1603
+ confidence: 0.3,
1604
+ value: lockFile,
1605
+ },
1606
+ ],
1607
+ },
1608
+ },
1609
+ };
1610
+ pkgnames[untrackedPurl] = true;
1611
+ pkgNameRef[rootDepName] = [untrackedBomRef];
1612
+ pkgList.push(untrackedRootDep);
1613
+ rootList.push(untrackedBomRef);
1614
+ dependenciesMap[untrackedBomRef] = new Set();
1615
+ }
1616
+ }
1617
+ });
1618
+ for (const k of Object.keys(dependenciesMap)) {
1619
+ dependenciesList.push({
1620
+ ref: k,
1621
+ dependsOn: Array.from(dependenciesMap[k]).sort(),
1622
+ });
1623
+ }
1624
+ if (shouldFetchLicense()) {
1625
+ pkgList = await getRubyGemsMetadata(pkgList);
1626
+ return { pkgList, dependenciesList, rootList };
1627
+ }
1628
+ return { pkgList, dependenciesList, rootList };
1629
+ }