@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,3191 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import {
4
+ sep as _sep,
5
+ basename,
6
+ dirname,
7
+ join,
8
+ relative,
9
+ resolve,
10
+ } from "node:path";
11
+ import process from "node:process";
12
+
13
+ import { build, Purl } from "@cdxgen/cdx-purl";
14
+ import { parse as _load } from "yaml";
15
+
16
+ import {
17
+ cdxgenAgent,
18
+ DEBUG_MODE,
19
+ readEnvironmentVariable,
20
+ } from "../core/activity.js";
21
+ import { shouldFetchLicense } from "../core/env.js";
22
+ import {
23
+ safeExistsSync,
24
+ safeSpawnSync,
25
+ safeWriteSync,
26
+ temporaryFiles,
27
+ } from "../core/fs.js";
28
+ import { thoughtLog } from "../core/logger.js";
29
+ import { isWin } from "../core/paths.js";
30
+ import { mesonWrapDB } from "../core/state.js";
31
+ import { isGitHubUrl } from "../core/urls.js";
32
+ import { parseWorkflowFile } from "../inventory/ciParsers/githubActions.js";
33
+ import {
34
+ prefetchEnabled,
35
+ prefetchedResponse,
36
+ prefetchJson,
37
+ } from "../inventory/fetchBatch.js";
38
+ import {
39
+ applyPurl,
40
+ mapConanPkgRefToPurlStringAndNameAndVersion,
41
+ nixGenericPurl,
42
+ purlFromUrlString,
43
+ } from "../inventory/purl.js";
44
+ import { getLicenses, spdxLicenses } from "../inventory/spdx.js";
45
+ import { getDartMetadata } from "./ecosystems.js";
46
+ import { parsePkgJson } from "./parsers-js.js";
47
+
48
+ /**
49
+ * Method to parse pubspec.lock files.
50
+ *
51
+ * @param pubLockData Contents of lock data
52
+ * @param lockFile Filename for setting evidence
53
+ *
54
+ * @returns {Object}
55
+ */
56
+ export async function parsePubLockData(pubLockData, lockFile) {
57
+ if (!pubLockData) {
58
+ return [];
59
+ }
60
+ let pkgList = [];
61
+ const rootList = [];
62
+ const data = _load(pubLockData);
63
+ const packages = data.packages;
64
+ for (const [packageName, packageData] of Object.entries(packages)) {
65
+ const pkg = {
66
+ name: packageName,
67
+ version: packageData.version,
68
+ properties: [],
69
+ };
70
+ // older dart versions don't have sha256
71
+ if (packageData.description?.sha256) {
72
+ pkg._integrity = `sha256-${packageData.description?.sha256}`;
73
+ }
74
+ if (
75
+ packageData.description?.url &&
76
+ packageData.description?.url !== "https://pub.dev"
77
+ ) {
78
+ pkg.properties.push({
79
+ name: "cdx:pub:registry",
80
+ value: packageData.description.url,
81
+ });
82
+ }
83
+ const purlString = new Purl({
84
+ type: "pub",
85
+ namespace: "" || null,
86
+ name: pkg.name,
87
+ version: pkg.version || null,
88
+ })
89
+ .toString()
90
+ .replace(/%2F/g, "/");
91
+ pkg["bom-ref"] = decodeURIComponent(purlString);
92
+ if (packageData.dependency === "direct main") {
93
+ pkg.scope = "required";
94
+ rootList.push(pkg);
95
+ } else if (packageData.dependency === "transitive") {
96
+ pkg.scope = "required";
97
+ } else if (packageData.dependency === "direct dev") {
98
+ pkg.scope = "optional";
99
+ }
100
+ if (lockFile) {
101
+ pkg.properties.push({
102
+ name: "internal:SrcFile",
103
+ value: lockFile,
104
+ });
105
+ pkg.evidence = {
106
+ identity: {
107
+ field: "purl",
108
+ confidence: 1,
109
+ methods: [
110
+ {
111
+ technique: "manifest-analysis",
112
+ confidence: 1,
113
+ value: lockFile,
114
+ },
115
+ ],
116
+ },
117
+ };
118
+ }
119
+ pkgList.push(pkg);
120
+ }
121
+ if (shouldFetchLicense()) {
122
+ pkgList = await getDartMetadata(pkgList);
123
+ }
124
+ return { rootList, pkgList };
125
+ }
126
+
127
+ /**
128
+ * Parses a Dart pub package's pubspec.yaml content and returns a list containing
129
+ * a single component object with name, description, version, homepage, and purl.
130
+ *
131
+ * @param {string} pubYamlData Raw YAML string contents of a pubspec.yaml file
132
+ * @returns {Object[]} List containing a single Dart package component object
133
+ */
134
+ export function parsePubYamlData(pubYamlData) {
135
+ const pkgList = [];
136
+ let yamlObj;
137
+ try {
138
+ yamlObj = _load(pubYamlData);
139
+ } catch (_err) {
140
+ // continue regardless of error
141
+ }
142
+ if (!yamlObj) {
143
+ return pkgList;
144
+ }
145
+ const pkg = {
146
+ name: yamlObj.name,
147
+ description: yamlObj.description,
148
+ version: yamlObj.version,
149
+ homepage: { url: yamlObj.homepage },
150
+ };
151
+ const purlString = new Purl({
152
+ type: "pub",
153
+ namespace: "" || null,
154
+ name: pkg.name,
155
+ version: pkg.version || null,
156
+ })
157
+ .toString()
158
+ .replace(/%2F/g, "/");
159
+ pkg.purl = purlString;
160
+ pkg["bom-ref"] = decodeURIComponent(purlString);
161
+ pkgList.push(pkg);
162
+ return pkgList;
163
+ }
164
+
165
+ /**
166
+ * Parses Helm chart YAML data (Chart.yaml or repository index.yaml) and returns
167
+ * a list of Helm chart component objects including the chart itself and any
168
+ * declared dependencies or index entries.
169
+ *
170
+ * @param {string} helmData Raw YAML string contents of a Helm Chart.yaml or index.yaml file
171
+ * @returns {Object[]} List of Helm chart component objects with name, version, and optional homepage/repository
172
+ */
173
+ export function parseHelmYamlData(helmData) {
174
+ const pkgList = [];
175
+ let yamlObj;
176
+ try {
177
+ yamlObj = _load(helmData);
178
+ } catch (_err) {
179
+ // continue regardless of error
180
+ }
181
+ if (!yamlObj) {
182
+ return pkgList;
183
+ }
184
+ if (yamlObj.name && yamlObj.version) {
185
+ const pkg = {
186
+ name: yamlObj.name,
187
+ description: yamlObj.description || "",
188
+ version: yamlObj.version,
189
+ };
190
+ if (yamlObj.home) {
191
+ pkg["homepage"] = { url: yamlObj.home };
192
+ }
193
+ pkgList.push(pkg);
194
+ }
195
+ if (yamlObj.dependencies) {
196
+ for (const hd of yamlObj.dependencies) {
197
+ const pkg = {
198
+ name: hd.name,
199
+ version: hd.version, // This could have * so not precise
200
+ };
201
+ if (hd.repository) {
202
+ pkg["repository"] = { url: hd.repository };
203
+ }
204
+ pkgList.push(pkg);
205
+ }
206
+ }
207
+ if (yamlObj.entries) {
208
+ for (const he of Object.keys(yamlObj.entries)) {
209
+ for (const key of Object.keys(yamlObj.entries[he])) {
210
+ const hd = yamlObj.entries[he][key];
211
+ if (hd.name && hd.version) {
212
+ const pkg = {
213
+ name: hd.name,
214
+ version: hd.version,
215
+ description: hd.description || "",
216
+ };
217
+ if (hd.sources && Array.isArray(hd.sources) && hd.sources.length) {
218
+ pkg["repository"] = { url: hd.sources[0] };
219
+ if (hd.home && hd.home !== hd.sources[0]) {
220
+ pkg["homepage"] = { url: hd.home };
221
+ }
222
+ }
223
+ if (hd.home && !pkg["homepage"]) {
224
+ pkg["homepage"] = { url: hd.home };
225
+ }
226
+ if (hd.digest) {
227
+ pkg._integrity = `sha256-${hd.digest}`;
228
+ }
229
+
230
+ pkgList.push(pkg);
231
+ }
232
+ }
233
+ }
234
+ }
235
+ return pkgList;
236
+ }
237
+
238
+ /**
239
+ * Recursively walks a parsed YAML/JSON object structure to find container image
240
+ * references stored under common keys (image, repository, dockerImage, etc.) and
241
+ * appends discovered image and service entries to pkgList while tracking seen
242
+ * images in imgList to avoid duplicates.
243
+ *
244
+ * @param {Object|Array|string} keyValueObj The object, array, or string node to inspect
245
+ * @param {Object[]} pkgList Accumulator array that receives {image} and {service} entries
246
+ * @param {string[]} imgList Accumulator array of image name strings already seen
247
+ * @returns {string[]} The updated imgList
248
+ */
249
+ export function recurseImageNameLookup(keyValueObj, pkgList, imgList) {
250
+ if (typeof keyValueObj === "string" || keyValueObj instanceof String) {
251
+ return imgList;
252
+ }
253
+ if (Array.isArray(keyValueObj)) {
254
+ for (const ele of keyValueObj) {
255
+ if (typeof ele !== "string") {
256
+ recurseImageNameLookup(ele, pkgList, imgList);
257
+ }
258
+ }
259
+ } else if (Object.keys(keyValueObj).length) {
260
+ let imageLike =
261
+ keyValueObj.image ||
262
+ keyValueObj.repository ||
263
+ keyValueObj.dockerImage ||
264
+ keyValueObj.mavenImage ||
265
+ keyValueObj.gradleImage ||
266
+ keyValueObj.packImage ||
267
+ keyValueObj.koImage ||
268
+ keyValueObj.kanikoImage;
269
+ if (
270
+ !imageLike &&
271
+ keyValueObj.name &&
272
+ typeof keyValueObj.name === "string" &&
273
+ keyValueObj.name.includes("/")
274
+ ) {
275
+ imageLike = keyValueObj.name;
276
+ }
277
+ if (
278
+ imageLike &&
279
+ typeof imageLike === "string" &&
280
+ !imgList.includes(imageLike)
281
+ ) {
282
+ if (imageLike.includes("VERSION")) {
283
+ imageLike = imageLike
284
+ .replaceAll(":${VERSION:-", ":")
285
+ .replaceAll(":${VERSION:", ":")
286
+ .replaceAll(":%VERSION%", ":latest")
287
+ .replaceAll("}", "");
288
+ }
289
+ pkgList.push({ image: imageLike });
290
+ pkgList.push({ service: keyValueObj.name || imageLike });
291
+ imgList.push(imageLike);
292
+ }
293
+ for (const key of Object.keys(keyValueObj)) {
294
+ // Skip unwanted blocks to improve performance
295
+ if (["schema", "openAPIV3Schema", "names", "status"].includes(key)) {
296
+ continue;
297
+ }
298
+ const valueObj = keyValueObj[key];
299
+ if (!valueObj) {
300
+ continue;
301
+ }
302
+ if (Object.keys(valueObj).length && typeof valueObj !== "string") {
303
+ recurseImageNameLookup(valueObj, pkgList, imgList);
304
+ }
305
+ if (Array.isArray(valueObj)) {
306
+ for (const ele of valueObj) {
307
+ if (typeof ele !== "string") {
308
+ recurseImageNameLookup(ele, pkgList, imgList);
309
+ }
310
+ }
311
+ }
312
+ }
313
+ }
314
+ return imgList;
315
+ }
316
+
317
+ function substituteBuildArgs(statement, buildArgs) {
318
+ for (const argMatch of [
319
+ ...statement.matchAll(/\${?([^:\/\\}]+)}?/g),
320
+ ].reverse()) {
321
+ const fullArgName = argMatch[0];
322
+ const argName = argMatch[1];
323
+ const argIndex = argMatch.index;
324
+ if (buildArgs.has(argName)) {
325
+ statement =
326
+ statement.slice(0, argIndex) +
327
+ buildArgs.get(argName) +
328
+ statement.slice(argIndex + fullArgName.length);
329
+ }
330
+ }
331
+ return statement;
332
+ }
333
+
334
+ /**
335
+ * Parses the contents of a Dockerfile or Containerfile and returns a list of
336
+ * base image objects referenced by FROM instructions, substituting ARG default
337
+ * values where possible and skipping multi-stage build alias references.
338
+ *
339
+ * @param {string} fileContents Raw string contents of the Dockerfile/Containerfile
340
+ * @returns {Object[]} Array of objects with an image property for each unique base image
341
+ */
342
+ export function parseContainerFile(fileContents) {
343
+ const buildArgs = new Map();
344
+ const imagesSet = new Set();
345
+ const buildStageNames = [];
346
+ for (let line of fileContents.split("\n")) {
347
+ line = line.trim();
348
+
349
+ if (line.startsWith("#")) {
350
+ continue; // skip commented out lines
351
+ }
352
+
353
+ if (line.startsWith("ARG")) {
354
+ const argStatement = line.split("ARG ")[1].split("=");
355
+
356
+ if (argStatement.length < 2) {
357
+ continue; // skip ARG statements without default value
358
+ }
359
+
360
+ const argName = argStatement[0].trim();
361
+ let argValue = argStatement[1].trim().replace(/['"]+/g, "");
362
+ if (argValue.includes("$")) {
363
+ argValue = substituteBuildArgs(argValue, buildArgs);
364
+ }
365
+ buildArgs.set(argName, argValue);
366
+ }
367
+
368
+ if (line.startsWith("FROM")) {
369
+ // The alias could be called AS or as
370
+ const fromStatement = line.split("FROM ")[1].split(/\s(as|AS)\s/);
371
+
372
+ let imageStatement = fromStatement[0].trim();
373
+ const buildStageName =
374
+ fromStatement.length > 1
375
+ ? fromStatement[fromStatement.length - 1].trim()
376
+ : undefined;
377
+ if (buildStageNames.includes(imageStatement)) {
378
+ if (DEBUG_MODE) {
379
+ console.log(
380
+ `Skipping image ${imageStatement} which uses previously seen build stage name.`,
381
+ );
382
+ }
383
+ continue;
384
+ }
385
+ if (imageStatement.includes("$")) {
386
+ imageStatement = substituteBuildArgs(imageStatement, buildArgs);
387
+ if (imageStatement.includes("$")) {
388
+ if (DEBUG_MODE) {
389
+ console.log(
390
+ `Unable to substitute build arguments in '${line}' statement.`,
391
+ );
392
+ }
393
+ continue;
394
+ }
395
+ }
396
+ imagesSet.add(imageStatement);
397
+
398
+ if (buildStageName) {
399
+ buildStageNames.push(buildStageName);
400
+ }
401
+ }
402
+ }
403
+
404
+ return Array.from(imagesSet).map((i) => {
405
+ return { image: i };
406
+ });
407
+ }
408
+
409
+ /**
410
+ * Parses a Bitbucket Pipelines YAML file and extracts all Docker image references
411
+ * used as build environments and pipe references (docker:// pipes are normalized).
412
+ *
413
+ * @param {string} fileContents Raw string contents of the bitbucket-pipelines.yml file
414
+ * @returns {Object[]} Array of objects with an image property for each referenced image or pipe
415
+ */
416
+ export function parseBitbucketPipelinesFile(fileContents) {
417
+ const imgList = [];
418
+
419
+ let privateImageBlockFound = false;
420
+
421
+ for (let line of fileContents.split("\n")) {
422
+ line = line.trim();
423
+ if (line.startsWith("#")) {
424
+ continue; // skip commented out lines
425
+ }
426
+
427
+ // Assume this is a private build image object
428
+ if (line.startsWith("name:") && privateImageBlockFound) {
429
+ const imageName = line.split("name:").pop().trim();
430
+
431
+ imgList.push({
432
+ image: imageName,
433
+ });
434
+
435
+ privateImageBlockFound = false;
436
+ }
437
+
438
+ // Docker image usage
439
+ if (line.startsWith("image:")) {
440
+ const imageName = line.split("image:").pop().trim();
441
+
442
+ /**
443
+ * Assume this is a private build image object
444
+ * See: https://support.atlassian.com/bitbucket-cloud/docs/use-docker-images-as-build-environments/#Using-private-build-images
445
+ */
446
+ if (imageName === "") {
447
+ privateImageBlockFound = true;
448
+ continue;
449
+ }
450
+ /**
451
+ * Assume this is a public build image
452
+ * See: https://support.atlassian.com/bitbucket-cloud/docs/use-docker-images-as-build-environments/#Using-public-build-images
453
+ */
454
+
455
+ imgList.push({
456
+ image: imageName,
457
+ });
458
+ }
459
+
460
+ // Pipe usage
461
+ if (line.startsWith("- pipe:")) {
462
+ let pipeName = line.split("- pipe:").pop().trim();
463
+
464
+ if (pipeName.startsWith("docker://")) {
465
+ pipeName = pipeName.replace("docker://", "");
466
+ }
467
+
468
+ imgList.push({
469
+ image: pipeName,
470
+ });
471
+ }
472
+ }
473
+
474
+ return imgList;
475
+ }
476
+
477
+ /**
478
+ * Parses container specification data such as Docker Compose files, Kubernetes
479
+ * manifests, Tekton tasks, Skaffold configs, or Kustomize overlays (YAML, possibly
480
+ * multi-document) and returns a list of image, service, and OCI spec entries.
481
+ *
482
+ * @param {string} dcData Raw YAML string contents of the container spec file
483
+ * @returns {Object[]} Array of objects with image, service, or ociSpec properties
484
+ */
485
+ export function parseContainerSpecData(dcData) {
486
+ const pkgList = [];
487
+ const imgList = [];
488
+ if (!dcData.includes("image") && !dcData.includes("kind")) {
489
+ return pkgList;
490
+ }
491
+ let dcDataList = [dcData];
492
+ if (dcData.includes("---")) {
493
+ dcDataList = dcData.split("---");
494
+ }
495
+ for (const dcData of dcDataList) {
496
+ let yamlObj;
497
+ try {
498
+ yamlObj = _load(dcData);
499
+ } catch (_err) {
500
+ // ignore errors
501
+ }
502
+ if (!yamlObj) {
503
+ continue;
504
+ }
505
+ if (yamlObj.services) {
506
+ for (const serv of Object.keys(yamlObj.services)) {
507
+ pkgList.push({
508
+ service: serv,
509
+ });
510
+ const aservice = yamlObj.services[serv];
511
+ // Track locally built images
512
+ if (aservice.build) {
513
+ if (Object.keys(aservice.build).length && aservice.build.dockerfile) {
514
+ pkgList.push({
515
+ ociSpec: aservice.build.dockerfile,
516
+ });
517
+ } else {
518
+ if (aservice.build === "." || aservice.build === "./") {
519
+ pkgList.push({
520
+ ociSpec: "Dockerfile",
521
+ });
522
+ } else {
523
+ pkgList.push({
524
+ ociSpec: aservice.build,
525
+ });
526
+ }
527
+ }
528
+ } else if (aservice.image && !imgList.includes(aservice.image)) {
529
+ let imgFullName = aservice.image;
530
+ if (imgFullName.includes(":${VERSION:")) {
531
+ imgFullName = imgFullName
532
+ .replaceAll(":${VERSION:-", ":")
533
+ .replaceAll(":${VERSION:", ":")
534
+ .replaceAll("}", "");
535
+ }
536
+ pkgList.push({
537
+ image: imgFullName,
538
+ });
539
+ imgList.push(imgFullName);
540
+ }
541
+ }
542
+ }
543
+ // Tekton tasks and kustomize have spec. Skaffold has build
544
+ const recurseBlock = yamlObj.spec || yamlObj.build || yamlObj.images;
545
+ if (recurseBlock) {
546
+ recurseImageNameLookup(recurseBlock, pkgList, imgList);
547
+ }
548
+ }
549
+ return pkgList;
550
+ }
551
+
552
+ /**
553
+ * Identifies the data flow direction of a Privado processing object based on its
554
+ * sinkId value: "write" sinks map to "inbound", "read" sinks to "outbound", and
555
+ * HTTP/gRPC sinks to "bi-directional".
556
+ *
557
+ * @param {Object} processingObj Privado processing object, expected to have a sinkId property
558
+ * @returns {string} Flow direction string: "inbound", "outbound", "bi-directional", or "unknown"
559
+ */
560
+ export function identifyFlow(processingObj) {
561
+ let flow = "unknown";
562
+ if (processingObj.sinkId) {
563
+ const sinkId = processingObj.sinkId.toLowerCase();
564
+ if (sinkId.endsWith("write")) {
565
+ flow = "inbound";
566
+ } else if (sinkId.endsWith("read")) {
567
+ flow = "outbound";
568
+ } else if (sinkId.includes("http") || sinkId.includes("grpc")) {
569
+ flow = "bi-directional";
570
+ }
571
+ }
572
+ return flow;
573
+ }
574
+
575
+ function convertProcessing(processing_list) {
576
+ const data_list = [];
577
+ for (const p of processing_list) {
578
+ data_list.push({
579
+ classification: p.sourceId || p.sinkId,
580
+ flow: identifyFlow(p),
581
+ });
582
+ }
583
+ return data_list;
584
+ }
585
+
586
+ /**
587
+ * Parses a Privado data flow JSON file and returns a list of service objects
588
+ * enriched with data classifications, endpoints, trust-boundary flag, violations,
589
+ * and git metadata properties extracted from the scan result.
590
+ *
591
+ * @param {string} f Path to the Privado scan result JSON file
592
+ * @returns {Object[]} List of service component objects suitable for a SaaSBOM
593
+ */
594
+ export function parsePrivadoFile(f) {
595
+ const pData = readFileSync(f, { encoding: "utf-8" });
596
+ const servlist = [];
597
+ if (!pData) {
598
+ return servlist;
599
+ }
600
+ const jsonData = JSON.parse(pData);
601
+ const aservice = {
602
+ "x-trust-boundary": false,
603
+ properties: [],
604
+ data: [],
605
+ endpoints: [],
606
+ };
607
+ if (jsonData.repoName) {
608
+ aservice.name = jsonData.repoName;
609
+ aservice.properties = [
610
+ {
611
+ name: "internal:SrcFile",
612
+ value: f,
613
+ },
614
+ ];
615
+ // Capture git metadata info
616
+ if (jsonData.gitMetadata) {
617
+ aservice.version = jsonData.gitMetadata.commitId || "";
618
+ aservice.properties.push({
619
+ name: "internal:privadoCoreVersion",
620
+ value: jsonData.privadoCoreVersion || "",
621
+ });
622
+ aservice.properties.push({
623
+ name: "internal:privadoCLIVersion",
624
+ value: jsonData.privadoCLIVersion || "",
625
+ });
626
+ aservice.properties.push({
627
+ name: "internal:localScanPath",
628
+ value: jsonData.localScanPath || "",
629
+ });
630
+ }
631
+ // Capture processing
632
+ if (jsonData.processing?.length) {
633
+ aservice.data = aservice.data.concat(
634
+ convertProcessing(jsonData.processing),
635
+ );
636
+ }
637
+ // Capture sink processing
638
+ if (jsonData.sinkProcessing?.length) {
639
+ aservice.data = aservice.data.concat(
640
+ convertProcessing(jsonData.sinkProcessing),
641
+ );
642
+ }
643
+ // Find endpoints
644
+ if (jsonData.collections) {
645
+ const endpoints = [];
646
+ for (const c of jsonData.collections) {
647
+ for (const occ of c.collections) {
648
+ for (const e of occ.occurrences) {
649
+ if (e.endPoint) {
650
+ endpoints.push(e.endPoint);
651
+ }
652
+ }
653
+ }
654
+ }
655
+ aservice.endpoints = endpoints;
656
+ }
657
+ // Capture violations
658
+ if (jsonData.violations) {
659
+ for (const v of jsonData.violations) {
660
+ aservice.properties.push({
661
+ name: "internal:privado_violations",
662
+ value: v.policyId,
663
+ });
664
+ }
665
+ }
666
+ // If there are third party libraries detected, then there are cross boundary calls happening
667
+ if (jsonData.dataFlow?.third_parties?.length) {
668
+ aservice["x-trust-boundary"] = true;
669
+ }
670
+ servlist.push(aservice);
671
+ }
672
+ return servlist;
673
+ }
674
+
675
+ /**
676
+ * Parses an OpenAPI specification (JSON or YAML string) and returns a list
677
+ * containing a single service object with name, version, endpoints, and
678
+ * authentication flag derived from the spec's info, servers, paths, and
679
+ * securitySchemes sections.
680
+ *
681
+ * @param {string} oaData Raw JSON or YAML string contents of an OpenAPI specification
682
+ * @returns {Object[]} List containing a single service component object
683
+ */
684
+ export function parseOpenapiSpecData(oaData) {
685
+ const servlist = [];
686
+ if (!oaData) {
687
+ return servlist;
688
+ }
689
+ try {
690
+ if (oaData.startsWith("openapi:")) {
691
+ oaData = _load(oaData);
692
+ } else {
693
+ oaData = JSON.parse(oaData);
694
+ }
695
+ } catch (_e) {
696
+ return servlist;
697
+ }
698
+
699
+ const name = oaData.info?.title
700
+ ? oaData.info.title.replace(/ /g, "-")
701
+ : "default-name";
702
+ const version = oaData.info?.version ? oaData.info.version : "latest";
703
+ const aservice = {
704
+ "bom-ref": `urn:service:${name}:${version}`,
705
+ name,
706
+ description: oaData.description || "",
707
+ version,
708
+ };
709
+ let serverName = [];
710
+ if (oaData.servers?.length && oaData.servers[0].url) {
711
+ serverName = oaData.servers[0].url;
712
+ if (!serverName.startsWith("http") || !serverName.includes("//")) {
713
+ serverName = `http://${serverName}`;
714
+ }
715
+ }
716
+ if (oaData.paths) {
717
+ const endpoints = [];
718
+ for (const route of Object.keys(oaData.paths)) {
719
+ let sep = "";
720
+ if (!route.startsWith("/")) {
721
+ sep = "/";
722
+ }
723
+ endpoints.push(`${serverName}${sep}${route}`);
724
+ }
725
+ aservice.endpoints = endpoints;
726
+ }
727
+ let authenticated = false;
728
+ if (oaData.components?.securitySchemes) {
729
+ authenticated = true;
730
+ }
731
+ aservice.authenticated = authenticated;
732
+ servlist.push(aservice);
733
+ return servlist;
734
+ }
735
+
736
+ /**
737
+ * Parses Haskell Cabal freeze file content and extracts package name and version
738
+ * pairs from constraint lines (lines containing " ==").
739
+ *
740
+ * @param {string} cabalData Raw string contents of a Cabal freeze file
741
+ * @returns {Object[]} List of package objects with name and version fields
742
+ */
743
+ export function parseCabalData(cabalData) {
744
+ const pkgList = [];
745
+ if (!cabalData) {
746
+ return pkgList;
747
+ }
748
+ cabalData.split("\n").forEach((l) => {
749
+ if (!l.includes(" ==")) {
750
+ return;
751
+ }
752
+ l = l.replaceAll("\r", "");
753
+ if (l.includes(" ==")) {
754
+ const tmpA = l.split(" ==");
755
+ const name = tmpA[0]
756
+ .replace(/^constraints: /, "")
757
+ .replace(/^any\./, "")
758
+ .trim();
759
+ const version = tmpA[1].replace(",", "").trim();
760
+ if (name && version) {
761
+ pkgList.push({
762
+ name,
763
+ version,
764
+ });
765
+ }
766
+ }
767
+ });
768
+ return pkgList;
769
+ }
770
+
771
+ /**
772
+ * Parses an Elixir mix.lock file and extracts Hex package name and version pairs
773
+ * from lines containing ":hex".
774
+ *
775
+ * @param {string} mixData Raw string contents of a mix.lock file
776
+ * @returns {Object[]} List of package objects with name and version fields
777
+ */
778
+ export function parseMixLockData(mixData) {
779
+ const pkgList = [];
780
+ if (!mixData) {
781
+ return pkgList;
782
+ }
783
+ mixData.split("\n").forEach((l) => {
784
+ if (!l.includes(":hex")) {
785
+ return;
786
+ }
787
+ l = l.replaceAll("\r", "");
788
+ if (l.includes(":hex")) {
789
+ const tmpA = l.split(",");
790
+ if (tmpA.length > 3) {
791
+ const name = tmpA[1].replace(/^\s*:/, "").trim();
792
+ const version = tmpA[2].trim().replace(/"/g, "");
793
+ if (name && version) {
794
+ pkgList.push({
795
+ name,
796
+ version,
797
+ });
798
+ }
799
+ }
800
+ }
801
+ });
802
+ return pkgList;
803
+ }
804
+
805
+ /**
806
+ * Parses a GitHub Actions workflow YAML file and returns a list of action
807
+ * components for each step that uses an external action (steps with a "uses"
808
+ * field). Each component captures the action name, group, version/commit SHA,
809
+ * version pinning type, job context (runner, permissions, environment), and
810
+ * workflow-level metadata (triggers, concurrency, write permissions).
811
+ *
812
+ * @param {string} f Path to the GitHub Actions workflow YAML file
813
+ * @returns {Object[]} List of action component objects with purl, properties, and evidence
814
+ */
815
+ export function parseGitHubWorkflowData(f) {
816
+ const { components } = parseWorkflowFile(f);
817
+ return components.filter(
818
+ (component) =>
819
+ component.scope === "required" ||
820
+ component.properties?.some(
821
+ (property) =>
822
+ property?.name === "cdx:github:step:usesCargo" &&
823
+ property?.value === "true",
824
+ ),
825
+ );
826
+ }
827
+
828
+ /**
829
+ * Parse Google Cloud Build YAML data and extract container image steps as packages.
830
+ *
831
+ * @param {string} cbwData Raw YAML string of a Cloud Build configuration file
832
+ * @returns {Object[]} Array of package objects parsed from the build steps
833
+ */
834
+ export function parseCloudBuildData(cbwData) {
835
+ const pkgList = [];
836
+ const keys_cache = {};
837
+ if (!cbwData) {
838
+ return pkgList;
839
+ }
840
+ const yamlObj = _load(cbwData);
841
+ if (!yamlObj) {
842
+ return pkgList;
843
+ }
844
+ if (yamlObj.steps) {
845
+ for (const step of yamlObj.steps) {
846
+ if (step.name) {
847
+ const tmpA = step.name.split(":");
848
+ if (tmpA.length === 2) {
849
+ let group = dirname(tmpA[0]);
850
+ const name = basename(tmpA[0]);
851
+ if (group === ".") {
852
+ group = "";
853
+ }
854
+ const version = tmpA[1];
855
+ const key = `${group}-${name}-${version}`;
856
+ if (!keys_cache[key] && name && version) {
857
+ keys_cache[key] = key;
858
+ pkgList.push({
859
+ group,
860
+ name,
861
+ version,
862
+ });
863
+ }
864
+ }
865
+ }
866
+ }
867
+ }
868
+ return pkgList;
869
+ }
870
+
871
+ /**
872
+ * Parse Conan lock file data (conan.lock) and return the package list, dependency map,
873
+ * and parent component dependencies.
874
+ *
875
+ * Supports both the legacy `graph_lock.nodes` format (Conan 1.x) and the newer
876
+ * `requires` format (Conan 2.x).
877
+ *
878
+ * @param {string} conanLockData Raw JSON string of the Conan lock file
879
+ * @returns {{ pkgList: Object[], dependencies: Object, parentComponentDependencies: string[] }}
880
+ */
881
+ export function parseConanLockData(conanLockData) {
882
+ const pkgList = [];
883
+ const dependencies = {};
884
+ const parentComponentDependencies = [];
885
+
886
+ if (!conanLockData) {
887
+ return { pkgList, dependencies, parentComponentDependencies };
888
+ }
889
+
890
+ const lockFile = JSON.parse(conanLockData);
891
+ if (!lockFile?.graph_lock?.nodes && !lockFile.requires) {
892
+ return { pkgList, dependencies, parentComponentDependencies };
893
+ }
894
+
895
+ if (lockFile.graph_lock?.nodes) {
896
+ const depends = lockFile.graph_lock.nodes;
897
+ const nodeKeyToBomRefMap = {};
898
+
899
+ for (const nk of Object.keys(depends)) {
900
+ if (!depends[nk].ref) continue;
901
+
902
+ const [purl, name, version] = mapConanPkgRefToPurlStringAndNameAndVersion(
903
+ depends[nk].ref,
904
+ );
905
+ if (purl === null) continue;
906
+
907
+ const bomRef = decodeURIComponent(purl);
908
+ pkgList.push({
909
+ name,
910
+ version,
911
+ purl,
912
+ "bom-ref": bomRef,
913
+ });
914
+
915
+ nodeKeyToBomRefMap[nk] = bomRef;
916
+ }
917
+
918
+ for (const nk of Object.keys(depends)) {
919
+ let requirementNodeKeys = [];
920
+ if (Array.isArray(depends[nk].requires))
921
+ requirementNodeKeys = requirementNodeKeys.concat(depends[nk].requires);
922
+ if (Array.isArray(depends[nk].build_requires))
923
+ requirementNodeKeys = requirementNodeKeys.concat(
924
+ depends[nk].build_requires,
925
+ );
926
+
927
+ for (const dependencyNodeKey of requirementNodeKeys) {
928
+ const dependencyBomRef = nodeKeyToBomRefMap[dependencyNodeKey];
929
+ if (!dependencyBomRef) continue;
930
+
931
+ const dependentBomRef = nodeKeyToBomRefMap[nk];
932
+ if (dependentBomRef) {
933
+ if (!(dependentBomRef in dependencies))
934
+ dependencies[dependentBomRef] = [];
935
+ if (!dependencies[dependentBomRef].includes(dependencyBomRef))
936
+ dependencies[dependentBomRef].push(dependencyBomRef);
937
+ } else if (nk === "0") {
938
+ // parent component for which the conan.lock was generated
939
+ if (!parentComponentDependencies.includes(dependencyBomRef))
940
+ parentComponentDependencies.push(dependencyBomRef);
941
+ }
942
+ }
943
+ }
944
+ } else if (lockFile.requires) {
945
+ const depends = lockFile.requires;
946
+ for (const nk of Object.keys(depends)) {
947
+ depends[nk] = depends[nk].split("%").shift();
948
+ const [purl, name, version] = mapConanPkgRefToPurlStringAndNameAndVersion(
949
+ depends[nk],
950
+ );
951
+ if (purl !== null) {
952
+ pkgList.push({
953
+ name,
954
+ version,
955
+ purl,
956
+ "bom-ref": decodeURIComponent(purl),
957
+ });
958
+ }
959
+ }
960
+ }
961
+ return { pkgList, dependencies, parentComponentDependencies };
962
+ }
963
+
964
+ /**
965
+ * Parse a Conan conanfile.txt and extract required and optional packages.
966
+ *
967
+ * @param {string} conanData Raw text contents of a conanfile.txt
968
+ * @returns {Object[]} Array of package objects with purl, name, version, and scope
969
+ */
970
+ export function parseConanData(conanData) {
971
+ const pkgList = [];
972
+ if (!conanData) {
973
+ return pkgList;
974
+ }
975
+ let scope = "required";
976
+ conanData.split("\n").forEach((l) => {
977
+ l = l.replaceAll("\r", "");
978
+ if (l.includes("[build_requires]")) {
979
+ scope = "optional";
980
+ }
981
+ if (l.includes("[requires]")) {
982
+ scope = "required";
983
+ }
984
+
985
+ // The line must start with sequence non-whitespace characters, followed by a slash,
986
+ // followed by at least one more non-whitespace character.
987
+ // Provides a heuristic for locating Conan package references inside conanfile.txt files.
988
+ if (l.match(/^[^\s\/]+\/\S+/)) {
989
+ const [purl, name, version] =
990
+ mapConanPkgRefToPurlStringAndNameAndVersion(l);
991
+ if (purl !== null) {
992
+ pkgList.push({
993
+ name,
994
+ version,
995
+ purl,
996
+ "bom-ref": decodeURIComponent(purl),
997
+ scope,
998
+ });
999
+ }
1000
+ }
1001
+ });
1002
+ return pkgList;
1003
+ }
1004
+
1005
+ /**
1006
+ * Construct a generic package component object for collider-managed packages.
1007
+ *
1008
+ * @param {string} name Package name
1009
+ * @param {Object} pkgData Locked package entry from collider.lock
1010
+ * @param {string} lockFile Source lock file path
1011
+ * @param {string} dependencyKind Whether the package is direct or transitive
1012
+ * @returns {Object|undefined} Package component
1013
+ */
1014
+ function buildColliderComponent(name, pkgData, lockFile, dependencyKind) {
1015
+ if (!name) {
1016
+ return undefined;
1017
+ }
1018
+ pkgData = pkgData || {};
1019
+ dependencyKind = dependencyKind || "transitive";
1020
+ const version = pkgData?.version || "";
1021
+ const purl = build({
1022
+ type: "generic",
1023
+ namespace: "" || null,
1024
+ name: name,
1025
+ version: version || undefined || null,
1026
+ });
1027
+ const properties = [
1028
+ {
1029
+ name: "cdx:collider:dependencyKind",
1030
+ value: dependencyKind,
1031
+ },
1032
+ ];
1033
+ if (lockFile) {
1034
+ properties.unshift({
1035
+ name: "internal:SrcFile",
1036
+ value: lockFile,
1037
+ });
1038
+ }
1039
+ const wrapHash =
1040
+ typeof pkgData?.wrap_hash === "string" ? pkgData.wrap_hash.trim() : "";
1041
+ const wrapHashMatch = wrapHash.match(/^sha256:([0-9A-Fa-f]{64})$/);
1042
+ if (wrapHash) {
1043
+ properties.push({
1044
+ name: "cdx:collider:wrapHash",
1045
+ value: wrapHash,
1046
+ });
1047
+ }
1048
+ properties.push({
1049
+ name: "cdx:collider:hasWrapHash",
1050
+ value: wrapHashMatch ? "true" : "false",
1051
+ });
1052
+ if (wrapHash && !wrapHashMatch) {
1053
+ properties.push({
1054
+ name: "cdx:collider:wrapHashInvalid",
1055
+ value: "true",
1056
+ });
1057
+ }
1058
+ let originReference;
1059
+ if (typeof pkgData?.origin === "string" && pkgData.origin.trim()) {
1060
+ try {
1061
+ const originUrl = new URL(pkgData.origin.trim());
1062
+ const originHadSensitiveParts = Boolean(
1063
+ originUrl.username ||
1064
+ originUrl.password ||
1065
+ originUrl.search ||
1066
+ originUrl.hash,
1067
+ );
1068
+ originUrl.username = "";
1069
+ originUrl.password = "";
1070
+ originUrl.search = "";
1071
+ originUrl.hash = "";
1072
+ originReference = originUrl.toString();
1073
+ properties.push({
1074
+ name: "cdx:collider:origin",
1075
+ value: originReference,
1076
+ });
1077
+ properties.push({
1078
+ name: "cdx:collider:originScheme",
1079
+ value: originUrl.protocol.replace(":", ""),
1080
+ });
1081
+ if (originUrl.host) {
1082
+ properties.push({
1083
+ name: "cdx:collider:originHost",
1084
+ value: originUrl.host,
1085
+ });
1086
+ }
1087
+ if (originHadSensitiveParts) {
1088
+ properties.push({
1089
+ name: "cdx:collider:originSanitized",
1090
+ value: "true",
1091
+ });
1092
+ }
1093
+ } catch {
1094
+ thoughtLog("Ignoring invalid Collider origin URL");
1095
+ }
1096
+ }
1097
+ const component = {
1098
+ name,
1099
+ version,
1100
+ purl,
1101
+ "bom-ref": decodeURIComponent(purl),
1102
+ properties,
1103
+ };
1104
+ if (dependencyKind === "direct") {
1105
+ component.scope = "required";
1106
+ }
1107
+ if (wrapHashMatch) {
1108
+ component.hashes = [
1109
+ {
1110
+ alg: "SHA-256",
1111
+ content: wrapHashMatch[1].toLowerCase(),
1112
+ },
1113
+ ];
1114
+ }
1115
+ if (originReference) {
1116
+ component.externalReferences = [
1117
+ {
1118
+ type: "distribution",
1119
+ url: originReference,
1120
+ },
1121
+ ];
1122
+ }
1123
+ return component;
1124
+ }
1125
+
1126
+ /**
1127
+ * Parse Collider lock file data (collider.lock) and return the package list and
1128
+ * parent component dependencies.
1129
+ *
1130
+ * @param {string} colliderLockData Raw JSON string of the Collider lock file
1131
+ * @param {string} lockFile Source lock file path
1132
+ * @returns {{ pkgList: Object[], dependencies: Object, parentComponentDependencies: string[] }}
1133
+ */
1134
+ export function parseColliderLockData(colliderLockData, lockFile) {
1135
+ const pkgList = [];
1136
+ const dependencies = {};
1137
+ const parentComponentDependencies = [];
1138
+ if (!colliderLockData) {
1139
+ return { pkgList, dependencies, parentComponentDependencies };
1140
+ }
1141
+ let parsedLockFile;
1142
+ try {
1143
+ parsedLockFile = JSON.parse(colliderLockData);
1144
+ } catch {
1145
+ return { pkgList, dependencies, parentComponentDependencies };
1146
+ }
1147
+ const addedBomRefs = new Set();
1148
+ const directDependencies = parsedLockFile.dependencies || {};
1149
+ const packages = parsedLockFile.packages || {};
1150
+ for (const [name, pkgData] of Object.entries(directDependencies)) {
1151
+ const component = buildColliderComponent(name, pkgData, lockFile, "direct");
1152
+ if (!component) {
1153
+ continue;
1154
+ }
1155
+ if (!addedBomRefs.has(component["bom-ref"])) {
1156
+ pkgList.push(component);
1157
+ addedBomRefs.add(component["bom-ref"]);
1158
+ }
1159
+ if (!parentComponentDependencies.includes(component["bom-ref"])) {
1160
+ parentComponentDependencies.push(component["bom-ref"]);
1161
+ }
1162
+ if (!(component["bom-ref"] in dependencies)) {
1163
+ dependencies[component["bom-ref"]] = [];
1164
+ }
1165
+ }
1166
+ for (const [name, pkgData] of Object.entries(packages)) {
1167
+ const component = buildColliderComponent(
1168
+ name,
1169
+ pkgData,
1170
+ lockFile,
1171
+ "transitive",
1172
+ );
1173
+ if (!component || addedBomRefs.has(component["bom-ref"])) {
1174
+ continue;
1175
+ }
1176
+ pkgList.push(component);
1177
+ addedBomRefs.add(component["bom-ref"]);
1178
+ if (!(component["bom-ref"] in dependencies)) {
1179
+ dependencies[component["bom-ref"]] = [];
1180
+ }
1181
+ }
1182
+ return { pkgList, dependencies, parentComponentDependencies };
1183
+ }
1184
+
1185
+ /**
1186
+ * Method to parse flake.nix files
1187
+ *
1188
+ * @param {String} flakeNixFile flake.nix file to parse
1189
+ * @returns {Object} Object containing package information
1190
+ */
1191
+ export function parseFlakeNix(flakeNixFile) {
1192
+ const pkgList = [];
1193
+ const dependencies = [];
1194
+
1195
+ if (!safeExistsSync(flakeNixFile)) {
1196
+ return { pkgList, dependencies };
1197
+ }
1198
+
1199
+ try {
1200
+ const flakeContent = readFileSync(flakeNixFile, "utf-8");
1201
+
1202
+ // Extract inputs from flake.nix using regex
1203
+ const inputsRegex = /inputs\s*=\s*\{[^}]*}/g;
1204
+ let match;
1205
+ while ((match = inputsRegex.exec(flakeContent)) !== null) {
1206
+ const inputBlock = match[0];
1207
+
1208
+ // Match different input patterns including nested inputs
1209
+ const inputPatterns = [
1210
+ /([a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*)\.url\s*=\s*"([^"]+)"/g,
1211
+ /([a-zA-Z0-9_-]+(?:\.[a-zA-Z0-9_-]+)*)\s*=\s*\{\s*url\s*=\s*"([^"]+)"[^}]*}/gs,
1212
+ ];
1213
+
1214
+ const addedPackages = new Set();
1215
+
1216
+ for (const pattern of inputPatterns) {
1217
+ let subMatch;
1218
+ pattern.lastIndex = 0;
1219
+ while ((subMatch = pattern.exec(inputBlock)) !== null) {
1220
+ const name = subMatch[1];
1221
+ const url = subMatch[2] || subMatch[3];
1222
+
1223
+ if (name && url && !addedPackages.has(name)) {
1224
+ addedPackages.add(name);
1225
+ const pkg = {
1226
+ name: name,
1227
+ version: "latest",
1228
+ description: `Nix flake input: ${name}`,
1229
+ scope: "required",
1230
+ properties: [
1231
+ {
1232
+ name: "internal:SrcFile",
1233
+ value: flakeNixFile,
1234
+ },
1235
+ {
1236
+ name: "cdx:nix:input_url",
1237
+ value: url,
1238
+ },
1239
+ ],
1240
+ evidence: {
1241
+ identity: {
1242
+ field: "purl",
1243
+ confidence: 0.8,
1244
+ methods: [
1245
+ {
1246
+ technique: "manifest-analysis",
1247
+ confidence: 0.8,
1248
+ value: flakeNixFile,
1249
+ },
1250
+ ],
1251
+ },
1252
+ },
1253
+ };
1254
+
1255
+ pkg.purl = nixGenericPurl(name, "latest");
1256
+ pkg["bom-ref"] = pkg.purl
1257
+ ? decodeURIComponent(pkg.purl)
1258
+ : `library:${name}:latest`;
1259
+ pkg.properties.push({
1260
+ name: "cdx:purl:proposedType",
1261
+ value: "nix",
1262
+ });
1263
+
1264
+ pkgList.push(pkg);
1265
+ }
1266
+ }
1267
+ }
1268
+ }
1269
+ } catch (error) {
1270
+ console.warn(`Failed to parse ${flakeNixFile}: ${error.message}`);
1271
+ }
1272
+
1273
+ return { pkgList, dependencies };
1274
+ }
1275
+
1276
+ /**
1277
+ * Method to parse flake.lock files
1278
+ *
1279
+ * @param {String} flakeLockFile flake.lock file to parse
1280
+ * @returns {Object} Object containing locked dependency information
1281
+ */
1282
+ export function parseFlakeLock(flakeLockFile) {
1283
+ const pkgList = [];
1284
+ const dependencies = [];
1285
+ const rootInputs = [];
1286
+
1287
+ if (!safeExistsSync(flakeLockFile)) {
1288
+ return { pkgList, dependencies, rootInputs };
1289
+ }
1290
+
1291
+ try {
1292
+ const lockContent = readFileSync(flakeLockFile, "utf-8");
1293
+ const lockData = JSON.parse(lockContent);
1294
+
1295
+ if (lockData.nodes) {
1296
+ for (const [nodeName, nodeData] of Object.entries(lockData.nodes)) {
1297
+ if (nodeName === "root" || !nodeData.locked) continue;
1298
+
1299
+ const locked = nodeData.locked;
1300
+
1301
+ let version = "latest";
1302
+ if (locked.rev) {
1303
+ version = locked.rev.substring(0, 7);
1304
+ } else if (locked.ref) {
1305
+ version = locked.ref;
1306
+ }
1307
+
1308
+ const pkg = {
1309
+ name: nodeName,
1310
+ version: version,
1311
+ description: `Nix flake dependency: ${nodeName}`,
1312
+ scope: "required",
1313
+ properties: [
1314
+ {
1315
+ name: "internal:SrcFile",
1316
+ value: flakeLockFile,
1317
+ },
1318
+ ],
1319
+ evidence: {
1320
+ identity: {
1321
+ field: "purl",
1322
+ confidence: 1.0,
1323
+ methods: [
1324
+ {
1325
+ technique: "manifest-analysis",
1326
+ confidence: 1.0,
1327
+ value: flakeLockFile,
1328
+ },
1329
+ ],
1330
+ },
1331
+ },
1332
+ };
1333
+
1334
+ if (locked.narHash) {
1335
+ pkg.properties.push({
1336
+ name: "cdx:nix:nar_hash",
1337
+ value: locked.narHash,
1338
+ });
1339
+ }
1340
+
1341
+ if (locked.lastModified) {
1342
+ pkg.properties.push({
1343
+ name: "cdx:nix:last_modified",
1344
+ value: locked.lastModified.toString(),
1345
+ });
1346
+ }
1347
+
1348
+ if (locked.rev) {
1349
+ pkg.properties.push({
1350
+ name: "cdx:nix:revision",
1351
+ value: locked.rev,
1352
+ });
1353
+ }
1354
+
1355
+ if (locked.ref) {
1356
+ pkg.properties.push({
1357
+ name: "cdx:nix:ref",
1358
+ value: locked.ref,
1359
+ });
1360
+ }
1361
+
1362
+ const urls = nixLockedUrls(locked);
1363
+ if (urls.vcsUrl) {
1364
+ pkg.properties.push({
1365
+ name: "cdx:nix:vcs_url",
1366
+ value: urls.vcsUrl,
1367
+ });
1368
+ }
1369
+ if (urls.downloadUrl) {
1370
+ pkg.properties.push({
1371
+ name: "cdx:nix:download_url",
1372
+ value: urls.downloadUrl,
1373
+ });
1374
+ }
1375
+
1376
+ pkg.properties.push({
1377
+ name: "cdx:purl:proposedType",
1378
+ value: "nix",
1379
+ });
1380
+
1381
+ const purl = nixGenericPurl(nodeName, version, {
1382
+ ...(urls.vcsUrl ? { vcs_url: urls.vcsUrl } : {}),
1383
+ });
1384
+ if (purl) {
1385
+ pkg.purl = purl;
1386
+ pkg["bom-ref"] = decodeURIComponent(purl);
1387
+ } else {
1388
+ pkg["bom-ref"] = `library:${nodeName}:${version}`;
1389
+ }
1390
+
1391
+ pkgList.push(pkg);
1392
+ }
1393
+
1394
+ // Collect the root's direct inputs as bom-refs. The root dependency edge
1395
+ // itself is built by createNixBom, which is the only place that knows the
1396
+ // parent component's real bom-ref. Emitting a synthetic root here with a
1397
+ // guessed ref produced a dangling edge (no component carried that ref).
1398
+ if (lockData.nodes?.root?.inputs) {
1399
+ const inputNames = Object.keys(lockData.nodes.root.inputs);
1400
+ for (const input of inputNames) {
1401
+ const ref = pkgList.find((pkg) => pkg.name === input)?.["bom-ref"];
1402
+ if (ref) {
1403
+ rootInputs.push(ref);
1404
+ }
1405
+ }
1406
+ }
1407
+ }
1408
+ } catch (error) {
1409
+ console.warn(`Failed to parse ${flakeLockFile}: ${error.message}`);
1410
+ }
1411
+
1412
+ return { pkgList, dependencies, rootInputs };
1413
+ }
1414
+
1415
+ /**
1416
+ * Derive version-control and download URLs for a flake lock `locked` node.
1417
+ *
1418
+ * flake.lock records a `type` discriminator (github, gitlab, sourcehut,
1419
+ * bitbucket, path, tarball, …). Only the git-hosted forge types carry enough
1420
+ * information to reconstruct a resolvable archive URL; local `path` inputs have
1421
+ * none and are left without URLs so downstream tooling does not chase a
1422
+ * non-existent artefact.
1423
+ *
1424
+ * @param {object} locked The `locked` object from a flake.lock node
1425
+ * @returns {{vcsUrl: string|null, downloadUrl: string|null}} derived URLs
1426
+ */
1427
+ function nixLockedUrls(locked) {
1428
+ const { type, owner, repo, rev } = locked;
1429
+ if (!type || !owner || !repo) {
1430
+ return { vcsUrl: null, downloadUrl: null };
1431
+ }
1432
+ const lower = `${type}`.toLowerCase();
1433
+ let vcsUrl = null;
1434
+ let downloadUrl = null;
1435
+ if (lower === "github") {
1436
+ vcsUrl = `https://github.com/${owner}/${repo}`;
1437
+ if (rev) {
1438
+ downloadUrl = `https://github.com/${owner}/${repo}/archive/${rev}.tar.gz`;
1439
+ }
1440
+ } else if (lower === "gitlab") {
1441
+ vcsUrl = `https://gitlab.com/${owner}/${repo}`;
1442
+ if (rev) {
1443
+ downloadUrl = `https://gitlab.com/${owner}/${repo}/-/archive/${rev}/${repo}-${rev}.tar.gz`;
1444
+ }
1445
+ } else if (lower === "bitbucket") {
1446
+ vcsUrl = `https://bitbucket.org/${owner}/${repo}`;
1447
+ if (rev) {
1448
+ downloadUrl = `https://bitbucket.org/${owner}/${repo}/get/${rev}.tar.gz`;
1449
+ }
1450
+ } else if (lower === "sourcehut" || lower === "sr.ht") {
1451
+ vcsUrl = `https://sr.ht/${owner}/${repo}`;
1452
+ }
1453
+ return { vcsUrl, downloadUrl };
1454
+ }
1455
+
1456
+ /**
1457
+ * Parse composer.json file
1458
+ *
1459
+ * @param {string} composerJsonFile composer.json file
1460
+ *
1461
+ * @returns {Object} Object with rootRequires and parent component
1462
+ */
1463
+ export function parseComposerJson(composerJsonFile) {
1464
+ const moduleParent = {};
1465
+ const composerData = JSON.parse(
1466
+ readFileSync(composerJsonFile, { encoding: "utf-8" }),
1467
+ );
1468
+ const rootRequires = {
1469
+ ...composerData.require,
1470
+ ...composerData["require-dev"],
1471
+ };
1472
+ const pkgName = composerData.name;
1473
+ if (pkgName) {
1474
+ moduleParent.group = dirname(pkgName);
1475
+ if (moduleParent.group === ".") {
1476
+ moduleParent.group = "";
1477
+ }
1478
+ moduleParent.name = basename(pkgName);
1479
+ moduleParent.type = "application";
1480
+ moduleParent.version = composerData.version;
1481
+ if (composerData.description) {
1482
+ moduleParent.description = composerData.description;
1483
+ }
1484
+ if (composerData.license) {
1485
+ moduleParent.licenses = getLicenses({
1486
+ expression: composerData.license,
1487
+ });
1488
+ }
1489
+ moduleParent.purl = build({
1490
+ type: "composer",
1491
+ namespace: moduleParent.group || null,
1492
+ name: moduleParent.name,
1493
+ version: moduleParent.version || null,
1494
+ });
1495
+ moduleParent["bom-ref"] = decodeURIComponent(moduleParent.purl);
1496
+ }
1497
+ return { rootRequires, moduleParent };
1498
+ }
1499
+
1500
+ /**
1501
+ * Parse composer lock file
1502
+ *
1503
+ * @param {string} pkgLockFile composer.lock file
1504
+ * @param {array} rootRequires require section from composer.json
1505
+ */
1506
+ export function parseComposerLock(pkgLockFile, rootRequires) {
1507
+ const pkgList = [];
1508
+ const dependenciesList = [];
1509
+ const dependenciesMap = {};
1510
+ const pkgNamePurlMap = {};
1511
+ const rootList = [];
1512
+ const rootRequiresMap = {};
1513
+ if (rootRequires) {
1514
+ for (const rr of Object.keys(rootRequires)) {
1515
+ // Skip platform requirements (php, hhvm, ext-*, lib-*) — they are never
1516
+ // Composer package names and must not be used to identify root packages.
1517
+ if (
1518
+ rr === "php" ||
1519
+ rr === "hhvm" ||
1520
+ rr.startsWith("ext-") ||
1521
+ rr.startsWith("lib-")
1522
+ ) {
1523
+ continue;
1524
+ }
1525
+ rootRequiresMap[rr] = true;
1526
+ }
1527
+ }
1528
+ if (safeExistsSync(pkgLockFile)) {
1529
+ let lockData = {};
1530
+ try {
1531
+ lockData = JSON.parse(readFileSync(pkgLockFile, { encoding: "utf-8" }));
1532
+ } catch (_e) {
1533
+ console.error("Invalid composer.lock file:", pkgLockFile);
1534
+ return [];
1535
+ }
1536
+ if (lockData) {
1537
+ const packages = {};
1538
+ if (lockData["packages"]) {
1539
+ packages["required"] = lockData["packages"];
1540
+ }
1541
+ if (lockData["packages-dev"]) {
1542
+ packages["optional"] = lockData["packages-dev"];
1543
+ }
1544
+ // Pass 1: Collect all packages
1545
+ for (const compScope in packages) {
1546
+ for (const i in packages[compScope]) {
1547
+ const pkg = packages[compScope][i];
1548
+ // Be extra cautious. Potential fix for #236
1549
+ if (!pkg?.name || !pkg.version) {
1550
+ continue;
1551
+ }
1552
+
1553
+ let group = dirname(pkg.name);
1554
+ if (group === ".") {
1555
+ group = "";
1556
+ }
1557
+ const name = basename(pkg.name);
1558
+ const purl = build({
1559
+ type: "composer",
1560
+ namespace: group || null,
1561
+ name: name,
1562
+ version: pkg.version?.toString() || null,
1563
+ });
1564
+ const apkg = {
1565
+ group: group,
1566
+ name: name,
1567
+ purl,
1568
+ "bom-ref": decodeURIComponent(purl),
1569
+ version: pkg.version?.toString(),
1570
+ repository: pkg.source,
1571
+ license: pkg.license,
1572
+ description: pkg.description,
1573
+ scope: compScope,
1574
+ properties: [
1575
+ {
1576
+ name: "internal:SrcFile",
1577
+ value: pkgLockFile,
1578
+ },
1579
+ ],
1580
+ evidence: {
1581
+ identity: {
1582
+ field: "purl",
1583
+ confidence: 1,
1584
+ methods: [
1585
+ {
1586
+ technique: "manifest-analysis",
1587
+ confidence: 1,
1588
+ value: pkgLockFile,
1589
+ },
1590
+ ],
1591
+ },
1592
+ },
1593
+ };
1594
+ if (pkg?.dist?.url) {
1595
+ // Replace placeholders like %prettyVersion% with actual version
1596
+ let distUrl = pkg.dist.url;
1597
+ if (
1598
+ distUrl.includes("%prettyVersion%") ||
1599
+ distUrl.includes("%version%")
1600
+ ) {
1601
+ const version = pkg.version || "unknown";
1602
+ distUrl = distUrl
1603
+ .replace(/%prettyVersion%/g, version)
1604
+ .replace(/%version%/g, version);
1605
+ }
1606
+ apkg.distribution = { url: distUrl };
1607
+ }
1608
+ if (pkg?.authors?.length) {
1609
+ apkg.authors = pkg.authors.map((a) => {
1610
+ return { name: a.name, email: a.email };
1611
+ });
1612
+ }
1613
+ if (pkg?.keywords?.length) {
1614
+ apkg.tags = pkg.keywords;
1615
+ }
1616
+ if (pkg.autoload && Object.keys(pkg.autoload).length) {
1617
+ const namespaces = [];
1618
+ for (const aaload of Object.keys(pkg.autoload)) {
1619
+ if (aaload.startsWith("psr")) {
1620
+ for (const ans of Object.keys(pkg.autoload[aaload])) {
1621
+ namespaces.push(ans.trim());
1622
+ }
1623
+ }
1624
+ }
1625
+ if (namespaces.length) {
1626
+ apkg.properties.push({
1627
+ name: "internal:Namespaces",
1628
+ value: namespaces.join(", "),
1629
+ });
1630
+ }
1631
+ }
1632
+ pkgList.push(apkg);
1633
+ dependenciesMap[purl] = new Set();
1634
+ pkgNamePurlMap[pkg.name] = purl;
1635
+ // Add this package to the root list if needed
1636
+ if (rootRequiresMap[pkg.name]) {
1637
+ rootList.push(apkg);
1638
+ }
1639
+ }
1640
+ }
1641
+ // Pass 2: Construct dependency tree
1642
+ for (const compScope in packages) {
1643
+ for (const i in packages[compScope]) {
1644
+ const pkg = packages[compScope][i];
1645
+ if (!pkg?.name || !pkg.version) {
1646
+ continue;
1647
+ }
1648
+ if (!pkg.require || !Object.keys(pkg.require).length) {
1649
+ continue;
1650
+ }
1651
+ const purl = pkgNamePurlMap[pkg.name];
1652
+ for (const adepName of Object.keys(pkg.require)) {
1653
+ if (pkgNamePurlMap[adepName]) {
1654
+ dependenciesMap[purl].add(pkgNamePurlMap[adepName]);
1655
+ }
1656
+ }
1657
+ }
1658
+ }
1659
+ }
1660
+ }
1661
+ for (const ref in dependenciesMap) {
1662
+ dependenciesList.push({
1663
+ ref: ref,
1664
+ dependsOn: Array.from(dependenciesMap[ref]).sort(),
1665
+ });
1666
+ }
1667
+ return {
1668
+ pkgList,
1669
+ dependenciesList,
1670
+ rootList,
1671
+ };
1672
+ }
1673
+
1674
+ function convertStdoutToList(result) {
1675
+ if (result.status !== 0 || result.error) {
1676
+ return undefined;
1677
+ }
1678
+ const stdout = result.stdout;
1679
+ if (stdout) {
1680
+ const cmdOutput = Buffer.from(stdout).toString();
1681
+ return cmdOutput
1682
+ .trim()
1683
+ .toLowerCase()
1684
+ .split("\n")
1685
+ .filter((p) => p.length > 2 && p.includes("."))
1686
+ .sort();
1687
+ }
1688
+ return undefined;
1689
+ }
1690
+
1691
+ /**
1692
+ * Method to execute dpkg --listfiles to determine the files provided by a given package
1693
+ *
1694
+ * @param {string} pkgName deb package name
1695
+ * @returns
1696
+ */
1697
+ export function executeDpkgList(pkgName) {
1698
+ const result = safeSpawnSync("dpkg", ["--listfiles", "--no-pager", pkgName]);
1699
+ return convertStdoutToList(result);
1700
+ }
1701
+
1702
+ /**
1703
+ * Method to execute dnf repoquery to determine the files provided by a given package
1704
+ *
1705
+ * @param {string} pkgName deb package name
1706
+ * @returns
1707
+ */
1708
+ export function executeRpmList(pkgName) {
1709
+ let result = safeSpawnSync("dnf", ["repoquery", "-l", pkgName]);
1710
+ // Fallback to rpm
1711
+ if (result.status !== 0 || result.error) {
1712
+ result = safeSpawnSync("rpm", ["-ql", pkgName]);
1713
+ }
1714
+ return convertStdoutToList(result);
1715
+ }
1716
+
1717
+ /**
1718
+ * Method to execute apk -L info to determine the files provided by a given package
1719
+ *
1720
+ * @param {string} pkgName deb package name
1721
+ * @returns
1722
+ */
1723
+ export function executeApkList(pkgName) {
1724
+ const result = safeSpawnSync("apk", ["-L", "info", pkgName]);
1725
+ return convertStdoutToList(result);
1726
+ }
1727
+
1728
+ /**
1729
+ * Method to execute alpm -Ql to determine the files provided by a given package
1730
+ *
1731
+ * @param {string} pkgName deb package name
1732
+ * @returns
1733
+ */
1734
+ export function executeAlpmList(pkgName) {
1735
+ const result = safeSpawnSync("pacman", ["-Ql", pkgName]);
1736
+ return convertStdoutToList(result);
1737
+ }
1738
+
1739
+ /**
1740
+ * Method to execute equery files to determine the files provided by a given package
1741
+ *
1742
+ * @param {string} pkgName deb package name
1743
+ * @returns
1744
+ */
1745
+ export function executeEqueryList(pkgName) {
1746
+ const result = safeSpawnSync("equery", ["files", pkgName]);
1747
+ return convertStdoutToList(result);
1748
+ }
1749
+
1750
+ /**
1751
+ * Parse swift dependency tree output json object
1752
+ *
1753
+ * @param {Array} pkgList Package list
1754
+ * @param {Array} dependenciesList Dependencies
1755
+ * @param {string} jsonObject Swift dependencies json object
1756
+ * @param {string} pkgFile Package.swift file
1757
+ */
1758
+ export function parseSwiftJsonTreeObject(
1759
+ pkgList,
1760
+ dependenciesList,
1761
+ jsonObject,
1762
+ pkgFile,
1763
+ ) {
1764
+ let purl;
1765
+ const urlOrPath = jsonObject.url || jsonObject.path;
1766
+ if (jsonObject?.url?.startsWith("http")) {
1767
+ const version = jsonObject.version;
1768
+ purl = purlFromUrlString("swift", urlOrPath, version);
1769
+ } else {
1770
+ // Local swift packages have no remote URL and therefore no valid swift
1771
+ // namespace (cdx-purl requires host/owner). Construct the component
1772
+ // directly without a purl.
1773
+ purl = null;
1774
+ }
1775
+ const rootPkg = applyPurl(
1776
+ {
1777
+ name: purl ? purl.name : jsonObject.name,
1778
+ group: purl ? purl.namespace : "",
1779
+ version: purl ? purl.version : jsonObject.version,
1780
+ },
1781
+ purl ? decodeURIComponent(purl.toString()) : null,
1782
+ jsonObject.name,
1783
+ );
1784
+ const properties = [];
1785
+ if (jsonObject.name && jsonObject.name !== rootPkg.name) {
1786
+ properties.push({
1787
+ name: "cdx:swift:packageName",
1788
+ value: jsonObject.name,
1789
+ });
1790
+ }
1791
+ if (jsonObject?.path?.includes("checkouts")) {
1792
+ const relativeCheckoutPath = relative(dirname(pkgFile), jsonObject.path);
1793
+ if (!relativeCheckoutPath.startsWith("..")) {
1794
+ properties.push({
1795
+ name: "cdx:swift:localCheckoutPath",
1796
+ value: relativeCheckoutPath,
1797
+ });
1798
+ }
1799
+ }
1800
+ if (urlOrPath) {
1801
+ if (urlOrPath.startsWith("http")) {
1802
+ rootPkg.repository = { url: urlOrPath };
1803
+ } else {
1804
+ rootPkg.type = "application";
1805
+ properties.push({
1806
+ name: "internal:SrcPath",
1807
+ value: urlOrPath,
1808
+ });
1809
+ if (pkgFile) {
1810
+ properties.push({
1811
+ name: "internal:SrcFile",
1812
+ value: pkgFile,
1813
+ });
1814
+ }
1815
+ }
1816
+ }
1817
+ if (properties.length) {
1818
+ rootPkg.properties = properties;
1819
+ }
1820
+ pkgList.push(rootPkg);
1821
+ const depList = new Set();
1822
+ if (jsonObject.dependencies) {
1823
+ for (const dependency of jsonObject.dependencies) {
1824
+ const res = parseSwiftJsonTreeObject(
1825
+ pkgList,
1826
+ dependenciesList,
1827
+ dependency,
1828
+ pkgFile,
1829
+ );
1830
+ depList.add(res);
1831
+ }
1832
+ }
1833
+ dependenciesList.push({
1834
+ ref: rootPkg["bom-ref"],
1835
+ dependsOn: [...depList].sort(),
1836
+ });
1837
+ return rootPkg["bom-ref"];
1838
+ }
1839
+
1840
+ /**
1841
+ * Parse swift dependency tree output
1842
+ * @param {string} rawOutput Swift dependencies json output
1843
+ * @param {string} pkgFile Package.swift file
1844
+ */
1845
+ export function parseSwiftJsonTree(rawOutput, pkgFile) {
1846
+ if (!rawOutput) {
1847
+ return {};
1848
+ }
1849
+ const pkgList = [];
1850
+ const dependenciesList = [];
1851
+ try {
1852
+ const jsonData = JSON.parse(rawOutput);
1853
+ parseSwiftJsonTreeObject(pkgList, dependenciesList, jsonData, pkgFile);
1854
+ } catch (e) {
1855
+ if (DEBUG_MODE) {
1856
+ console.log(e);
1857
+ }
1858
+ return {};
1859
+ }
1860
+ const rootList = pkgList.filter((p) => p.type === "application");
1861
+ return {
1862
+ rootList,
1863
+ pkgList,
1864
+ dependenciesList,
1865
+ };
1866
+ }
1867
+
1868
+ /**
1869
+ * Parse swift package resolved file
1870
+ * @param {string} resolvedFile Package.resolved file
1871
+ */
1872
+ export function parseSwiftResolved(resolvedFile) {
1873
+ const pkgList = [];
1874
+ if (safeExistsSync(resolvedFile)) {
1875
+ try {
1876
+ const pkgData = JSON.parse(
1877
+ readFileSync(resolvedFile, { encoding: "utf-8" }),
1878
+ );
1879
+ let resolvedList = [];
1880
+ if (pkgData.pins) {
1881
+ resolvedList = pkgData.pins;
1882
+ } else if (pkgData.object?.pins) {
1883
+ resolvedList = pkgData.object.pins;
1884
+ }
1885
+ for (const adep of resolvedList) {
1886
+ const locationOrUrl = adep.location || adep.repositoryURL;
1887
+ const version = adep.state.version || adep.state.revision;
1888
+ const purl = purlFromUrlString("swift", locationOrUrl, version);
1889
+ const purlString = decodeURIComponent(purl.toString());
1890
+ const rootPkg = {
1891
+ name: purl.name,
1892
+ group: purl.namespace,
1893
+ version: purl.version,
1894
+ purl: purlString,
1895
+ "bom-ref": purlString,
1896
+ properties: [
1897
+ {
1898
+ name: "internal:SrcFile",
1899
+ value: resolvedFile,
1900
+ },
1901
+ ],
1902
+ evidence: {
1903
+ identity: {
1904
+ field: "purl",
1905
+ confidence: 1,
1906
+ methods: [
1907
+ {
1908
+ technique: "manifest-analysis",
1909
+ confidence: 1,
1910
+ value: resolvedFile,
1911
+ },
1912
+ ],
1913
+ },
1914
+ },
1915
+ };
1916
+ if (locationOrUrl) {
1917
+ rootPkg.repository = { url: locationOrUrl };
1918
+ }
1919
+ pkgList.push(rootPkg);
1920
+ }
1921
+ } catch (_err) {
1922
+ // continue regardless of error
1923
+ }
1924
+ }
1925
+ return pkgList;
1926
+ }
1927
+
1928
+ /**
1929
+ * Parse a CMake-generated dot/graphviz file and extract components and their dependency
1930
+ * relationships.
1931
+ *
1932
+ * The first `digraph` entry becomes the parent component. Subsequent `node` entries
1933
+ * with a `label` attribute are treated as direct dependencies, while commented
1934
+ * `node -> node` relationships are used to construct the dependency graph.
1935
+ *
1936
+ * @param {string} dotFile Path to the CMake-generated dot file
1937
+ * @param {string} pkgType PackageURL type to assign to extracted packages (e.g. `"generic"`)
1938
+ * @param {Object} options CLI options; may contain `projectGroup`, `projectName`, and `projectVersion`
1939
+ * @returns {{ parentComponent: Object, pkgList: Object[], dependenciesList: Object[] }}
1940
+ */
1941
+ export function parseCmakeDotFile(dotFile, pkgType, options = {}) {
1942
+ const dotGraphData = readFileSync(dotFile, { encoding: "utf-8" });
1943
+ const pkgList = [];
1944
+ const dependenciesMap = {};
1945
+ const pkgBomRefMap = {};
1946
+ let parentComponent = {};
1947
+ dotGraphData.split("\n").forEach((l) => {
1948
+ l = l.replaceAll("\r", "").trim();
1949
+ if (l === "\n" || l.startsWith("#")) {
1950
+ return;
1951
+ }
1952
+ let name = "";
1953
+ const group = "";
1954
+ const version = "";
1955
+ let path;
1956
+ if (l.startsWith("digraph")) {
1957
+ const tmpA = l.split(" ");
1958
+ if (tmpA && tmpA.length > 1) {
1959
+ name = tmpA[1].replace(/"/g, "");
1960
+ }
1961
+ } else if (l.startsWith('"node')) {
1962
+ // Direct dependencies are represented as nodes
1963
+ if (l.includes("label =")) {
1964
+ const tmpA = l.split('label = "');
1965
+ if (tmpA && tmpA.length > 1) {
1966
+ name = tmpA[1].split('"')[0];
1967
+ }
1968
+ if (name.includes("\\n")) {
1969
+ name = name.split("\\n")[0];
1970
+ } else if (name.includes(_sep)) {
1971
+ path = name;
1972
+ name = basename(name);
1973
+ }
1974
+ } else if (l.includes("// ")) {
1975
+ // Indirect dependencies are represented with comments
1976
+ const tmpA = l.split("// ");
1977
+ if (tmpA?.length) {
1978
+ const relationship = tmpA[1];
1979
+ if (relationship.includes("->")) {
1980
+ const tmpB = relationship.split(" -> ");
1981
+ if (tmpB && tmpB.length === 2) {
1982
+ if (tmpB[0].includes(_sep)) {
1983
+ tmpB[0] = basename(tmpB[0]);
1984
+ }
1985
+ if (tmpB[1].includes(_sep)) {
1986
+ tmpB[1] = basename(tmpB[1]);
1987
+ }
1988
+ const ref = pkgBomRefMap[tmpB[0]];
1989
+ const depends = pkgBomRefMap[tmpB[1]];
1990
+ if (ref && depends) {
1991
+ if (!dependenciesMap[ref]) {
1992
+ dependenciesMap[ref] = new Set();
1993
+ }
1994
+ dependenciesMap[ref].add(depends);
1995
+ }
1996
+ }
1997
+ }
1998
+ }
1999
+ }
2000
+ }
2001
+ if (!Object.keys(parentComponent).length) {
2002
+ parentComponent = {
2003
+ group: options.projectGroup || "",
2004
+ name: "project-name" in options ? options.projectName : name,
2005
+ version: options.projectVersion || "",
2006
+ type: "application",
2007
+ };
2008
+ parentComponent["purl"] = build({
2009
+ type: pkgType,
2010
+ namespace: parentComponent.group || null,
2011
+ name: parentComponent.name,
2012
+ version: parentComponent.version || null,
2013
+ subpath: path || null,
2014
+ });
2015
+ parentComponent["bom-ref"] = decodeURIComponent(parentComponent["purl"]);
2016
+ } else if (name) {
2017
+ const apkg = {
2018
+ name: name,
2019
+ type: pkgType,
2020
+ purl: build({
2021
+ type: pkgType,
2022
+ namespace: group || null,
2023
+ name: name,
2024
+ version: version || null,
2025
+ subpath: path ? path.replace(/^\/+/, "") : null,
2026
+ }),
2027
+ };
2028
+ apkg["bom-ref"] = decodeURIComponent(apkg["purl"]);
2029
+ pkgList.push(apkg);
2030
+ pkgBomRefMap[name] = apkg["bom-ref"];
2031
+ }
2032
+ });
2033
+ const dependenciesList = [];
2034
+ for (const pk of Object.keys(dependenciesMap)) {
2035
+ const dependsOn = Array.from(dependenciesMap[pk] || []).sort();
2036
+ dependenciesList.push({
2037
+ ref: pk,
2038
+ dependsOn,
2039
+ });
2040
+ }
2041
+ return {
2042
+ parentComponent,
2043
+ pkgList,
2044
+ dependenciesList,
2045
+ };
2046
+ }
2047
+
2048
+ /**
2049
+ * Parse a CMake-like build file (CMakeLists.txt, meson.build, etc.) and extract the
2050
+ * parent component and list of dependency packages.
2051
+ *
2052
+ * Handles `set`, `project`, `find_package`, `find_library`, `find_dependency`,
2053
+ * `find_file`, `FetchContent_MakeAvailable`, and `dependency()` directives.
2054
+ * Uses the MesonWrapDB to improve name resolution confidence.
2055
+ *
2056
+ * @param {string} cmakeListFile Path to the CMake-like build file
2057
+ * @param {string} pkgType PackageURL type to assign to extracted packages (e.g. `"generic"`)
2058
+ * @param {Object} options CLI options; may contain `projectGroup`, `projectName`, and `projectVersion`
2059
+ * @returns {{ parentComponent: Object, pkgList: Object[] }}
2060
+ */
2061
+ export function parseCmakeLikeFile(cmakeListFile, pkgType, options = {}) {
2062
+ let cmakeListData = readFileSync(cmakeListFile, { encoding: "utf-8" });
2063
+ const pkgList = [];
2064
+ const pkgAddedMap = {};
2065
+ const versionSpecifiersMap = {};
2066
+ const versionsMap = {};
2067
+ let parentComponent = {};
2068
+ const templateValues = {};
2069
+ cmakeListData = cmakeListData
2070
+ .replace(/^ {2}/g, "")
2071
+ .replace(/\(\r\n/g, "(")
2072
+ .replace(/\(\n/g, "(")
2073
+ .replace(/,\r\n/g, ",")
2074
+ .replace(/,\n/g, ",");
2075
+ cmakeListData.split("\n").forEach((l) => {
2076
+ l = l.replaceAll("\r", "").trim();
2077
+ if (l === "\n" || l.startsWith("#")) {
2078
+ return;
2079
+ }
2080
+ const group = "";
2081
+ const path = undefined;
2082
+ const name_list = [];
2083
+ if (l.startsWith("set")) {
2084
+ const tmpA = l.replace("set(", "").replace(")", "").trim().split(" ");
2085
+ if (tmpA && tmpA.length === 2) {
2086
+ templateValues[tmpA[0]] = tmpA[1];
2087
+ }
2088
+ } else if (
2089
+ l.startsWith("project") &&
2090
+ !Object.keys(parentComponent).length
2091
+ ) {
2092
+ if (l.includes("${")) {
2093
+ for (const tmplKey of Object.keys(templateValues)) {
2094
+ l = l.replace(`\${${tmplKey}}`, templateValues[tmplKey] || "");
2095
+ }
2096
+ }
2097
+ const tmpA = l.replace("project (", "project(").split("project(");
2098
+ if (tmpA && tmpA.length > 1) {
2099
+ const tmpB = (tmpA[1] || "")
2100
+ .trim()
2101
+ .replace(/["']/g, "")
2102
+ .replace(/ /g, ",")
2103
+ .split(")")[0]
2104
+ .split(",")
2105
+ .filter((v) => v.length > 1);
2106
+ const parentName =
2107
+ tmpB.length > 0 ? tmpB[0].replace(":", "").trim() : "";
2108
+ let parentVersion;
2109
+ // In case of meson.build we can find the version number after the word version
2110
+ // thanks to our replaces and splits
2111
+ const versionIndex = tmpB.findIndex(
2112
+ (v) => v?.toLowerCase() === "version",
2113
+ );
2114
+ if (versionIndex > -1 && tmpB.length > versionIndex) {
2115
+ parentVersion = tmpB[versionIndex + 1];
2116
+ }
2117
+ // A `${VAR}` that no `set()` in this file defines stays literal. It is
2118
+ // not a version, and percent-encoding it into a purl publishes the
2119
+ // variable name as the version, so the field is left empty instead.
2120
+ // CMakeCache.txt carries the evaluated value when a build tree exists.
2121
+ if (parentVersion?.includes("$")) {
2122
+ parentVersion = undefined;
2123
+ }
2124
+ if (parentName?.length && !parentName.includes("$")) {
2125
+ parentComponent = {
2126
+ group: options.projectGroup || "",
2127
+ name: parentName,
2128
+ version: parentVersion || options.projectVersion || "",
2129
+ type: "application",
2130
+ };
2131
+ const safePath = path ? path.replace(/^\/+/, "") : null;
2132
+ parentComponent["purl"] = build({
2133
+ type: pkgType,
2134
+ namespace: parentComponent.group || null,
2135
+ name: parentComponent.name,
2136
+ version: parentComponent.version || null,
2137
+ subpath: safePath,
2138
+ });
2139
+ parentComponent["bom-ref"] = decodeURIComponent(
2140
+ parentComponent["purl"],
2141
+ );
2142
+ }
2143
+ }
2144
+ } else if (l.startsWith("find_")) {
2145
+ let tmpA = [];
2146
+ for (const fm of [
2147
+ "find_package(",
2148
+ "find_library(",
2149
+ "find_dependency(",
2150
+ "find_file(",
2151
+ "FetchContent_MakeAvailable(",
2152
+ ]) {
2153
+ if (l.startsWith(fm)) {
2154
+ tmpA = l.split(fm);
2155
+ break;
2156
+ }
2157
+ }
2158
+ if (tmpA && tmpA.length > 1) {
2159
+ let tmpB = tmpA[1].split(")")[0].split(" ");
2160
+ tmpB = tmpB.filter(
2161
+ (v) =>
2162
+ ![
2163
+ "REQUIRED",
2164
+ "COMPONENTS",
2165
+ "QUIET",
2166
+ "NAMES",
2167
+ "PATHS",
2168
+ "ENV",
2169
+ "NO_MODULE",
2170
+ "NO_DEFAULT_PATH",
2171
+ ].includes(v) &&
2172
+ !v.includes("$") &&
2173
+ !v.startsWith("CMAKE_") &&
2174
+ v.length,
2175
+ );
2176
+ // find_package(Catch2)
2177
+ // find_package(GTest REQUIRED)
2178
+ // find_package(Boost 1.79 COMPONENTS date_time)
2179
+ // find_library(PTHREADPOOL_LIB pthreadpool REQUIRED)
2180
+ // find_library and find_file name the cache variable that receives the
2181
+ // result in their first argument, so that argument is a destination
2182
+ // rather than a package. Every other command in this list opens with a
2183
+ // package name, including names such as `ZLIB` that read like a
2184
+ // variable.
2185
+ if (l.startsWith("find_library") || l.startsWith("find_file")) {
2186
+ tmpB = tmpB.slice(1);
2187
+ }
2188
+ if (tmpB.length) {
2189
+ const working_name = tmpB[0];
2190
+ name_list.push(working_name);
2191
+ if (l.startsWith("find_package") && tmpB.length > 1) {
2192
+ if (
2193
+ /^\d+/.test(tmpB[1]) &&
2194
+ !tmpB[1].includes("${") &&
2195
+ !tmpB[1].startsWith("@")
2196
+ ) {
2197
+ versionsMap[working_name] = tmpB[1];
2198
+ }
2199
+ } else {
2200
+ for (const n of tmpB) {
2201
+ if (n.match(/^\d/)) {
2202
+ continue;
2203
+ }
2204
+ if (n.includes(_sep)) {
2205
+ if (
2206
+ n.includes(".so") ||
2207
+ n.includes(".a") ||
2208
+ n.includes(".dll")
2209
+ ) {
2210
+ name_list.push(basename(n));
2211
+ }
2212
+ } else {
2213
+ name_list.push(n);
2214
+ }
2215
+ }
2216
+ }
2217
+ }
2218
+ }
2219
+ } else if (l.includes("dependency(")) {
2220
+ if (!l.includes("_dependency") && !l.includes(".dependency")) {
2221
+ const depMatch = l.match(/dependency\(\s*['"]?([^'",)\s]+)['"]?/);
2222
+ const depName = depMatch?.[1]?.trim();
2223
+ if (depName) {
2224
+ name_list.push(depName);
2225
+ const versionMatch = l.match(/version\s*:\s*['"]?([^'",)\s]+)['"]?/);
2226
+ const depVersion = versionMatch?.[1]?.trim();
2227
+ if (depVersion) {
2228
+ if (depVersion.includes(">") || depVersion.includes("<")) {
2229
+ // We have a version specifier
2230
+ versionSpecifiersMap[depName] = depVersion;
2231
+ } else if (
2232
+ /^\d+/.test(depVersion) &&
2233
+ !depVersion.includes("${") &&
2234
+ !depVersion.startsWith("@")
2235
+ ) {
2236
+ // We have a valid version
2237
+ versionsMap[depName] = depVersion;
2238
+ }
2239
+ }
2240
+ }
2241
+ }
2242
+ }
2243
+ for (let n of name_list) {
2244
+ const props = [];
2245
+ let confidence = 0;
2246
+ if (
2247
+ n &&
2248
+ n.length > 1 &&
2249
+ !pkgAddedMap[n] &&
2250
+ !n.startsWith(_sep) &&
2251
+ !n.startsWith("@")
2252
+ ) {
2253
+ n = n.replace(/"/g, "");
2254
+ const scrapedName = n;
2255
+ // Can this be replaced with a db lookup?
2256
+ for (const wrapkey of Object.keys(mesonWrapDB)) {
2257
+ const awrap = mesonWrapDB[wrapkey];
2258
+ if (
2259
+ awrap.PkgProvides.includes(n) ||
2260
+ awrap.PkgProvides.includes(n.toLowerCase())
2261
+ ) {
2262
+ // Use the new name
2263
+ n = wrapkey;
2264
+ for (const eprop of Object.keys(awrap)) {
2265
+ props.push({
2266
+ name: eprop,
2267
+ value: Array.isArray(awrap[eprop])
2268
+ ? awrap[eprop].join("|")
2269
+ : awrap[eprop],
2270
+ });
2271
+ }
2272
+ // Our confidence has improved from 0 since there is a matching wrap so we know the correct name
2273
+ // and url. We lack the version details.
2274
+ confidence = 0.5;
2275
+ break;
2276
+ }
2277
+ }
2278
+ // A wrap match renames the package, so the same directive reached under
2279
+ // its scraped name and under its wrap name describe one component. Both
2280
+ // spellings are remembered to keep the second arrival out of the list.
2281
+ if (pkgAddedMap[n]) {
2282
+ pkgAddedMap[scrapedName] = true;
2283
+ continue;
2284
+ }
2285
+ // The version maps are keyed by the name the directive spelled, which a
2286
+ // wrap match has since replaced, so the scraped name is the fallback.
2287
+ const versionSpecifier =
2288
+ versionSpecifiersMap[n] || versionSpecifiersMap[scrapedName];
2289
+ const version = versionsMap[n] || versionsMap[scrapedName] || "";
2290
+ if (versionSpecifier) {
2291
+ props.push({
2292
+ name: "cdx:build:versionSpecifiers",
2293
+ value: versionSpecifier,
2294
+ });
2295
+ }
2296
+ const apkg = {
2297
+ name: n,
2298
+ version,
2299
+ type: pkgType,
2300
+ purl: build({
2301
+ type: pkgType,
2302
+ namespace: group || null,
2303
+ name: n,
2304
+ version: version || null,
2305
+ subpath: path || null,
2306
+ }),
2307
+ evidence: {
2308
+ identity: {
2309
+ field: "purl",
2310
+ confidence,
2311
+ methods: [
2312
+ {
2313
+ technique: "source-code-analysis",
2314
+ confidence: 0.5,
2315
+ value: `Filename ${cmakeListFile}`,
2316
+ },
2317
+ ],
2318
+ },
2319
+ },
2320
+ properties: props,
2321
+ };
2322
+ apkg["bom-ref"] = decodeURIComponent(apkg["purl"]);
2323
+ pkgList.push(apkg);
2324
+ pkgAddedMap[n] = true;
2325
+ pkgAddedMap[scrapedName] = true;
2326
+ }
2327
+ }
2328
+ });
2329
+ return {
2330
+ parentComponent,
2331
+ pkgList,
2332
+ };
2333
+ }
2334
+
2335
+ /**
2336
+ * Parse an atom C/C++ usage-slice report into per-file package usage data.
2337
+ *
2338
+ * Walks the `objectSlices` of an atom report, collecting resolved method names
2339
+ * and full names per source file while skipping include globals and compiler
2340
+ * internals.
2341
+ *
2342
+ * @param {object} sliceData Parsed atom usage-slice report.
2343
+ * @returns {Object<string, Set<string>>|undefined} Map of file name to the set
2344
+ * of used symbols, or undefined when sliceData is empty.
2345
+ */
2346
+ export function parseCUsageSlice(sliceData) {
2347
+ if (!sliceData) {
2348
+ return undefined;
2349
+ }
2350
+ const usageData = {};
2351
+ try {
2352
+ const objectSlices = sliceData.objectSlices || [];
2353
+ for (const slice of objectSlices) {
2354
+ if (
2355
+ (!slice.fileName && !slice.code.startsWith("#include")) ||
2356
+ slice.fileName.startsWith("<includes") ||
2357
+ slice.fileName.startsWith("<global") ||
2358
+ slice.fileName.includes("__")
2359
+ ) {
2360
+ continue;
2361
+ }
2362
+ const slFileName = slice.fileName;
2363
+ const allLines = usageData[slFileName] || new Set();
2364
+ if (slice.fullName && slice.fullName.length > 3) {
2365
+ if (slice.code?.startsWith("#include")) {
2366
+ usageData[slice.fullName] = new Set();
2367
+ } else {
2368
+ allLines.add(slice.fullName);
2369
+ }
2370
+ }
2371
+ for (const ausage of slice.usages) {
2372
+ let calls = ausage?.invokedCalls || [];
2373
+ calls = calls.concat(ausage?.argToCalls || []);
2374
+ for (const acall of calls) {
2375
+ if (!acall.resolvedMethod.includes("->")) {
2376
+ allLines.add(acall.resolvedMethod);
2377
+ }
2378
+ }
2379
+ }
2380
+ if (Array.from(allLines).length) {
2381
+ usageData[slFileName] = allLines;
2382
+ }
2383
+ }
2384
+ } catch (_err) {
2385
+ // ignore
2386
+ }
2387
+ return usageData;
2388
+ }
2389
+
2390
+ /**
2391
+ * Function to parse the .d make files
2392
+ *
2393
+ * @param {String} dfile .d file path
2394
+ *
2395
+ * @returns {Object} pkgFilesMap Object with package name and list of files
2396
+ */
2397
+ export function parseMakeDFile(dfile) {
2398
+ const pkgFilesMap = {};
2399
+ const dData = readFileSync(dfile, { encoding: "utf-8" });
2400
+ const pkgName = basename(dfile).split("-").shift();
2401
+ const filesList = new Set();
2402
+ dData.split("\n").forEach((l) => {
2403
+ l = l.replaceAll("\r", "");
2404
+ if (!l.endsWith(".rs:")) {
2405
+ return;
2406
+ }
2407
+ const fileName = `.cargo/${l.split(".cargo/").pop()}`.replace(
2408
+ ".rs:",
2409
+ ".rs",
2410
+ );
2411
+ filesList.add(fileName);
2412
+ });
2413
+ pkgFilesMap[pkgName] = Array.from(filesList);
2414
+ return pkgFilesMap;
2415
+ }
2416
+
2417
+ /**
2418
+ * Parse the contents of a 'Podfile.lock'
2419
+ *
2420
+ * @param {Object} podfileLock The content of the podfile.lock as an Object
2421
+ * @param {String} projectPath The path to the project root
2422
+ * @returns {Map} Map of all dependencies with their direct dependencies
2423
+ */
2424
+ export async function parsePodfileLock(podfileLock, projectPath) {
2425
+ const dependencies = new Map();
2426
+ for (const pod of podfileLock["PODS"]) {
2427
+ const dependency = {};
2428
+ if (pod.constructor === Object) {
2429
+ for (const key in pod) {
2430
+ dependency.metadata = parseCocoaDependency(key);
2431
+ const subDependencies = new Set();
2432
+ for (const subPod of pod[key]) {
2433
+ subDependencies.add(parseCocoaDependency(subPod, false));
2434
+ }
2435
+ dependency.dependencies = Array.from(subDependencies);
2436
+ }
2437
+ } else {
2438
+ dependency.metadata = parseCocoaDependency(pod);
2439
+ }
2440
+ const podName = dependency.metadata.name.includes("/")
2441
+ ? dependency.metadata.name.substring(
2442
+ 0,
2443
+ dependency.metadata.name.indexOf("/"),
2444
+ )
2445
+ : dependency.metadata.name;
2446
+ if (podfileLock["EXTERNAL SOURCES"]?.[podName]) {
2447
+ const externalPod = podfileLock["EXTERNAL SOURCES"][podName];
2448
+ if (externalPod[":git"]) {
2449
+ let projectRepo = externalPod[":git"];
2450
+ if (isGitHubUrl(projectRepo)) {
2451
+ projectRepo = projectRepo.replace(
2452
+ "github.com",
2453
+ "raw.githubusercontent.com",
2454
+ );
2455
+ }
2456
+ if (projectRepo.endsWith(".git")) {
2457
+ projectRepo = projectRepo.substring(0, projectRepo.length - 4);
2458
+ }
2459
+ const projectRepoBranchOrTag = externalPod[":tag"]
2460
+ ? `tags/${externalPod[":tag"]}`
2461
+ : `heads/${externalPod[":branch"] ? externalPod[":branch"] : "<DEFAULT>"}`;
2462
+ dependency.metadata.properties = [
2463
+ {
2464
+ name: "cdx:pods:podspecLocation",
2465
+ value: `${projectRepo}/refs/${projectRepoBranchOrTag}/${podName}.podspec`,
2466
+ },
2467
+ ];
2468
+ } else if (externalPod[":path"]) {
2469
+ const projectLocation = resolve(projectPath, externalPod[":path"]);
2470
+ dependency.metadata.properties = [
2471
+ {
2472
+ name: "cdx:pods:projectDir",
2473
+ value: projectLocation,
2474
+ },
2475
+ ];
2476
+ let podspec = join(projectLocation, `${podName}.podspec`);
2477
+ if (!safeExistsSync(podspec)) {
2478
+ podspec = `${podspec}.json`;
2479
+ }
2480
+ if (safeExistsSync(podspec)) {
2481
+ dependency.metadata.properties.push({
2482
+ name: "cdx:pods:podspecLocation",
2483
+ value: podspec,
2484
+ });
2485
+ }
2486
+ } else if (externalPod[":podspec"]) {
2487
+ const podspecLocation = resolve(projectPath, externalPod[":podspec"]);
2488
+ dependency.metadata.properties = [
2489
+ {
2490
+ name: "cdx:pods:projectDir",
2491
+ value: dirname(podspecLocation),
2492
+ },
2493
+ {
2494
+ name: "cdx:pods:podspecLocation",
2495
+ value: podspecLocation,
2496
+ },
2497
+ ];
2498
+ }
2499
+ }
2500
+ dependencies.set(dependency.metadata.name, dependency);
2501
+ }
2502
+ if (
2503
+ !["false", "0"].includes(readEnvironmentVariable("COCOA_MERGE_SUBSPECS"))
2504
+ ) {
2505
+ for (const subspecComponentName of [...dependencies.keys()].filter((name) =>
2506
+ name.includes("/"),
2507
+ )) {
2508
+ const subspecComponent = dependencies.get(subspecComponentName);
2509
+ const mainComponentName = subspecComponentName.split("/")[0];
2510
+ let mainComponent = dependencies.get(mainComponentName);
2511
+ if (!mainComponent) {
2512
+ mainComponent = {
2513
+ metadata: {
2514
+ name: mainComponentName,
2515
+ version: subspecComponent.metadata.version,
2516
+ },
2517
+ };
2518
+ dependencies.set(mainComponentName, mainComponent);
2519
+ }
2520
+ if (subspecComponent.dependencies) {
2521
+ if (mainComponent.dependencies) {
2522
+ mainComponent.dependencies = [
2523
+ ...mainComponent.dependencies,
2524
+ ...subspecComponent.dependencies,
2525
+ ];
2526
+ } else {
2527
+ mainComponent.dependencies = subspecComponent.dependencies;
2528
+ }
2529
+ }
2530
+ mainComponent.metadata.properties = [
2531
+ ...(mainComponent.metadata.properties
2532
+ ? mainComponent.metadata.properties
2533
+ : []),
2534
+ {
2535
+ name: "cdx:pods:Subspec",
2536
+ value: subspecComponentName.substring(
2537
+ subspecComponentName.indexOf("/") + 1,
2538
+ ),
2539
+ },
2540
+ ...(subspecComponent.metadata.propertie
2541
+ ? subspecComponent.metadata.properties
2542
+ : []),
2543
+ ];
2544
+ dependencies.delete(subspecComponentName);
2545
+ }
2546
+ for (const [dependencyName, dependency] of dependencies) {
2547
+ if (dependency.dependencies) {
2548
+ dependency.dependencies.forEach((dep) => {
2549
+ dep.name = dep.name.split("/")[0];
2550
+ });
2551
+ dependency.dependencies = [
2552
+ ...new Map(
2553
+ dependency.dependencies
2554
+ .filter((dep) => dep.name !== dependencyName)
2555
+ .map((dep) => [dep.name, dep]),
2556
+ ).values(),
2557
+ ];
2558
+ if (dependency.dependencies.length === 0) {
2559
+ delete dependency.dependencies;
2560
+ }
2561
+ }
2562
+ }
2563
+ }
2564
+ return dependencies;
2565
+ }
2566
+
2567
+ /**
2568
+ * Parse all targets and their direct dependencies from the 'Podfile'
2569
+ *
2570
+ * @param {Object} target A JSON-object representing a target
2571
+ * @param {Map} allDependencies The map containing all parsed direct dependencies for a target
2572
+ * @param {String} [prefix=undefined] Prefix to add to the targets name
2573
+ */
2574
+ export function parsePodfileTargets(
2575
+ target,
2576
+ allDependencies,
2577
+ prefix = undefined,
2578
+ ) {
2579
+ const targetName = (prefix ? `${prefix}/` : "") + target.name;
2580
+ const targetDependencies = new Set(
2581
+ prefix && allDependencies.has(prefix)
2582
+ ? allDependencies.get(prefix)
2583
+ : targetName !== "Pods"
2584
+ ? allDependencies.get("Pods")
2585
+ : [],
2586
+ );
2587
+ if (target["dependencies"]) {
2588
+ for (const targetDependency of target["dependencies"]) {
2589
+ if (targetDependency.constructor === Object) {
2590
+ targetDependencies.add(Object.keys(targetDependency)[0]);
2591
+ } else {
2592
+ targetDependencies.add(targetDependency);
2593
+ }
2594
+ }
2595
+ }
2596
+ allDependencies.set(targetName, Array.from(targetDependencies));
2597
+ if (target.children) {
2598
+ const childPrefix = targetName === "Pods" ? undefined : targetName;
2599
+ for (const childTarget of target.children) {
2600
+ parsePodfileTargets(childTarget, allDependencies, childPrefix);
2601
+ }
2602
+ }
2603
+ }
2604
+
2605
+ /**
2606
+ * Parse a single line representing a dependency
2607
+ *
2608
+ * @param {String} dependencyLine The line that should be parsed as a dependency
2609
+ * @param {boolean} [parseVersion=true] Include parsing the version of the dependency
2610
+ * @returns {Object} Object representing a dependency
2611
+ */
2612
+ export function parseCocoaDependency(dependencyLine, parseVersion = true) {
2613
+ const dependencyData = dependencyLine.split(" (");
2614
+ const dependency = { name: dependencyData[0] };
2615
+ if (parseVersion) {
2616
+ dependency.version = dependencyData[1].substring(
2617
+ 0,
2618
+ dependencyData[1].length - 1,
2619
+ );
2620
+ }
2621
+ return dependency;
2622
+ }
2623
+
2624
+ /**
2625
+ * Execute the 'pod'-command with parameters
2626
+ *
2627
+ * @param {String[]} parameters The parameters for the command
2628
+ * @param {String} path The path where the command should be executed
2629
+ * @param {Object} options CLI options
2630
+ * @returns {Object} The result of running the command
2631
+ */
2632
+ export function executePodCommand(parameters, path, options) {
2633
+ if (DEBUG_MODE) {
2634
+ if (path) {
2635
+ console.log("Executing pod", parameters.join(" "), "in", path);
2636
+ } else {
2637
+ console.log("Executing pod", parameters.join(" "));
2638
+ }
2639
+ }
2640
+ const result = safeSpawnSync(
2641
+ readEnvironmentVariable("POD_CMD") || "pod",
2642
+ parameters,
2643
+ {
2644
+ cwd: path,
2645
+ shell: isWin,
2646
+ },
2647
+ );
2648
+ if (result.status !== 0 || result.error) {
2649
+ if (result?.stderr?.includes("Unable to find a pod")) {
2650
+ console.log(
2651
+ "Try again by running 'pod install' before invoking 'cdxgen'.",
2652
+ );
2653
+ }
2654
+ if (readEnvironmentVariable("CDXGEN_IN_CONTAINER") !== "true") {
2655
+ console.log(
2656
+ "Consider using the cdxgen container image (`ghcr.io/cdxgen/cdxgen`), which includes cocoapods and additional build tools.",
2657
+ );
2658
+ } else if (!DEBUG_MODE) {
2659
+ console.log(
2660
+ "Something went wrong when trying to execute cocoapods -- Set the environment variable 'CDXGEN_DEBUG_MODE=debug' to troubleshoot cocoapods related errors",
2661
+ );
2662
+ }
2663
+ if (options.failOnError || DEBUG_MODE) {
2664
+ if (result.stdout) {
2665
+ console.log(result.stdout);
2666
+ }
2667
+ if (result.stderr) {
2668
+ console.log(result.stderr);
2669
+ }
2670
+ options.failOnError && process.exit(1);
2671
+ }
2672
+ }
2673
+ return result;
2674
+ }
2675
+
2676
+ /**
2677
+ * Method that handles object creation for cocoa pods.
2678
+ *
2679
+ * @param {Object} dependency The dependency that is to be transformed into an SBOM object
2680
+ * @param {Object} options CLI options
2681
+ * @param {String} [type="library"] The type of Object to create
2682
+ * @returns {Object} An object representing the pod in SBOM-format
2683
+ */
2684
+ export async function buildObjectForCocoaPod(
2685
+ dependency,
2686
+ options,
2687
+ type = "library",
2688
+ ) {
2689
+ let component;
2690
+ if (
2691
+ !["false", "0"].includes(
2692
+ readEnvironmentVariable("COCOA_RESOLVE_FROM_NODE"),
2693
+ ) &&
2694
+ dependency.properties?.find(({ name }) => name === "cdx:pods:projectDir")
2695
+ ) {
2696
+ let tmpDir = dependency.properties.find(
2697
+ ({ name }) => name === "cdx:pods:projectDir",
2698
+ ).value;
2699
+ const exclusionDirs = readEnvironmentVariable(
2700
+ "COCOA_RESOLVE_FROM_NODE_EXCLUSION_DIRS",
2701
+ )
2702
+ ? readEnvironmentVariable("COCOA_RESOLVE_FROM_NODE_EXCLUSION_DIRS").split(
2703
+ ",",
2704
+ )
2705
+ : [];
2706
+ if (
2707
+ tmpDir &&
2708
+ !exclusionDirs.some((dir) =>
2709
+ `${tmpDir.replaceAll("\\", "/")}/`.includes(
2710
+ `/${dir.replaceAll("\\", "/")}/`.replaceAll("//", "/"),
2711
+ ),
2712
+ ) &&
2713
+ tmpDir.indexOf("node_modules") !== -1
2714
+ ) {
2715
+ do {
2716
+ const npmPackages = await parsePkgJson(join(tmpDir, "package.json"));
2717
+ if (npmPackages.length === 1) {
2718
+ component = npmPackages[0];
2719
+ component.type = "library";
2720
+ component.properties = component.properties.concat(
2721
+ {
2722
+ name: "cdx:pods:PodName",
2723
+ value: dependency.name,
2724
+ },
2725
+ dependency.properties,
2726
+ );
2727
+ tmpDir = undefined;
2728
+ } else {
2729
+ tmpDir = dirname(tmpDir);
2730
+ }
2731
+ } while (tmpDir && tmpDir.indexOf("node_modules") !== -1);
2732
+ }
2733
+ }
2734
+ if (!component) {
2735
+ let name = dependency.name;
2736
+ let subspec = null;
2737
+ const locationOfSubspec = dependency.name.indexOf("/");
2738
+ if (locationOfSubspec !== -1) {
2739
+ name = dependency.name.substring(0, locationOfSubspec);
2740
+ subspec = dependency.name.substring(locationOfSubspec + 1);
2741
+ }
2742
+ component = {
2743
+ ...dependency,
2744
+ type,
2745
+ };
2746
+ if (subspec) {
2747
+ if (!component.properties) {
2748
+ component.properties = [];
2749
+ }
2750
+ component.properties.push({
2751
+ name: "cdx:pods:Subspec",
2752
+ value: subspec,
2753
+ });
2754
+ }
2755
+ const purl = build({
2756
+ type: "cocoapods",
2757
+ namespace: "" || null,
2758
+ name: name,
2759
+ version: component.version || null,
2760
+ subpath: subspec || null,
2761
+ });
2762
+ component["purl"] = purl;
2763
+ component["bom-ref"] = decodeURIComponent(purl);
2764
+ if (
2765
+ options &&
2766
+ !["false", "0"].includes(readEnvironmentVariable("COCOA_FULL_SCAN"))
2767
+ ) {
2768
+ await fullScanCocoaPod(dependency, component, options);
2769
+ }
2770
+ }
2771
+ return component;
2772
+ }
2773
+
2774
+ /**
2775
+ * The podspec URLs to try, in the order `fullScanCocoaPod` tries them.
2776
+ *
2777
+ * A location carrying `<DEFAULT>` stands for "the repository's default branch",
2778
+ * which the lockfile does not name, so both conventional names are probed and
2779
+ * each is probed twice: CocoaPods publishes some podspecs as `.podspec` and
2780
+ * some as `.podspec.json` at the same path.
2781
+ *
2782
+ * @param {String} podspecLocation Location with an unresolved `<DEFAULT>`.
2783
+ * @returns {Array<String>} Candidate URLs, most likely first.
2784
+ */
2785
+ function podspecCandidates(podspecLocation) {
2786
+ const candidates = [];
2787
+ for (const branchName of ["main", "master"]) {
2788
+ const resolved = podspecLocation.replace("<DEFAULT>", branchName);
2789
+ candidates.push(resolved, `${resolved}.json`);
2790
+ }
2791
+ return candidates;
2792
+ }
2793
+
2794
+ /**
2795
+ * Podspec documents, prefetched by {@link prefetchCocoaPodspecs}.
2796
+ *
2797
+ * Module state for the same reason as the other prefetch maps: the reader is
2798
+ * several call frames below the point where the whole pod list is in hand. A
2799
+ * miss means the scan issues its own request.
2800
+ */
2801
+ let cocoaPodspecPrefetch = new Map();
2802
+
2803
+ /**
2804
+ * Discard prefetched podspecs. Tests only.
2805
+ */
2806
+ export function resetCocoaPodspecPrefetch() {
2807
+ cocoaPodspecPrefetch = new Map();
2808
+ }
2809
+
2810
+ /**
2811
+ * Prefetch the remote podspecs a full CocoaPods scan is about to read.
2812
+ *
2813
+ * The probe is speculative — up to four URLs per pod — so batching all of them
2814
+ * at once would issue requests the serial path never makes: a pod whose podspec
2815
+ * sits on `main` is one request there and would be four here. Instead each
2816
+ * candidate position is its own round, and a round carries only the pods that
2817
+ * every earlier round missed. That is the same set of requests the serial path
2818
+ * makes, in the same order of preference, with the pods within a round
2819
+ * overlapped rather than queued. Rounds are bounded at four however many pods
2820
+ * there are.
2821
+ *
2822
+ * @param {Array<Object>} dependencies Pod metadata objects, each optionally
2823
+ * carrying a `cdx:pods:podspecLocation` property.
2824
+ * @returns {Promise<void>}
2825
+ */
2826
+ export async function prefetchCocoaPodspecs(dependencies) {
2827
+ if (!prefetchEnabled() || !Array.isArray(dependencies)) {
2828
+ return;
2829
+ }
2830
+ if (["false", "0"].includes(readEnvironmentVariable("COCOA_FULL_SCAN"))) {
2831
+ return;
2832
+ }
2833
+ let pending = [];
2834
+ for (const dependency of dependencies || []) {
2835
+ const location = dependency?.properties?.find(
2836
+ ({ name }) => name === "cdx:pods:podspecLocation",
2837
+ )?.value;
2838
+ if (location?.startsWith("http")) {
2839
+ pending.push(podspecCandidates(location));
2840
+ }
2841
+ }
2842
+ if (!pending.length) {
2843
+ return;
2844
+ }
2845
+ const rounds = Math.max(...pending.map((candidates) => candidates.length));
2846
+ for (let position = 0; position < rounds && pending.length; position++) {
2847
+ const requests = [];
2848
+ const seen = new Set();
2849
+ for (const candidates of pending) {
2850
+ const url = candidates[position];
2851
+ if (!url || seen.has(url) || cocoaPodspecPrefetch.has(url)) {
2852
+ continue;
2853
+ }
2854
+ seen.add(url);
2855
+ requests.push({ url, responseType: "text" });
2856
+ }
2857
+ if (requests.length) {
2858
+ for (const [key, value] of await prefetchJson(requests)) {
2859
+ cocoaPodspecPrefetch.set(key, value);
2860
+ }
2861
+ }
2862
+ // A pod whose candidate resolved is done; the rest fall through to the next
2863
+ // position, exactly as the serial probe would.
2864
+ pending = pending.filter(
2865
+ (candidates) => !cocoaPodspecPrefetch.get(candidates[position])?.ok,
2866
+ );
2867
+ }
2868
+ }
2869
+
2870
+ async function fullScanCocoaPod(dependency, component, options) {
2871
+ let result;
2872
+ if (
2873
+ component.properties?.find(
2874
+ ({ name }) => name === "cdx:pods:podspecLocation",
2875
+ )
2876
+ ) {
2877
+ let podspecLocation = component.properties.find(
2878
+ ({ name }) => name === "cdx:pods:podspecLocation",
2879
+ ).value;
2880
+ if (
2881
+ component.properties.find(({ name }) => name === "cdx:pods:projectDir")
2882
+ ) {
2883
+ component.properties.push({
2884
+ name: "internal:SrcFile",
2885
+ value: podspecLocation,
2886
+ });
2887
+ }
2888
+ let replacements = [];
2889
+ if (
2890
+ podspecLocation.endsWith(".podspec") &&
2891
+ readEnvironmentVariable("COCOA_PODSPEC_REPLACEMENTS")
2892
+ ) {
2893
+ replacements = readEnvironmentVariable(
2894
+ "COCOA_PODSPEC_REPLACEMENTS",
2895
+ ).split(";");
2896
+ } else if (
2897
+ podspecLocation.endsWith(".json") &&
2898
+ readEnvironmentVariable("COCOA_PODSPEC_JSON_REPLACEMENTS")
2899
+ ) {
2900
+ replacements = readEnvironmentVariable(
2901
+ "COCOA_PODSPEC_JSON_REPLACEMENTS",
2902
+ ).split(";");
2903
+ }
2904
+ if (replacements || podspecLocation.startsWith("http")) {
2905
+ let podspecContent;
2906
+ if (podspecLocation.startsWith("http")) {
2907
+ let httpResult;
2908
+ for (const candidate of podspecCandidates(podspecLocation)) {
2909
+ try {
2910
+ httpResult =
2911
+ prefetchedResponse(cocoaPodspecPrefetch, candidate) ||
2912
+ (await cdxgenAgent.get(candidate));
2913
+ podspecLocation = candidate;
2914
+ } catch (_err) {
2915
+ continue;
2916
+ }
2917
+ component.properties.find(
2918
+ ({ name }) => name === "cdx:pods:podspecLocation",
2919
+ ).value = podspecLocation;
2920
+ podspecLocation = `${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`;
2921
+ podspecContent = httpResult.body;
2922
+ break;
2923
+ }
2924
+ } else {
2925
+ podspecContent = readFileSync(podspecLocation, "utf-8");
2926
+ }
2927
+ for (const replacement of replacements) {
2928
+ const replacementPair = replacement.split("=");
2929
+ let match = replacementPair[0].replaceAll("<NEWLINE>", "\n");
2930
+ if (match.startsWith("/") && match.endsWith("/")) {
2931
+ match = new RegExp(match.substring(1, match.length - 1), "g");
2932
+ }
2933
+ const repl = replacementPair[1].replaceAll("<NEWLINE>", "\n");
2934
+ podspecContent = podspecContent.replaceAll(match, repl);
2935
+ }
2936
+ podspecLocation = join(
2937
+ dirname(podspecLocation),
2938
+ `${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`,
2939
+ );
2940
+ safeWriteSync(podspecLocation, podspecContent);
2941
+ temporaryFiles.add(podspecLocation);
2942
+ }
2943
+ result = executePodCommand(
2944
+ ["ipc", "spec", "--silent", podspecLocation],
2945
+ undefined,
2946
+ options,
2947
+ );
2948
+ } else {
2949
+ let dependencyName = dependency.name;
2950
+ if (dependencyName.includes("/")) {
2951
+ dependencyName = dependencyName.substring(0, dependencyName.indexOf("/"));
2952
+ }
2953
+ // `pod` may not be installed at all, in which case spawn returns a result
2954
+ // with no stdout. Calling .trim() on that threw and aborted the whole
2955
+ // CocoaPods scan, so any machine without CocoaPods could not generate a
2956
+ // BOM from a Podfile.lock it had already parsed successfully.
2957
+ const srcFileResult = executePodCommand(
2958
+ [
2959
+ "spec",
2960
+ "which",
2961
+ `^${dependencyName}$`,
2962
+ "--regex",
2963
+ `--version=${dependency.version}`,
2964
+ ],
2965
+ undefined,
2966
+ options,
2967
+ );
2968
+ if (srcFileResult?.stdout) {
2969
+ const srcFileProperty = {
2970
+ name: "internal:SrcFile",
2971
+ value: srcFileResult.stdout.trim(),
2972
+ };
2973
+ if (component.properties) {
2974
+ component.properties.push(srcFileProperty);
2975
+ } else {
2976
+ component.properties = [srcFileProperty];
2977
+ }
2978
+ }
2979
+ result = executePodCommand(
2980
+ [
2981
+ "spec",
2982
+ "cat",
2983
+ `^${dependencyName}$`,
2984
+ "--regex",
2985
+ `--version=${dependency.version}`,
2986
+ ],
2987
+ undefined,
2988
+ options,
2989
+ );
2990
+ }
2991
+ // Same reason as above: no `pod` binary means no result to parse.
2992
+ const podspecText = result?.stdout;
2993
+ if (!podspecText) {
2994
+ return;
2995
+ }
2996
+ let podspec;
2997
+ try {
2998
+ podspec = JSON.parse(
2999
+ podspecText.substring(
3000
+ podspecText.indexOf("{"),
3001
+ podspecText.lastIndexOf("}") + 1,
3002
+ ),
3003
+ );
3004
+ } catch (_e) {
3005
+ return;
3006
+ }
3007
+ const externalRefs = [];
3008
+ if (podspec.authors) {
3009
+ component.authors = [];
3010
+ if (podspec.authors.constructor === Object) {
3011
+ Object.entries(podspec.authors).forEach(([name, email]) => {
3012
+ email.includes("@")
3013
+ ? component.authors.push({ name, email })
3014
+ : component.authors.push({ name });
3015
+ });
3016
+ } else if (podspec.authors.constructor === Array) {
3017
+ podspec.authors.forEach((name) => {
3018
+ component.authors.push({ name });
3019
+ });
3020
+ } else {
3021
+ component.authors.push({ name: podspec.authors });
3022
+ }
3023
+ }
3024
+ if (podspec.description) {
3025
+ component.description = podspec.description;
3026
+ } else if (podspec.summary) {
3027
+ component.description = podspec.summary;
3028
+ }
3029
+ if (podspec.documentation_url) {
3030
+ externalRefs.push({
3031
+ type: "documentation",
3032
+ url: podspec.documentation_url,
3033
+ });
3034
+ } else if (podspec.readme) {
3035
+ externalRefs.push({
3036
+ type: "documentation",
3037
+ url: podspec.readme,
3038
+ });
3039
+ }
3040
+ if (podspec.homepage) {
3041
+ externalRefs.push({
3042
+ type: "website",
3043
+ url: podspec.homepage,
3044
+ });
3045
+ }
3046
+ if (podspec.license) {
3047
+ if (podspec.license.constructor === Object) {
3048
+ if (podspec.license.type === "Copyright") {
3049
+ component.copyright = podspec.license.text;
3050
+ } else {
3051
+ component.licenses = [{ license: {} }];
3052
+ if (spdxLicenses.includes(podspec.license.type)) {
3053
+ component.licenses[0].license.id = podspec.license.type;
3054
+ } else {
3055
+ component.licenses[0].license.name = podspec.license.type;
3056
+ }
3057
+ const licenseText = [];
3058
+ if (podspec.license.text) {
3059
+ if (podspec.license.text.startsWith("http")) {
3060
+ component.licenses[0].license.url = podspec.license.text;
3061
+ } else {
3062
+ licenseText.push(podspec.license.text);
3063
+ }
3064
+ }
3065
+ if (podspec.license.file) {
3066
+ if (podspec.license.file.startsWith("http")) {
3067
+ if (component.licenses[0].license.url) {
3068
+ if (licenseText.length !== 0) {
3069
+ licenseText.push("");
3070
+ }
3071
+ licenseText.push(
3072
+ `See also: ${component.licenses[0].license.url}`,
3073
+ );
3074
+ }
3075
+ component.licenses[0].license.url = podspec.license.file;
3076
+ } else {
3077
+ if (licenseText.length !== 0) {
3078
+ licenseText.push("");
3079
+ }
3080
+ licenseText.push(`See license in file '${podspec.license.file}'`);
3081
+ }
3082
+ }
3083
+ if (licenseText.length !== 0) {
3084
+ component.licenses[0].license.text = {
3085
+ content: licenseText.join("\n"),
3086
+ };
3087
+ }
3088
+ }
3089
+ } else {
3090
+ if (spdxLicenses.includes(podspec.license)) {
3091
+ component.licenses = [{ license: { id: podspec.license } }];
3092
+ } else {
3093
+ component.licenses = [{ license: { name: podspec.license } }];
3094
+ }
3095
+ }
3096
+ }
3097
+ if (podspec.social_media_url) {
3098
+ externalRefs.push({
3099
+ type: "social",
3100
+ url: podspec.social_media_url,
3101
+ });
3102
+ }
3103
+ if (podspec.source) {
3104
+ const comment = [];
3105
+ if (podspec.source.http) {
3106
+ const sourceDistro = {
3107
+ type: "source-distribution",
3108
+ url: podspec.source.http,
3109
+ };
3110
+ const hashes = [];
3111
+ if (podspec.source.http.sha1) {
3112
+ hashes.push({
3113
+ alg: "SHA-1",
3114
+ content: podspec.source.http.sha1,
3115
+ });
3116
+ }
3117
+ if (podspec.source.http.sha256) {
3118
+ hashes.push({
3119
+ alg: "SHA-256",
3120
+ content: podspec.source.http.sha256,
3121
+ });
3122
+ }
3123
+ if (hashes.length !== 0) {
3124
+ sourceDistro.hashes = hashes;
3125
+ }
3126
+ if (podspec.source.flatten) {
3127
+ comment.push(`Flatten: ${podspec.source.flatten}`);
3128
+ }
3129
+ if (podspec.source.type) {
3130
+ comment.push(`Type: ${podspec.source.type}`);
3131
+ }
3132
+ if (podspec.source.headers) {
3133
+ comment.push(`Headers: ${podspec.source.headers}`);
3134
+ }
3135
+ if (comment.length !== 0) {
3136
+ sourceDistro.comment = comment.join("\n");
3137
+ }
3138
+ externalRefs.push(sourceDistro);
3139
+ } else {
3140
+ let url;
3141
+ if (podspec.source.git) {
3142
+ url = podspec.source.git;
3143
+ comment.push("Type: git");
3144
+ if (podspec.source.branch) {
3145
+ comment.push(`Branch: ${podspec.source.branch}`);
3146
+ }
3147
+ if (podspec.source.commit) {
3148
+ comment.push(`Commit: ${podspec.source.commit}`);
3149
+ }
3150
+ if (podspec.source.tag) {
3151
+ comment.push(`Tag: ${podspec.source.tag}`);
3152
+ }
3153
+ if (podspec.source.submodules) {
3154
+ comment.push(`Submodules: ${podspec.source.submodules}`);
3155
+ }
3156
+ } else if (podspec.source.hg) {
3157
+ url = podspec.source.hg;
3158
+ comment.push("Type: hg");
3159
+ if (podspec.source.revision) {
3160
+ comment.push(`Revision: ${podspec.source.revision}`);
3161
+ }
3162
+ } else if (podspec.source.svn) {
3163
+ url = podspec.source.svn;
3164
+ comment.push("Type: svn");
3165
+ if (podspec.source.folder) {
3166
+ comment.push(`Folder: ${podspec.source.folder}`);
3167
+ }
3168
+ if (podspec.source.revision) {
3169
+ comment.push(`Revision: ${podspec.source.revision}`);
3170
+ }
3171
+ if (podspec.source.tag) {
3172
+ comment.push(`Tag: ${podspec.source.tag}`);
3173
+ }
3174
+ }
3175
+ if (url) {
3176
+ externalRefs.push({
3177
+ type: "vcs",
3178
+ url: url,
3179
+ comment: comment.join("\n"),
3180
+ });
3181
+ } else {
3182
+ console.warn(
3183
+ `${dependency.name} has property 'source' defined, but it does not contain a URL -- ignoring...`,
3184
+ );
3185
+ }
3186
+ }
3187
+ }
3188
+ if (externalRefs.length !== 0) {
3189
+ component.externalReferences = externalRefs;
3190
+ }
3191
+ }