@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,2145 @@
1
+ import { Buffer } from "node:buffer";
2
+ import {
3
+ createReadStream,
4
+ lstatSync,
5
+ readdirSync,
6
+ readFileSync,
7
+ } from "node:fs";
8
+ import { platform as _platform, userInfo as _userInfo, homedir } from "node:os";
9
+ import { basename, join, resolve, win32 } from "node:path";
10
+ import process from "node:process";
11
+ import stream from "node:stream/promises";
12
+ import { URL } from "node:url";
13
+
14
+ import { x } from "tar";
15
+
16
+ import {
17
+ DEBUG_MODE,
18
+ isDryRun,
19
+ readEnvironmentVariable,
20
+ recordActivity,
21
+ recordDecisionActivity,
22
+ recordSensitiveFileRead,
23
+ } from "../core/activity.js";
24
+ import { extractPathEnv } from "../core/env.js";
25
+ import {
26
+ getAllFiles,
27
+ getTmpDir,
28
+ safeExistsSync,
29
+ safeExtractArchive,
30
+ safeMkdirSync,
31
+ safeMkdtempSync,
32
+ safeRmSync,
33
+ safeSpawnSync,
34
+ safeWriteSync,
35
+ } from "../core/fs.js";
36
+ import { ui } from "../core/ui.js";
37
+ import { getDirs, getOnlyDirs, getOnlyDirsMulti } from "./containerutils.js";
38
+ import { createDaemonConnection } from "./dockerConnection.js";
39
+
40
+ /** True when the current operating system is Windows. */
41
+ export const isWin = _platform() === "win32";
42
+ /**
43
+ * Canonical Docker Hub registry hostname used as the default registry when
44
+ * parsing image names.
45
+ *
46
+ * @type {string}
47
+ */
48
+ export const DOCKER_HUB_REGISTRY = "docker.io";
49
+ // Docker commonly stores Hub credentials under index.docker.io or
50
+ // registry-1.docker.io while pulls target docker.io.
51
+ const DOCKER_HUB_REGISTRY_ALIASES = new Set([
52
+ DOCKER_HUB_REGISTRY,
53
+ "index.docker.io",
54
+ "registry-1.docker.io",
55
+ ]);
56
+
57
+ /**
58
+ * Encode a value as base64url (RFC 4648 §5) with padding.
59
+ * Docker Engine decodes X-Registry-Auth with Go's base64.URLEncoding,
60
+ * which uses the URL-safe alphabet (-_ instead of +/) and expects = padding.
61
+ * Node's "base64url" omits padding, so we convert from standard base64.
62
+ */
63
+ const toBase64Url = (value) =>
64
+ Buffer.from(value).toString("base64").replace(/\+/g, "-").replace(/\//g, "_");
65
+
66
+ const normalizeRegistryPath = (registryPath) => {
67
+ if (!registryPath || registryPath === "/") {
68
+ return "";
69
+ }
70
+ let normalizedPath = registryPath.trim();
71
+ if (!normalizedPath.startsWith("/")) {
72
+ normalizedPath = `/${normalizedPath}`;
73
+ }
74
+ while (normalizedPath.endsWith("/")) {
75
+ normalizedPath = normalizedPath.slice(0, -1);
76
+ }
77
+ const lowerCasePath = normalizedPath.toLowerCase();
78
+ if (lowerCasePath.endsWith("/v1") || lowerCasePath.endsWith("/v2")) {
79
+ normalizedPath = normalizedPath.slice(0, -3);
80
+ }
81
+ return normalizedPath === "/" ? "" : normalizedPath;
82
+ };
83
+
84
+ const buildRegistryAuthority = (hostname, port) => {
85
+ if (!hostname) {
86
+ return undefined;
87
+ }
88
+ hostname = hostname.toLowerCase();
89
+ if (hostname.includes(":") && !hostname.startsWith("[")) {
90
+ hostname = `[${hostname}]`;
91
+ }
92
+ if (port) {
93
+ return `${hostname}:${port}`;
94
+ }
95
+ return hostname;
96
+ };
97
+
98
+ const parseRawRegistryAuthority = (authority) => {
99
+ if (!authority?.trim()) {
100
+ return undefined;
101
+ }
102
+ authority = authority.trim();
103
+ if (authority.startsWith("[")) {
104
+ const closingBracketIndex = authority.indexOf("]");
105
+ if (closingBracketIndex === -1) {
106
+ return undefined;
107
+ }
108
+ const hostname = authority.slice(0, closingBracketIndex + 1);
109
+ const portSuffix = authority.slice(closingBracketIndex + 1);
110
+ if (!portSuffix) {
111
+ return buildRegistryAuthority(hostname);
112
+ }
113
+ if (!/^:\d+$/.test(portSuffix)) {
114
+ return undefined;
115
+ }
116
+ return buildRegistryAuthority(hostname, portSuffix.slice(1));
117
+ }
118
+ const colonIndex = authority.lastIndexOf(":");
119
+ if (colonIndex > -1 && authority.indexOf(":") === colonIndex) {
120
+ const portCandidate = authority.slice(colonIndex + 1);
121
+ if (/^\d+$/.test(portCandidate)) {
122
+ return buildRegistryAuthority(
123
+ authority.slice(0, colonIndex),
124
+ portCandidate,
125
+ );
126
+ }
127
+ }
128
+ return buildRegistryAuthority(authority);
129
+ };
130
+
131
+ const parseRegistryReference = (registry) => {
132
+ if (!registry?.trim()) {
133
+ return undefined;
134
+ }
135
+ registry = registry.trim();
136
+ if (registry.includes("://")) {
137
+ if (!URL.canParse(registry)) {
138
+ return undefined;
139
+ }
140
+ const registryUrl = new URL(registry);
141
+ const authoritySource = registry
142
+ .slice(registry.indexOf("://") + 3)
143
+ .split("/")[0];
144
+ return {
145
+ authority: parseRawRegistryAuthority(authoritySource),
146
+ path: normalizeRegistryPath(registryUrl.pathname),
147
+ };
148
+ }
149
+ const slashIndex = registry.indexOf("/");
150
+ const authority =
151
+ slashIndex === -1 ? registry : registry.slice(0, slashIndex);
152
+ const registryPath = slashIndex === -1 ? "" : registry.slice(slashIndex);
153
+ if (!authority) {
154
+ return undefined;
155
+ }
156
+ try {
157
+ // Raw registry references such as host:port are not absolute URLs, so we
158
+ // add an https scheme only to parse the authority and optional port.
159
+ return {
160
+ authority: parseRawRegistryAuthority(authority),
161
+ path: normalizeRegistryPath(registryPath),
162
+ };
163
+ } catch (_err) {
164
+ return undefined;
165
+ }
166
+ };
167
+
168
+ const looksLikeImageReference = (value) => {
169
+ if (typeof value !== "string" || !value.trim()) {
170
+ return false;
171
+ }
172
+ value = value.trim();
173
+ if (value.includes("://")) {
174
+ return false;
175
+ }
176
+ if (!value.includes("/")) {
177
+ if (value.includes(":")) {
178
+ const tagOrPortSuffix = value.slice(value.lastIndexOf(":") + 1);
179
+ if (!/^\d+$/.test(tagOrPortSuffix)) {
180
+ return true;
181
+ }
182
+ return !parseRegistryReference(value)?.authority;
183
+ }
184
+ return !(
185
+ value.includes(".") ||
186
+ value === "localhost" ||
187
+ value.startsWith("[")
188
+ );
189
+ }
190
+ const firstSegment = value.split("/")[0];
191
+ return !(
192
+ parseRegistryReference(firstSegment)?.authority &&
193
+ (firstSegment.includes(".") ||
194
+ firstSegment.includes(":") ||
195
+ firstSegment === "localhost" ||
196
+ firstSegment.startsWith("["))
197
+ );
198
+ };
199
+
200
+ const resolveRequestedRegistryRef = (forRegistry, requestedRegistryRef) => {
201
+ const fallbackRegistry =
202
+ forRegistry ||
203
+ readEnvironmentVariable("DOCKER_SERVER_ADDRESS") ||
204
+ DOCKER_HUB_REGISTRY;
205
+ if (
206
+ typeof requestedRegistryRef !== "string" ||
207
+ !requestedRegistryRef.trim()
208
+ ) {
209
+ return fallbackRegistry;
210
+ }
211
+ requestedRegistryRef = requestedRegistryRef.trim();
212
+ if (requestedRegistryRef.includes("://")) {
213
+ return requestedRegistryRef;
214
+ }
215
+ return looksLikeImageReference(requestedRegistryRef)
216
+ ? fallbackRegistry
217
+ : requestedRegistryRef;
218
+ };
219
+
220
+ const extractRequestedRegistryRefFromPath = (path, forRegistry) => {
221
+ if (!path?.includes("?")) {
222
+ return resolveRequestedRegistryRef(forRegistry, forRegistry);
223
+ }
224
+ const queryString = path.slice(path.indexOf("?") + 1);
225
+ const requestedImageRef = new URLSearchParams(queryString).get("fromImage");
226
+ return resolveRequestedRegistryRef(
227
+ forRegistry,
228
+ requestedImageRef || forRegistry,
229
+ );
230
+ };
231
+
232
+ const normalizeRegistryReference = (registry) => {
233
+ const parsedRegistry = parseRegistryReference(registry);
234
+ if (!parsedRegistry?.authority) {
235
+ return undefined;
236
+ }
237
+ return parsedRegistry.path
238
+ ? `${parsedRegistry.authority}${parsedRegistry.path}`
239
+ : parsedRegistry.authority;
240
+ };
241
+
242
+ const registriesMatch = (configuredRegistry, requestedRegistry) => {
243
+ if (!requestedRegistry) {
244
+ return false;
245
+ }
246
+ const normalizedConfiguredRegistry =
247
+ parseRegistryReference(configuredRegistry);
248
+ const normalizedRequestedRegistry = parseRegistryReference(requestedRegistry);
249
+ if (
250
+ !normalizedConfiguredRegistry?.authority ||
251
+ !normalizedRequestedRegistry?.authority
252
+ ) {
253
+ return false;
254
+ }
255
+ const hostMatches =
256
+ normalizedConfiguredRegistry.authority ===
257
+ normalizedRequestedRegistry.authority ||
258
+ (DOCKER_HUB_REGISTRY_ALIASES.has(normalizedConfiguredRegistry.authority) &&
259
+ DOCKER_HUB_REGISTRY_ALIASES.has(normalizedRequestedRegistry.authority));
260
+ if (!hostMatches) {
261
+ return false;
262
+ }
263
+ if (!normalizedConfiguredRegistry.path) {
264
+ return true;
265
+ }
266
+ return (
267
+ normalizedConfiguredRegistry.path === normalizedRequestedRegistry.path ||
268
+ normalizedRequestedRegistry.path.startsWith(
269
+ `${normalizedConfiguredRegistry.path}/`,
270
+ )
271
+ );
272
+ };
273
+
274
+ // Should we extract the tar image in non-strict mode
275
+ const NON_STRICT_TAR_EXTRACT = ["true", "1"].includes(
276
+ process?.env?.NON_STRICT_TAR_EXTRACT,
277
+ );
278
+ if (NON_STRICT_TAR_EXTRACT) {
279
+ console.log(
280
+ "Warning: Extracting container images and tar files in non-strict mode could lead to security risks!",
281
+ );
282
+ }
283
+
284
+ let dockerConn;
285
+ let isPodman = false;
286
+ let isPodmanRootless = true;
287
+ let isDockerRootless = false;
288
+ // https://github.com/containerd/containerd
289
+ let isContainerd = !!readEnvironmentVariable("CONTAINERD_ADDRESS");
290
+ const WIN_LOCAL_TLS = "http://localhost:2375";
291
+ let isWinLocalTLS = false;
292
+ let isNerdctl;
293
+ let isColima;
294
+
295
+ if (
296
+ !readEnvironmentVariable("DOCKER_HOST") &&
297
+ (readEnvironmentVariable("CONTAINERD_ADDRESS") ||
298
+ (readEnvironmentVariable("XDG_RUNTIME_DIR") &&
299
+ safeExistsSync(
300
+ join(
301
+ readEnvironmentVariable("XDG_RUNTIME_DIR"),
302
+ "containerd-rootless",
303
+ "api.sock",
304
+ ),
305
+ )))
306
+ ) {
307
+ isContainerd = true;
308
+ }
309
+
310
+ /**
311
+ * Strip absolute path prefixes from a path string, handling both Unix and
312
+ * Windows paths (including UNC and extended-length paths such as //?/C:/).
313
+ * Taken from https://github.com/isaacs/node-tar/blob/main/src/strip-absolute-path.ts
314
+ *
315
+ * @param {string} path The path to strip
316
+ * @returns {string} The path with its absolute root removed
317
+ */
318
+ export const stripAbsolutePath = (path) => {
319
+ // This appears to be a most frequent case, so let's return quickly.
320
+ if (path === "/") {
321
+ return "";
322
+ }
323
+ let parsed = win32.parse(path);
324
+ while (win32.isAbsolute(path) || parsed.root) {
325
+ // windows will think that //x/y/z has a "root" of //x/y/
326
+ // but strip the //?/C:/ off of //?/C:/path
327
+ const root =
328
+ path.charAt(0) === "/" && path.slice(0, 4) !== "//?/" ? "/" : parsed.root;
329
+ path = path.slice(root.length);
330
+ parsed = win32.parse(path);
331
+ }
332
+ return path;
333
+ };
334
+
335
+ /**
336
+ * Detect colima
337
+ */
338
+ export function detectColima() {
339
+ if (isColima) {
340
+ return true;
341
+ }
342
+ if (_platform() === "darwin") {
343
+ const result = safeSpawnSync("colima", ["version"]);
344
+ if (result.status !== 0 || result.error) {
345
+ return false;
346
+ }
347
+ if (result?.stdout?.includes("colima version")) {
348
+ isColima = true;
349
+ console.log(
350
+ "Colima is known to have issues with volume mounts, which might result in incomplete BOM. Use it with caution!",
351
+ );
352
+ if (result?.stdout?.includes("runtime: containerd")) {
353
+ isNerdctl = true;
354
+ }
355
+ }
356
+ }
357
+ return isColima;
358
+ }
359
+
360
+ /**
361
+ * Detect if Rancher desktop is running on a mac.
362
+ */
363
+ export function detectRancherDesktop() {
364
+ // Detect Rancher desktop and nerdctl on a mac
365
+ if (_platform() === "darwin") {
366
+ const limaHome = join(
367
+ homedir(),
368
+ "Library",
369
+ "Application Support",
370
+ "rancher-desktop",
371
+ "lima",
372
+ );
373
+ const limactl = join(
374
+ "/Applications",
375
+ "Rancher Desktop.app",
376
+ "Contents",
377
+ "Resources",
378
+ "resources",
379
+ "darwin",
380
+ "lima",
381
+ "bin",
382
+ "limactl",
383
+ );
384
+ // Is Rancher Desktop running
385
+ if (safeExistsSync(limactl) || safeExistsSync(limaHome)) {
386
+ const result = safeSpawnSync("rdctl", ["list-settings"]);
387
+ if (result.status !== 0 || result.error) {
388
+ if (
389
+ isNerdctl === undefined &&
390
+ result.stderr?.includes("connection refused")
391
+ ) {
392
+ console.warn(
393
+ "Ensure Rancher Desktop is running prior to invoking cdxgen. To start from the command line, type the command 'rdctl start'",
394
+ );
395
+ isNerdctl = false;
396
+ }
397
+ } else {
398
+ if (DEBUG_MODE) {
399
+ console.log("Rancher Desktop found!");
400
+ }
401
+ isNerdctl = true;
402
+ }
403
+ }
404
+ }
405
+ return isNerdctl;
406
+ }
407
+
408
+ // Cache the registry auth keys
409
+ const registry_auth_keys = {};
410
+ const REQUEST_TIMEOUT_SECS = 60000;
411
+ /**
412
+ * Build a `got` options object for Docker / registry API requests. Resolves
413
+ * authentication headers by consulting (in order) the DOCKER_AUTH_CONFIG
414
+ * environment variable, DOCKER_USER/DOCKER_PASSWORD/DOCKER_EMAIL environment
415
+ * variables, hardcoded tokens in ~/.docker/config.json, credential helpers
416
+ * listed in credHelpers/credsStore, and finally TLS certificate files pointed
417
+ * to by DOCKER_CERT_PATH.
418
+ *
419
+ * @param {string} [forRegistry] Registry hostname (e.g. "registry-1.docker.io").
420
+ * Defaults to DOCKER_SERVER_ADDRESS env var or "docker.io".
421
+ * @param {string} [requestedRegistryRef] Requested registry/image reference used
422
+ * to scope config.json auth matching. Unqualified images default to Docker Hub.
423
+ * @returns {Object} Options object suitable for passing to `got`
424
+ */
425
+ const getDefaultOptions = (forRegistry, requestedRegistryRef = forRegistry) => {
426
+ let authTokenSet = false;
427
+ const credentialSourceEvaluations = [];
428
+ let selectedCredentialSource;
429
+ const noteCredentialSource = (source, outcome, detail = undefined) => {
430
+ credentialSourceEvaluations.push({
431
+ detail,
432
+ outcome,
433
+ source,
434
+ });
435
+ if (outcome === "selected") {
436
+ selectedCredentialSource = source;
437
+ }
438
+ };
439
+ const dockerServerAddress = readEnvironmentVariable("DOCKER_SERVER_ADDRESS");
440
+ const dockerConfig = readEnvironmentVariable("DOCKER_CONFIG");
441
+ const dockerAuthConfig = readEnvironmentVariable("DOCKER_AUTH_CONFIG", {
442
+ sensitive: true,
443
+ });
444
+ const dockerUser = readEnvironmentVariable("DOCKER_USER", {
445
+ sensitive: true,
446
+ });
447
+ const dockerPassword = readEnvironmentVariable("DOCKER_PASSWORD", {
448
+ sensitive: true,
449
+ });
450
+ const dockerEmail = readEnvironmentVariable("DOCKER_EMAIL", {
451
+ sensitive: true,
452
+ });
453
+ if (!forRegistry) {
454
+ forRegistry = dockerServerAddress ?? DOCKER_HUB_REGISTRY;
455
+ }
456
+ requestedRegistryRef = resolveRequestedRegistryRef(
457
+ forRegistry,
458
+ requestedRegistryRef,
459
+ );
460
+ const normalizedForRegistry =
461
+ parseRegistryReference(forRegistry)?.authority ?? forRegistry;
462
+ const authDecisionTarget =
463
+ requestedRegistryRef ||
464
+ normalizedForRegistry ||
465
+ forRegistry ||
466
+ DOCKER_HUB_REGISTRY;
467
+ const opts = {
468
+ enableUnixSockets: true,
469
+ throwHttpErrors: true,
470
+ method: "GET",
471
+ timeout: {
472
+ request: REQUEST_TIMEOUT_SECS,
473
+ socket: REQUEST_TIMEOUT_SECS,
474
+ },
475
+ retry: {
476
+ limit: 3,
477
+ methods: ["GET", "POST", "HEAD"],
478
+ statusCodes: [408, 413, 429, 500, 502, 503, 504, 521, 522, 524],
479
+ },
480
+ hooks: { beforeError: [] },
481
+ mutableDefaults: true,
482
+ };
483
+ const DOCKER_CONFIG = dockerConfig || join(homedir(), ".docker");
484
+ const dockerConfigFile = join(DOCKER_CONFIG, "config.json");
485
+ // Support for private registry
486
+ if (dockerAuthConfig) {
487
+ opts.headers = {
488
+ "X-Registry-Auth": dockerAuthConfig,
489
+ };
490
+ authTokenSet = true;
491
+ noteCredentialSource("DOCKER_AUTH_CONFIG", "selected");
492
+ } else {
493
+ noteCredentialSource("DOCKER_AUTH_CONFIG", "skipped", "not set");
494
+ }
495
+ if (
496
+ !authTokenSet &&
497
+ dockerUser &&
498
+ dockerPassword &&
499
+ dockerEmail &&
500
+ normalizedForRegistry
501
+ ) {
502
+ const authPayload = {
503
+ username: dockerUser,
504
+ email: dockerEmail,
505
+ serveraddress: normalizedForRegistry,
506
+ };
507
+ if (dockerUser === "<token>") {
508
+ authPayload.IdentityToken = dockerPassword;
509
+ } else {
510
+ authPayload.password = dockerPassword;
511
+ }
512
+ opts.headers = {
513
+ "X-Registry-Auth": toBase64Url(JSON.stringify(authPayload)),
514
+ };
515
+ authTokenSet = true;
516
+ noteCredentialSource(
517
+ "DOCKER_USER/DOCKER_PASSWORD/DOCKER_EMAIL",
518
+ "selected",
519
+ );
520
+ } else if (!authTokenSet) {
521
+ noteCredentialSource(
522
+ "DOCKER_USER/DOCKER_PASSWORD/DOCKER_EMAIL",
523
+ "skipped",
524
+ dockerUser || dockerPassword || dockerEmail
525
+ ? "incomplete environment credentials"
526
+ : "not set",
527
+ );
528
+ }
529
+ if (!authTokenSet && safeExistsSync(dockerConfigFile)) {
530
+ const configData = readFileSync(dockerConfigFile, "utf-8");
531
+ recordSensitiveFileRead(dockerConfigFile, {
532
+ label: "Docker credential file",
533
+ });
534
+ if (configData) {
535
+ try {
536
+ const configJson = JSON.parse(configData);
537
+ if (configJson.auths) {
538
+ // Check if there are hardcoded tokens
539
+ for (const serverAddress of Object.keys(configJson.auths)) {
540
+ if (!registriesMatch(serverAddress, requestedRegistryRef)) {
541
+ continue;
542
+ }
543
+ if (configJson.auths[serverAddress].auth) {
544
+ // The Docker config stores auth as base64("user:pass").
545
+ // The X-Registry-Auth header expects base64-encoded JSON.
546
+ const decoded = Buffer.from(
547
+ configJson.auths[serverAddress].auth,
548
+ "base64",
549
+ ).toString();
550
+ const sepIdx = decoded.indexOf(":");
551
+ const authPayload = {
552
+ username: decoded.substring(0, sepIdx),
553
+ password: decoded.substring(sepIdx + 1),
554
+ serveraddress: serverAddress,
555
+ };
556
+ opts.headers = {
557
+ "X-Registry-Auth": toBase64Url(JSON.stringify(authPayload)),
558
+ };
559
+ authTokenSet = true;
560
+ noteCredentialSource(
561
+ "docker-config-auth",
562
+ "selected",
563
+ serverAddress,
564
+ );
565
+ break;
566
+ }
567
+ if (configJson.credsStore) {
568
+ const helperAuthToken = getCredsFromHelper(
569
+ configJson.credsStore,
570
+ serverAddress,
571
+ );
572
+ if (helperAuthToken) {
573
+ opts.headers = {
574
+ "X-Registry-Auth": helperAuthToken,
575
+ };
576
+ authTokenSet = true;
577
+ noteCredentialSource(
578
+ `docker-credential-helper:${configJson.credsStore}`,
579
+ "selected",
580
+ serverAddress,
581
+ );
582
+ break;
583
+ }
584
+ }
585
+ }
586
+ } else if (configJson.credHelpers) {
587
+ // Support for credential helpers
588
+ for (const serverAddress of Object.keys(configJson.credHelpers)) {
589
+ if (!registriesMatch(serverAddress, requestedRegistryRef)) {
590
+ continue;
591
+ }
592
+ if (configJson.credHelpers[serverAddress]) {
593
+ const helperAuthToken = getCredsFromHelper(
594
+ configJson.credHelpers[serverAddress],
595
+ serverAddress,
596
+ );
597
+ if (helperAuthToken) {
598
+ opts.headers = {
599
+ "X-Registry-Auth": helperAuthToken,
600
+ };
601
+ authTokenSet = true;
602
+ noteCredentialSource(
603
+ `docker-credential-helper:${configJson.credHelpers[serverAddress]}`,
604
+ "selected",
605
+ serverAddress,
606
+ );
607
+ break;
608
+ }
609
+ }
610
+ }
611
+ }
612
+ if (!authTokenSet) {
613
+ noteCredentialSource(
614
+ "docker-config",
615
+ "skipped",
616
+ "no matching config.json auth entry",
617
+ );
618
+ }
619
+ } catch (_err) {
620
+ // pass
621
+ noteCredentialSource("docker-config", "skipped", "config parse failed");
622
+ }
623
+ }
624
+ } else if (!authTokenSet) {
625
+ noteCredentialSource("docker-config", "skipped", "config.json not found");
626
+ }
627
+ const userInfo = _userInfo();
628
+ opts.podmanPrefixUrl = isWin ? "" : "http://unix:/run/podman/podman.sock:";
629
+ opts.podmanRootlessPrefixUrl = isWin
630
+ ? ""
631
+ : `http://unix:/run/user/${userInfo.uid}/podman/podman.sock:`;
632
+ const dockerHost = readEnvironmentVariable("DOCKER_HOST");
633
+ const dockerCertPath = readEnvironmentVariable("DOCKER_CERT_PATH");
634
+ const dockerTlsVerify = readEnvironmentVariable("DOCKER_TLS_VERIFY");
635
+ if (!dockerHost) {
636
+ if (isPodman) {
637
+ opts.prefixUrl = isPodmanRootless
638
+ ? opts.podmanRootlessPrefixUrl
639
+ : opts.podmanPrefixUrl;
640
+ } else {
641
+ if (isWinLocalTLS) {
642
+ opts.prefixUrl = WIN_LOCAL_TLS;
643
+ } else {
644
+ // Named pipes syntax for Windows doesn't work with got
645
+ // See: https://github.com/sindresorhus/got/issues/2178
646
+ /*
647
+ opts.prefixUrl = isWin
648
+ ? "npipe//./pipe/docker_engine:"
649
+ : "unix:/var/run/docker.sock:";
650
+ */
651
+ opts.prefixUrl = isWin
652
+ ? WIN_LOCAL_TLS
653
+ : isDockerRootless
654
+ ? `http://unix:${homedir()}/.docker/run/docker.sock:`
655
+ : "http://unix:/var/run/docker.sock:";
656
+ }
657
+ }
658
+ } else {
659
+ let hostStr = dockerHost;
660
+ if (hostStr.startsWith("unix:///")) {
661
+ hostStr = hostStr.replace("unix:///", "http://unix:/");
662
+ if (hostStr.includes("docker.sock")) {
663
+ hostStr = hostStr.replace("docker.sock", "docker.sock:");
664
+ isDockerRootless = true;
665
+ }
666
+ }
667
+ opts.prefixUrl = hostStr;
668
+ if (dockerCertPath) {
669
+ const dockerCertFile = join(dockerCertPath, "cert.pem");
670
+ const dockerKeyFile = join(dockerCertPath, "key.pem");
671
+ const dockerCertificate = readFileSync(dockerCertFile, "utf8");
672
+ recordSensitiveFileRead(dockerCertFile, {
673
+ label: "Docker client certificate",
674
+ });
675
+ const dockerKey = readFileSync(dockerKeyFile, "utf8");
676
+ recordSensitiveFileRead(dockerKeyFile, {
677
+ label: "Docker client private key",
678
+ });
679
+ opts.https = {
680
+ certificate: dockerCertificate,
681
+ key: dockerKey,
682
+ };
683
+ // Disable tls on empty values
684
+ // From the docker docs: Setting the DOCKER_TLS_VERIFY environment variable to any value other than the empty string is equivalent to setting the --tlsverify flag
685
+ if (dockerTlsVerify === "") {
686
+ opts.https.rejectUnauthorized = false;
687
+ console.log("TLS Verification disabled for", hostStr);
688
+ }
689
+ }
690
+ }
691
+
692
+ if (!selectedCredentialSource) {
693
+ noteCredentialSource(
694
+ "anonymous",
695
+ "selected",
696
+ "no credential source resolved",
697
+ );
698
+ }
699
+ const skippedSources = credentialSourceEvaluations
700
+ .filter((entry) => entry.outcome !== "selected")
701
+ .map((entry) =>
702
+ entry.detail ? `${entry.source} (${entry.detail})` : entry.source,
703
+ );
704
+ recordDecisionActivity(`docker-auth:${authDecisionTarget}`, {
705
+ metadata: {
706
+ decisionType: "credential-source-selection",
707
+ evaluatedSources: credentialSourceEvaluations.map(
708
+ ({ detail, outcome, source }) =>
709
+ detail ? `${source}:${outcome}:${detail}` : `${source}:${outcome}`,
710
+ ),
711
+ selectedSource: selectedCredentialSource,
712
+ },
713
+ reason: `Selected Docker auth source '${selectedCredentialSource}' for ${authDecisionTarget}. Skipped: ${skippedSources.length ? skippedSources.join(", ") : "none"}.`,
714
+ });
715
+
716
+ return opts;
717
+ };
718
+
719
+ /**
720
+ * Establish (or reuse) a `got` client connected to the local Docker or Podman
721
+ * daemon. Tries multiple socket / URL candidates in order: the default Docker
722
+ * socket, the rootless Docker socket, the Windows TCP endpoint, the rootless
723
+ * Podman socket, and the root Podman socket. Sets the module-level flags
724
+ * `isPodman`, `isPodmanRootless`, `isDockerRootless`, and `isWinLocalTLS` as a
725
+ * side-effect. Returns `undefined` when containerd / nerdctl is in use or no
726
+ * daemon could be reached.
727
+ *
728
+ * @param {Object} _options Additional options (currently unused; retained for
729
+ * backwards compatibility with existing callers)
730
+ * @param {string} [forRegistry] Registry hostname forwarded to `getDefaultOptions`
731
+ * @returns {Promise<Object|undefined>} A daemon connection object bound to the
732
+ * daemon base URL, or `undefined`
733
+ */
734
+ export const getConnection = async (_options, forRegistry) => {
735
+ if (isContainerd || isNerdctl) {
736
+ return undefined;
737
+ }
738
+ if (isDryRun) {
739
+ try {
740
+ getDefaultOptions(forRegistry);
741
+ } catch (error) {
742
+ recordActivity({
743
+ kind: "read",
744
+ reason: `Dry run mode failed while tracing Docker credential inputs: ${error.message}`,
745
+ status: "failed",
746
+ target: error?.path || forRegistry || "container-daemon",
747
+ });
748
+ }
749
+ recordActivity({
750
+ kind: "network",
751
+ reason:
752
+ "Dry run mode blocks container daemon and registry HTTP requests.",
753
+ status: "blocked",
754
+ target: forRegistry || "container-daemon",
755
+ });
756
+ return undefined;
757
+ }
758
+ if (!dockerConn) {
759
+ const defaultOptions = getDefaultOptions(forRegistry);
760
+ const podmanRootlessUrl = defaultOptions.podmanRootlessPrefixUrl;
761
+ const podmanRootUrl = defaultOptions.podmanPrefixUrl;
762
+ // Probe a candidate daemon endpoint. Returns a live connection on success
763
+ // and closes/discards the connection (throwing) on failure so callers can
764
+ // try the next candidate.
765
+ const tryConnect = async (prefixUrl, pingPath) => {
766
+ const conn = createDaemonConnection(
767
+ prefixUrl,
768
+ defaultOptions.headers,
769
+ defaultOptions.https,
770
+ );
771
+ try {
772
+ await conn.request(pingPath, { method: "GET" });
773
+ return conn;
774
+ } catch (err) {
775
+ await conn.close();
776
+ throw err;
777
+ }
778
+ };
779
+ try {
780
+ dockerConn = await tryConnect(defaultOptions.prefixUrl, "_ping");
781
+ if (DEBUG_MODE) {
782
+ if (isDockerRootless) {
783
+ console.log("Docker service in rootless mode detected.");
784
+ } else {
785
+ console.log(
786
+ "Docker service in root mode detected. Consider switching to rootless mode to improve security. See https://docs.docker.com/engine/security/rootless/",
787
+ );
788
+ }
789
+ }
790
+ } catch (_err) {
791
+ try {
792
+ dockerConn = await tryConnect(
793
+ `http://unix:${homedir()}/.docker/run/docker.sock:`,
794
+ "_ping",
795
+ );
796
+ isDockerRootless = true;
797
+ if (DEBUG_MODE) {
798
+ console.log("Docker service in rootless mode detected.");
799
+ }
800
+ return dockerConn;
801
+ } catch (_err) {
802
+ // ignore
803
+ }
804
+ try {
805
+ if (isWin) {
806
+ dockerConn = await tryConnect(WIN_LOCAL_TLS, "_ping");
807
+ isWinLocalTLS = true;
808
+ if (DEBUG_MODE) {
809
+ console.log("Docker desktop on Windows detected.");
810
+ }
811
+ } else {
812
+ dockerConn = await tryConnect(podmanRootlessUrl, "libpod/_ping");
813
+ isPodman = true;
814
+ isPodmanRootless = true;
815
+ if (DEBUG_MODE) {
816
+ console.log(
817
+ "Podman in rootless mode detected. Thank you for using podman!",
818
+ );
819
+ }
820
+ }
821
+ } catch (_err) {
822
+ try {
823
+ dockerConn = await tryConnect(podmanRootUrl, "libpod/_ping");
824
+ isPodman = true;
825
+ isPodmanRootless = false;
826
+ console.log(
827
+ "Podman in root mode detected. Consider switching to rootless mode to improve security. See https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md",
828
+ );
829
+ } catch (_err) {
830
+ if (_platform() === "win32") {
831
+ console.warn(
832
+ "Ensure Docker for Desktop is running as an administrator with 'Exposing daemon on TCP without TLS' setting turned on.",
833
+ );
834
+ } else if (_platform() === "darwin" && !isNerdctl) {
835
+ if (detectRancherDesktop() || detectColima()) {
836
+ return undefined;
837
+ }
838
+ if (isNerdctl === undefined) {
839
+ console.warn(
840
+ "Ensure Podman Desktop (open-source) or Docker for Desktop (May require subscription) is running.",
841
+ );
842
+ }
843
+ } else {
844
+ console.warn(
845
+ "Ensure docker/podman service or Docker for Desktop is running.",
846
+ );
847
+ console.log(
848
+ "Check if the post-installation steps were performed correctly as per this documentation https://docs.docker.com/engine/install/linux-postinstall/",
849
+ );
850
+ }
851
+ }
852
+ }
853
+ }
854
+ }
855
+ return dockerConn;
856
+ };
857
+
858
+ /**
859
+ * Send a single HTTP request to the Docker / Podman daemon via the connection
860
+ * returned by {@link getConnection}. GET requests are parsed as JSON; all other
861
+ * methods receive a Buffer response body.
862
+ *
863
+ * @param {string} path API path relative to the daemon base URL (e.g. "images/ubuntu:latest/json")
864
+ * @param {string} method HTTP method (e.g. "GET", "POST", "DELETE")
865
+ * @param {string} [forRegistry] Registry hostname forwarded to `getDefaultOptions` for auth headers
866
+ * @returns {Promise<Object|Buffer|undefined>} Parsed JSON object for GET
867
+ * requests, raw Buffer for other methods, or `undefined` if no client is available
868
+ */
869
+ export const makeRequest = async (path, method, forRegistry) => {
870
+ if (isDryRun) {
871
+ recordActivity({
872
+ kind: "network",
873
+ reason:
874
+ "Dry run mode blocks container daemon and registry HTTP requests.",
875
+ status: "blocked",
876
+ target: `${method} ${forRegistry || "container-daemon"}/${path}`,
877
+ });
878
+ return undefined;
879
+ }
880
+ const client = await getConnection({}, forRegistry);
881
+ if (!client) {
882
+ return undefined;
883
+ }
884
+ // The connection is already bound to the correct daemon base URL. Only pass
885
+ // per-request auth headers and the method.
886
+ const defaultOptions = getDefaultOptions(
887
+ forRegistry,
888
+ extractRequestedRegistryRefFromPath(path, forRegistry),
889
+ );
890
+ const opts = {
891
+ responseType: method === "GET" ? "json" : "buffer",
892
+ method,
893
+ };
894
+ if (defaultOptions.headers) {
895
+ opts.headers = defaultOptions.headers;
896
+ }
897
+ return await client.request(path, opts);
898
+ };
899
+
900
+ /**
901
+ * Parse image name
902
+ *
903
+ * docker pull debian
904
+ * docker pull debian:jessie
905
+ * docker pull ubuntu@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2
906
+ * docker pull myregistry.local:5000/testing/test-image
907
+ */
908
+ export const parseImageName = (fullImageName) => {
909
+ const nameObj = {
910
+ registry: "",
911
+ repo: "",
912
+ tag: "",
913
+ digest: "",
914
+ platform: "",
915
+ group: "",
916
+ name: "",
917
+ };
918
+ if (!fullImageName) {
919
+ return nameObj;
920
+ }
921
+ // ensure it's lowercased
922
+ fullImageName = fullImageName.toLowerCase().trim();
923
+
924
+ // Extract platform
925
+ if (fullImageName.startsWith("--platform=")) {
926
+ const tmpName = fullImageName.replace("--platform=", "").split(" ");
927
+ nameObj.platform = tmpName[0];
928
+ fullImageName = tmpName[1];
929
+ }
930
+
931
+ // Extract registry name
932
+ if (
933
+ fullImageName.includes("/") &&
934
+ (fullImageName.includes(".") || fullImageName.includes(":"))
935
+ ) {
936
+ let urlObj;
937
+ if (URL.canParse(fullImageName)) {
938
+ urlObj = new URL(fullImageName);
939
+ }
940
+ const tmpA = fullImageName.split("/");
941
+ if (
942
+ (urlObj && urlObj.pathname !== fullImageName) ||
943
+ tmpA[0].includes(".") ||
944
+ tmpA[0].includes(":")
945
+ ) {
946
+ nameObj.registry = tmpA[0];
947
+ fullImageName = fullImageName.replace(`${tmpA[0]}/`, "");
948
+ }
949
+ }
950
+
951
+ // Extract digest name
952
+ if (fullImageName.includes("@sha256:")) {
953
+ const tmpA = fullImageName.split("@sha256:");
954
+ if (tmpA.length > 1) {
955
+ nameObj.digest = tmpA[tmpA.length - 1];
956
+ fullImageName = fullImageName.replace(`@sha256:${nameObj.digest}`, "");
957
+ }
958
+ }
959
+
960
+ // Extract tag name
961
+ if (fullImageName.includes(":")) {
962
+ const tmpA = fullImageName.split(":");
963
+ if (tmpA.length > 1) {
964
+ nameObj.tag = tmpA[tmpA.length - 1];
965
+ fullImageName = fullImageName.replace(`:${nameObj.tag}`, "");
966
+ }
967
+ }
968
+
969
+ // The left over string is the repo name
970
+ nameObj.repo = fullImageName;
971
+ nameObj.name = fullImageName;
972
+
973
+ // extract group name
974
+ if (fullImageName.includes("/")) {
975
+ const tmpA = fullImageName.split("/");
976
+ if (tmpA.length > 1) {
977
+ nameObj.name = tmpA[tmpA.length - 1];
978
+ nameObj.group = fullImageName.replace(`/${tmpA[tmpA.length - 1]}`, "");
979
+ }
980
+ }
981
+
982
+ return nameObj;
983
+ };
984
+
985
+ /**
986
+ * Prefer cli on windows, nerdctl on mac, or when using tcp/ssh based host.
987
+ *
988
+ * @returns boolean true if we should use the cli. false otherwise
989
+ */
990
+ const getContainerCliCmd = () => {
991
+ if (readEnvironmentVariable("DOCKER_CMD")?.trim()) {
992
+ return readEnvironmentVariable("DOCKER_CMD").trim();
993
+ }
994
+ detectRancherDesktop() || detectColima();
995
+ if (isNerdctl) {
996
+ return "nerdctl";
997
+ }
998
+ return "docker";
999
+ };
1000
+
1001
+ const needsCliFallback = () => {
1002
+ if (
1003
+ ["true", "1"].includes(readEnvironmentVariable("DOCKER_USE_CLI")) ||
1004
+ readEnvironmentVariable("DOCKER_CMD")?.trim() ||
1005
+ (_platform() === "darwin" && (detectRancherDesktop() || detectColima()))
1006
+ ) {
1007
+ return true;
1008
+ }
1009
+ return (
1010
+ isWin ||
1011
+ (readEnvironmentVariable("DOCKER_HOST") &&
1012
+ (readEnvironmentVariable("DOCKER_HOST").startsWith("tcp://") ||
1013
+ readEnvironmentVariable("DOCKER_HOST").startsWith("ssh://")))
1014
+ );
1015
+ };
1016
+
1017
+ /**
1018
+ * Method to get image to the local registry by pulling from the remote if required
1019
+ */
1020
+ export const getImage = async (fullImageName) => {
1021
+ let localData;
1022
+ let pullData;
1023
+ const { registry, repo, tag, digest } = parseImageName(fullImageName);
1024
+ const repoWithTag =
1025
+ registry && registry !== DOCKER_HUB_REGISTRY
1026
+ ? fullImageName
1027
+ : `${repo}:${tag !== "" ? tag : ":latest"}`;
1028
+ // Fetch only the latest tag if none is specified
1029
+ if (tag === "" && digest === "") {
1030
+ fullImageName = `${fullImageName}:latest`;
1031
+ }
1032
+ if (isContainerd && !needsCliFallback()) {
1033
+ console.log(
1034
+ "containerd/nerdctl is currently unsupported. Export the image manually and run cdxgen against the tar image.",
1035
+ );
1036
+ return undefined;
1037
+ }
1038
+ if (needsCliFallback()) {
1039
+ const dockerCmd = getContainerCliCmd();
1040
+ let needsPull = true;
1041
+ // Let's check the local cache first
1042
+ let result = safeSpawnSync(dockerCmd, ["images", "--format=json"]);
1043
+ if (result.status === 0 && result.stdout) {
1044
+ for (const imgLine of result.stdout.split("\n")) {
1045
+ try {
1046
+ const imgObj = JSON.parse(Buffer.from(imgLine).toString());
1047
+ if (`${imgObj.Repository}:${imgObj.Tag}` === fullImageName) {
1048
+ needsPull = false;
1049
+ break;
1050
+ }
1051
+ } catch (_err) {
1052
+ // continue regardless of error
1053
+ }
1054
+ }
1055
+ }
1056
+ if (needsPull) {
1057
+ result = safeSpawnSync(dockerCmd, ["pull", fullImageName]);
1058
+ if (result.status !== 0 || result.error) {
1059
+ if (result.stderr?.includes("docker daemon is not running")) {
1060
+ console.log(
1061
+ "Ensure Docker for Desktop is running as an administrator with 'Exposing daemon on TCP without TLS' setting turned on.",
1062
+ );
1063
+ } else if (result.stderr?.includes("not found")) {
1064
+ console.log(
1065
+ "Set the environment variable DOCKER_CMD to use an alternative command such as nerdctl or podman.",
1066
+ );
1067
+ } else if (result.stderr) {
1068
+ console.log(result.stderr);
1069
+ }
1070
+ }
1071
+ }
1072
+ result = safeSpawnSync(dockerCmd, ["inspect", fullImageName]);
1073
+ if (result.status !== 0 || result.error) {
1074
+ if (result.stderr) {
1075
+ console.log(result.stderr);
1076
+ }
1077
+ // Continue with the daemon client when the CLI fallback is unavailable
1078
+ // or unable to inspect the image.
1079
+ } else {
1080
+ try {
1081
+ const stdout = result.stdout;
1082
+ if (stdout) {
1083
+ const inspectData = JSON.parse(Buffer.from(stdout).toString());
1084
+ if (inspectData && Array.isArray(inspectData)) {
1085
+ return inspectData[0];
1086
+ }
1087
+ return inspectData;
1088
+ }
1089
+ } catch (_err) {
1090
+ // continue regardless of error
1091
+ }
1092
+ }
1093
+ }
1094
+ try {
1095
+ localData = await makeRequest(
1096
+ `images/${repoWithTag}/json`,
1097
+ "GET",
1098
+ registry,
1099
+ );
1100
+ if (localData) {
1101
+ return localData;
1102
+ }
1103
+ } catch (_err) {
1104
+ // ignore
1105
+ }
1106
+ try {
1107
+ localData = await makeRequest(`images/${repo}/json`, "GET", registry);
1108
+ } catch (_err) {
1109
+ try {
1110
+ localData = await makeRequest(
1111
+ `images/${fullImageName}/json`,
1112
+ "GET",
1113
+ registry,
1114
+ );
1115
+ if (localData) {
1116
+ return localData;
1117
+ }
1118
+ } catch (_err) {
1119
+ // ignore
1120
+ }
1121
+ if (DEBUG_MODE) {
1122
+ console.log(
1123
+ `Trying to pull the image ${fullImageName} from registry. This might take a while ...`,
1124
+ );
1125
+ }
1126
+ // If the data is not available locally
1127
+ try {
1128
+ pullData = await makeRequest(
1129
+ `images/create?fromImage=${fullImageName}`,
1130
+ "POST",
1131
+ registry,
1132
+ );
1133
+ if (
1134
+ pullData &&
1135
+ (pullData.includes("no match for platform in manifest") ||
1136
+ pullData.includes("Error choosing an image from manifest list"))
1137
+ ) {
1138
+ console.warn(
1139
+ "You may have to enable experimental settings in docker to support this platform!",
1140
+ );
1141
+ console.warn(
1142
+ "To scan windows images, run cdxgen on a windows server with hyper-v and docker installed. Switch to windows containers in your docker settings.",
1143
+ );
1144
+ return undefined;
1145
+ }
1146
+ } catch (_err) {
1147
+ try {
1148
+ if (DEBUG_MODE) {
1149
+ console.log(`Re-trying the pull with the name ${repoWithTag}.`);
1150
+ }
1151
+ await makeRequest(
1152
+ `images/create?fromImage=${repoWithTag}`,
1153
+ "POST",
1154
+ registry,
1155
+ );
1156
+ } catch (_err) {
1157
+ // continue regardless of error
1158
+ }
1159
+ }
1160
+ try {
1161
+ if (DEBUG_MODE) {
1162
+ console.log(`Trying with ${repoWithTag}`);
1163
+ }
1164
+ localData = await makeRequest(
1165
+ `images/${repoWithTag}/json`,
1166
+ "GET",
1167
+ registry,
1168
+ );
1169
+ if (localData) {
1170
+ return localData;
1171
+ }
1172
+ } catch (_err) {
1173
+ try {
1174
+ if (DEBUG_MODE) {
1175
+ console.log(`Trying with ${repo}`);
1176
+ }
1177
+ localData = await makeRequest(`images/${repo}/json`, "GET", registry);
1178
+ if (localData) {
1179
+ return localData;
1180
+ }
1181
+ } catch (_err) {
1182
+ // continue regardless of error
1183
+ }
1184
+ try {
1185
+ if (DEBUG_MODE) {
1186
+ console.log(`Trying with ${fullImageName}`);
1187
+ }
1188
+ localData = await makeRequest(
1189
+ `images/${fullImageName}/json`,
1190
+ "GET",
1191
+ registry,
1192
+ );
1193
+ } catch (_err) {
1194
+ // continue regardless of error
1195
+ }
1196
+ }
1197
+ }
1198
+ if (!localData) {
1199
+ console.log(
1200
+ `Unable to pull ${fullImageName}. Check if the name is valid. Perform any authentication prior to invoking cdxgen.`,
1201
+ );
1202
+ console.log(
1203
+ `Try manually pulling this image using docker pull ${fullImageName}`,
1204
+ );
1205
+ }
1206
+ return localData;
1207
+ };
1208
+
1209
+ /**
1210
+ * @typedef {{ path: string }} TarReadEntryLike
1211
+ */
1212
+
1213
+ /**
1214
+ * Warnings such as TAR_ENTRY_INFO are treated as errors in strict mode. While this is mostly desired, we can relax this
1215
+ * requirement for one particular warning related to absolute paths.
1216
+ * This callback function checks for absolute paths in the entry read from the archive and strips them using a custom
1217
+ * method.
1218
+ *
1219
+ * @param {TarReadEntryLike} entry ReadEntry object from node-tar
1220
+ */
1221
+ function handleAbsolutePath(entry) {
1222
+ if (entry.path === "/" || win32.isAbsolute(entry.path)) {
1223
+ entry.path = stripAbsolutePath(entry.path);
1224
+ }
1225
+ }
1226
+
1227
+ /**
1228
+ * Filter out problematic files, paths, and devices during tar extraction.
1229
+ */
1230
+ function tarFilter(path, entry) {
1231
+ const name = basename(path);
1232
+ if (name.startsWith(".wh.")) {
1233
+ return false;
1234
+ }
1235
+ const ext = win32.extname(name).toLowerCase();
1236
+ if (MEDIA_EXTENSIONS.has(ext)) {
1237
+ return false;
1238
+ }
1239
+ return !(
1240
+ EXTRACT_EXCLUDE_PATHS.some((p) => path.includes(p)) ||
1241
+ EXTRACT_EXCLUDE_TYPES.has(entry.type)
1242
+ );
1243
+ }
1244
+
1245
+ /**
1246
+ * Suppress low-signal tar warnings (TAR_ENTRY_INFO, TAR_LONGLINK) that are
1247
+ * expected when extracting container image layers. All other warning codes are
1248
+ * logged when DEBUG_MODE is enabled.
1249
+ *
1250
+ * @param {string} code Tar warning code (e.g. "TAR_ENTRY_INFO")
1251
+ * @param {string} message Human-readable warning message
1252
+ */
1253
+ function handleTarWarning(code, message) {
1254
+ if (code === "TAR_ENTRY_INFO" || code === "TAR_LONGLINK") {
1255
+ return;
1256
+ }
1257
+ if (DEBUG_MODE) {
1258
+ console.log(code, message);
1259
+ }
1260
+ }
1261
+
1262
+ // These paths are known to cause extract errors
1263
+ const EXTRACT_EXCLUDE_PATHS = [
1264
+ "etc/machine-id",
1265
+ "etc/gshadow",
1266
+ "etc/shadow",
1267
+ "etc/passwd",
1268
+ "etc/ssl/certs",
1269
+ "etc/pki/ca-trust",
1270
+ "usr/lib/systemd/",
1271
+ "usr/lib64/libdevmapper.so",
1272
+ "usr/sbin/",
1273
+ "cacerts",
1274
+ "ssl/certs",
1275
+ "logs/",
1276
+ "dev/",
1277
+ "proc/",
1278
+ "sys/",
1279
+ "usr/share/zoneinfo/",
1280
+ "usr/share/doc/",
1281
+ "usr/share/man/",
1282
+ "usr/share/icons/",
1283
+ "usr/share/i18n/",
1284
+ "var/lib/ca-certificates",
1285
+ "root/.gnupg",
1286
+ "root/.dotnet",
1287
+ "usr/share/licenses/device-mapper-libs",
1288
+ ];
1289
+
1290
+ const MEDIA_EXTENSIONS = new Set([
1291
+ ".jpg",
1292
+ ".jpeg",
1293
+ ".png",
1294
+ ".gif",
1295
+ ".bmp",
1296
+ ".tiff",
1297
+ ".ico",
1298
+ ".svg",
1299
+ ".mp3",
1300
+ ".wav",
1301
+ ".mp4",
1302
+ ".avi",
1303
+ ".mov",
1304
+ ".ttf",
1305
+ ".woff",
1306
+ ".woff2",
1307
+ ".eot",
1308
+ ]);
1309
+
1310
+ // These device types are known to cause extract errors
1311
+ const EXTRACT_EXCLUDE_TYPES = new Set([
1312
+ "BlockDevice",
1313
+ "CharacterDevice",
1314
+ "FIFO",
1315
+ "MultiVolume",
1316
+ "TapeVolume",
1317
+ "SymbolicLink",
1318
+ "RenamedOrSymlinked",
1319
+ "HardLink",
1320
+ "Link",
1321
+ ]);
1322
+
1323
+ /**
1324
+ * Extract a container image tar archive into a destination directory.
1325
+ * Applies path sanitisation, ownership/permission preservation settings, and
1326
+ * an entry filter to skip problematic files and device nodes. Handles common
1327
+ * tar errors gracefully, logging only unexpected ones.
1328
+ *
1329
+ * @param {string} fullImageName Path to the source tar archive
1330
+ * @param {string} dir Destination directory to extract into
1331
+ * @param {Object} options CLI options (uses `options.failOnError`)
1332
+ * @returns {Promise<boolean>} `true` on success, `false` when the archive is
1333
+ * empty or a non-fatal error was encountered
1334
+ */
1335
+ export const extractTar = async (fullImageName, dir, options) => {
1336
+ try {
1337
+ return await safeExtractArchive(
1338
+ fullImageName,
1339
+ dir,
1340
+ async () =>
1341
+ await stream.pipeline(
1342
+ createReadStream(fullImageName),
1343
+ x({
1344
+ sync: false,
1345
+ preserveOwner: false,
1346
+ noMtime: true,
1347
+ noChmod: true,
1348
+ strict: !NON_STRICT_TAR_EXTRACT,
1349
+ C: dir,
1350
+ portable: true,
1351
+ unlink: true,
1352
+ onwarn: handleTarWarning,
1353
+ onReadEntry: handleAbsolutePath,
1354
+ filter: tarFilter,
1355
+ }),
1356
+ ),
1357
+ "untar",
1358
+ {
1359
+ blockedReason:
1360
+ "Dry run mode blocks untar and layer extraction operations because they create files on disk.",
1361
+ metadata: {
1362
+ archiveFormat: "tar",
1363
+ },
1364
+ },
1365
+ );
1366
+ } catch (err) {
1367
+ if (err.code === "EPERM" && err.syscall === "symlink") {
1368
+ console.log(
1369
+ "Please run cdxgen from a powershell terminal with admin privileges to create symlinks.",
1370
+ );
1371
+ console.log(err);
1372
+ } else if (
1373
+ ![
1374
+ "TAR_BAD_ARCHIVE",
1375
+ "TAR_ENTRY_INFO",
1376
+ "TAR_ENTRY_INVALID",
1377
+ "TAR_ENTRY_ERROR",
1378
+ "TAR_ENTRY_UNSUPPORTED",
1379
+ "TAR_ABORT",
1380
+ "EACCES",
1381
+ ].includes(err.code)
1382
+ ) {
1383
+ console.log(
1384
+ `Error while extracting image ${fullImageName} to ${dir}. Please file this bug to the cdxgen repo. https://github.com/cdxgen/cdxgen/issues`,
1385
+ );
1386
+ console.log("------------");
1387
+ console.log(err);
1388
+ console.log("------------");
1389
+ } else if (err.code === "TAR_BAD_ARCHIVE") {
1390
+ if (DEBUG_MODE) {
1391
+ console.log(`Archive ${fullImageName} is empty. Skipping.`);
1392
+ }
1393
+ return false;
1394
+ } else if (["EACCES"].includes(err.code)) {
1395
+ console.log(err);
1396
+ } else if (["TAR_ENTRY_INFO", "TAR_ENTRY_INVALID"].includes(err.code)) {
1397
+ if (
1398
+ err?.header?.path?.includes("{") ||
1399
+ err?.message?.includes("linkpath required") ||
1400
+ err?.message?.includes("linkpath forbidden")
1401
+ ) {
1402
+ return false;
1403
+ }
1404
+ if (DEBUG_MODE) {
1405
+ console.log(err);
1406
+ }
1407
+ } else if (DEBUG_MODE) {
1408
+ console.log(err.code, "is not handled yet in extractTar method.");
1409
+ }
1410
+ options.failOnError && process.exit(1);
1411
+ return false;
1412
+ }
1413
+ };
1414
+
1415
+ const readArchiveJson = (jsonFile) => {
1416
+ if (!jsonFile || !safeExistsSync(jsonFile)) {
1417
+ return undefined;
1418
+ }
1419
+ return JSON.parse(
1420
+ readFileSync(jsonFile, {
1421
+ encoding: "utf-8",
1422
+ }),
1423
+ );
1424
+ };
1425
+
1426
+ const tryReadArchiveJson = (jsonFile) => {
1427
+ try {
1428
+ return readArchiveJson(jsonFile);
1429
+ } catch (_err) {
1430
+ return undefined;
1431
+ }
1432
+ };
1433
+
1434
+ const digestToBlobPath = (digest) => {
1435
+ if (!digest?.startsWith("sha256:")) {
1436
+ return undefined;
1437
+ }
1438
+ return join("blobs", "sha256", digest.replace("sha256:", ""));
1439
+ };
1440
+
1441
+ const archiveBlobPath = (tempDir, digest) => {
1442
+ const blobPath = digestToBlobPath(digest);
1443
+ return blobPath ? join(tempDir, blobPath) : undefined;
1444
+ };
1445
+
1446
+ const toManifestEntry = (manifestBlob) => {
1447
+ const configBlob = digestToBlobPath(manifestBlob?.config?.digest);
1448
+ const layers =
1449
+ manifestBlob?.layers
1450
+ ?.map((layer) => digestToBlobPath(layer?.digest))
1451
+ .filter(Boolean) || [];
1452
+ if (!configBlob && !layers.length) {
1453
+ return undefined;
1454
+ }
1455
+ return {
1456
+ Config: configBlob,
1457
+ Layers: layers,
1458
+ };
1459
+ };
1460
+
1461
+ const resolveArchiveManifest = (manifestData, tempDir) => {
1462
+ if (Array.isArray(manifestData)) {
1463
+ return manifestData;
1464
+ }
1465
+ if (!manifestData || typeof manifestData !== "object") {
1466
+ return [];
1467
+ }
1468
+ if (Array.isArray(manifestData.manifests)) {
1469
+ const resolvedManifests = manifestData.manifests
1470
+ .map((manifestEntry) => {
1471
+ if (manifestEntry?.Layers?.length || manifestEntry?.Config) {
1472
+ return manifestEntry;
1473
+ }
1474
+ const manifestBlob = tryReadArchiveJson(
1475
+ archiveBlobPath(tempDir, manifestEntry?.digest),
1476
+ );
1477
+ const resolvedEntry = toManifestEntry(manifestBlob);
1478
+ return resolvedEntry
1479
+ ? {
1480
+ ...manifestEntry,
1481
+ ...resolvedEntry,
1482
+ }
1483
+ : manifestEntry;
1484
+ })
1485
+ .filter(Boolean);
1486
+ return resolvedManifests.length
1487
+ ? resolvedManifests
1488
+ : manifestData.manifests;
1489
+ }
1490
+ const manifestEntry = toManifestEntry(manifestData);
1491
+ return manifestEntry ? [manifestEntry] : [];
1492
+ };
1493
+
1494
+ const discoverManifestFromBlobs = (tempDir) => {
1495
+ const blobsDir = join(tempDir, "blobs", "sha256");
1496
+ if (!safeExistsSync(blobsDir)) {
1497
+ return undefined;
1498
+ }
1499
+ const blobFiles = readdirSync(blobsDir);
1500
+ for (const blobFile of blobFiles) {
1501
+ const manifestBlob = tryReadArchiveJson(join(blobsDir, blobFile));
1502
+ const manifestEntry = toManifestEntry(manifestBlob);
1503
+ if (manifestEntry?.Layers?.length || manifestEntry?.Config) {
1504
+ return [manifestEntry];
1505
+ }
1506
+ }
1507
+ return undefined;
1508
+ };
1509
+
1510
+ /**
1511
+ * Method to export a container image archive.
1512
+ * Returns the location of the layers with additional packages related metadata
1513
+ */
1514
+ export const exportArchive = async (fullImageName, options = {}) => {
1515
+ if (isDryRun) {
1516
+ recordActivity({
1517
+ kind: "container",
1518
+ reason:
1519
+ "Dry run mode blocks container archive expansion and layer materialization.",
1520
+ status: "blocked",
1521
+ target: fullImageName,
1522
+ });
1523
+ return undefined;
1524
+ }
1525
+ if (!safeExistsSync(fullImageName)) {
1526
+ console.log(`Unable to find container image archive ${fullImageName}`);
1527
+ return undefined;
1528
+ }
1529
+ const manifest = {};
1530
+ const tempDir = safeMkdtempSync(join(getTmpDir(), "docker-images-"));
1531
+ const allLayersExplodedDir = join(tempDir, "all-layers");
1532
+ const blobsDir = join(tempDir, "blobs", "sha256");
1533
+ safeMkdirSync(allLayersExplodedDir);
1534
+ const manifestFile = join(tempDir, "manifest.json");
1535
+ const manifestIndexFile = join(tempDir, "index.json");
1536
+ const synthesizedManifestFile = join(tempDir, "synthetic-manifest.json");
1537
+ try {
1538
+ await extractTar(fullImageName, tempDir, options);
1539
+ if (safeExistsSync(manifestFile)) {
1540
+ // docker archive manifest file
1541
+ return await extractFromManifest(
1542
+ manifestFile,
1543
+ {},
1544
+ tempDir,
1545
+ allLayersExplodedDir,
1546
+ options,
1547
+ );
1548
+ }
1549
+ if (safeExistsSync(manifestIndexFile)) {
1550
+ return await extractFromManifest(
1551
+ manifestIndexFile,
1552
+ {},
1553
+ tempDir,
1554
+ allLayersExplodedDir,
1555
+ options,
1556
+ );
1557
+ }
1558
+ // podman use blobs dir
1559
+ if (safeExistsSync(blobsDir)) {
1560
+ const discoveredManifest = discoverManifestFromBlobs(tempDir);
1561
+ if (discoveredManifest?.length) {
1562
+ safeWriteSync(
1563
+ synthesizedManifestFile,
1564
+ JSON.stringify(discoveredManifest),
1565
+ "utf-8",
1566
+ );
1567
+ return await extractFromManifest(
1568
+ synthesizedManifestFile,
1569
+ {},
1570
+ tempDir,
1571
+ allLayersExplodedDir,
1572
+ options,
1573
+ );
1574
+ }
1575
+ if (DEBUG_MODE) {
1576
+ console.log(
1577
+ `Image archive ${fullImageName} successfully exported to directory ${tempDir}`,
1578
+ );
1579
+ }
1580
+ const allBlobs = getAllFiles(blobsDir, "*");
1581
+ for (const ablob of allBlobs) {
1582
+ if (DEBUG_MODE) {
1583
+ console.log(`Extracting ${ablob} to ${allLayersExplodedDir}`);
1584
+ }
1585
+ await extractTar(ablob, allLayersExplodedDir, options);
1586
+ }
1587
+ const lastLayerConfig = {};
1588
+ // Bug #3565. We may not know the work directory, so we have to try and detect them.
1589
+ const lastWorkingDir = "/";
1590
+ const exportData = {
1591
+ manifest,
1592
+ allLayersDir: tempDir,
1593
+ allLayersExplodedDir,
1594
+ lastLayerConfig,
1595
+ lastWorkingDir,
1596
+ };
1597
+ exportData.pkgPathList = getPkgPathList(exportData, lastWorkingDir);
1598
+ return exportData;
1599
+ }
1600
+ console.log(`Unable to extract image archive to ${tempDir}`);
1601
+ options.failOnError && process.exit(1);
1602
+ } catch (_err) {
1603
+ // ignore
1604
+ options.failOnError && process.exit(1);
1605
+ }
1606
+ return undefined;
1607
+ };
1608
+
1609
+ /**
1610
+ * Parse a Docker/containerd manifest file and extract all image layers into a
1611
+ * single merged directory. Resolves the last layer's config to determine the
1612
+ * container's working directory, and builds the package path list for
1613
+ * subsequent analysis.
1614
+ *
1615
+ * @param {string} manifestFile Path to the manifest.json (or index.json) file
1616
+ * @param {Object} localData Local image inspect data (e.g. from `docker inspect`)
1617
+ * @param {string} tempDir Temporary directory that holds the unpacked image
1618
+ * @param {string} allLayersExplodedDir Directory where all layers are merged
1619
+ * @param {Object} options CLI options (uses `options.failOnError`)
1620
+ * @returns {Promise<Object>} Export data object containing `manifest`,
1621
+ * `allLayersDir`, `allLayersExplodedDir`, `lastLayerConfig`,
1622
+ * `lastWorkingDir`, `binPaths`, and `pkgPathList`
1623
+ */
1624
+ export const extractFromManifest = async (
1625
+ manifestFile,
1626
+ localData,
1627
+ tempDir,
1628
+ allLayersExplodedDir,
1629
+ options,
1630
+ ) => {
1631
+ // Example of manifests
1632
+ // [{"Config":"blobs/sha256/dedc100afa8d6718f5ac537730dd4a5ceea3563e695c90f1a8ac6df32c4cb291","RepoTags":["shiftleft/core:latest"],"Layers":["blobs/sha256/eaead16dc43bb8811d4ff450935d607f9ba4baffda4fc110cc402fa43f601d83","blobs/sha256/2039af03c0e17a3025b989335e9414149577fa09e7d0dcbee80155333639d11f"]}]
1633
+ // {"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.docker.distribution.manifest.list.v2+json","digest":"sha256:7706ac20c7587081dc7a00e0ec65a6633b0bb3788e0048a3e971d3eae492db63","size":318,"annotations":{"io.containerd.image.name":"docker.io/shiftleft/scan-slim:latest","org.opencontainers.image.ref.name":"latest"}}]}
1634
+ let manifest = readArchiveJson(manifestFile);
1635
+ let lastLayerConfig = {};
1636
+ let lastLayerConfigFile = "";
1637
+ let selectedManifest;
1638
+ let lastWorkingDir = "";
1639
+ manifest = resolveArchiveManifest(manifest, tempDir);
1640
+ if (Array.isArray(manifest)) {
1641
+ selectedManifest = manifest[manifest.length - 1];
1642
+ if (manifest.length !== 1) {
1643
+ if (DEBUG_MODE) {
1644
+ console.log(
1645
+ "Multiple image tags was downloaded. Only the last one would be used",
1646
+ );
1647
+ console.log(selectedManifest);
1648
+ }
1649
+ }
1650
+ const layers = selectedManifest?.Layers || [];
1651
+ if (!layers.length && safeExistsSync(tempDir)) {
1652
+ const blobFiles = readdirSync(join(tempDir, "blobs", "sha256"));
1653
+ if (blobFiles?.length) {
1654
+ for (const blobf of blobFiles) {
1655
+ layers.push(join("blobs", "sha256", blobf));
1656
+ }
1657
+ }
1658
+ }
1659
+ const lastLayer = layers[layers.length - 1];
1660
+ const layerPhase = ui.phase("Extracting image layers");
1661
+ let extractedLayers = 0;
1662
+ for (const layer of layers) {
1663
+ layerPhase.progress(extractedLayers, layers.length);
1664
+ layerPhase.detail(basename(layer));
1665
+ extractedLayers += 1;
1666
+ try {
1667
+ if (!lstatSync(join(tempDir, layer)).isFile()) {
1668
+ console.log(
1669
+ `Skipping layer ${layer} since it is not a readable file.`,
1670
+ );
1671
+ continue;
1672
+ }
1673
+ } catch (_e) {
1674
+ console.log(`Skipping layer ${layer} since it is not a readable file.`);
1675
+ continue;
1676
+ }
1677
+ if (DEBUG_MODE) {
1678
+ console.log(`Extracting layer ${layer} to ${allLayersExplodedDir}`);
1679
+ }
1680
+ try {
1681
+ await extractTar(join(tempDir, layer), allLayersExplodedDir, options);
1682
+ } catch (err) {
1683
+ if (err.code === "TAR_BAD_ARCHIVE") {
1684
+ if (DEBUG_MODE) {
1685
+ console.log(`Layer ${layer} is empty.`);
1686
+ }
1687
+ } else {
1688
+ console.log(err);
1689
+ options.failOnError && process.exit(1);
1690
+ }
1691
+ }
1692
+ }
1693
+ layerPhase.succeed(
1694
+ `${layers.length} layer${layers.length === 1 ? "" : "s"}`,
1695
+ );
1696
+ if (selectedManifest?.Config) {
1697
+ lastLayerConfigFile = join(tempDir, selectedManifest.Config);
1698
+ }
1699
+ if (!lastLayerConfigFile && lastLayer?.includes("layer.tar")) {
1700
+ lastLayerConfigFile = join(
1701
+ tempDir,
1702
+ lastLayer.replace("layer.tar", "json"),
1703
+ );
1704
+ }
1705
+ if (lastLayerConfigFile && safeExistsSync(lastLayerConfigFile)) {
1706
+ try {
1707
+ lastLayerConfig = JSON.parse(
1708
+ readFileSync(lastLayerConfigFile, {
1709
+ encoding: "utf-8",
1710
+ }),
1711
+ );
1712
+ lastWorkingDir = lastLayerConfig.config?.WorkingDir
1713
+ ? join(allLayersExplodedDir, lastLayerConfig.config.WorkingDir)
1714
+ : "";
1715
+ } catch (_err) {
1716
+ options.failOnError && process.exit(1);
1717
+ }
1718
+ }
1719
+ }
1720
+ const inspectData = localData?.Config
1721
+ ? localData
1722
+ : lastLayerConfig?.config
1723
+ ? {
1724
+ ...localData,
1725
+ Config: lastLayerConfig.config,
1726
+ }
1727
+ : localData;
1728
+ const binPaths = extractPathEnv(inspectData?.Config?.Env);
1729
+ const exportData = {
1730
+ inspectData,
1731
+ manifest,
1732
+ allLayersDir: tempDir,
1733
+ allLayersExplodedDir,
1734
+ lastLayerConfig,
1735
+ lastWorkingDir,
1736
+ binPaths,
1737
+ };
1738
+ exportData.pkgPathList = getPkgPathList(exportData, lastWorkingDir);
1739
+ return exportData;
1740
+ };
1741
+
1742
+ /**
1743
+ * Method to export a container image by using the export feature in docker or podman service.
1744
+ * Returns the location of the layers with additional packages related metadata
1745
+ */
1746
+ export const exportImage = async (fullImageName, options) => {
1747
+ // Safely ignore local directories
1748
+ if (
1749
+ !fullImageName ||
1750
+ fullImageName === "." ||
1751
+ safeExistsSync(resolve(fullImageName))
1752
+ ) {
1753
+ return undefined;
1754
+ }
1755
+ if (isDryRun) {
1756
+ const imageDetails = parseImageName(fullImageName);
1757
+ const requestedRegistryRef = imageDetails.registry
1758
+ ? imageDetails.repo
1759
+ ? `${imageDetails.registry}/${imageDetails.repo}`
1760
+ : imageDetails.registry
1761
+ : DOCKER_HUB_REGISTRY;
1762
+ await getConnection({}, requestedRegistryRef);
1763
+ recordActivity({
1764
+ kind: "container",
1765
+ reason:
1766
+ "Dry run mode blocks container image pull, save, and export operations.",
1767
+ status: "blocked",
1768
+ target: fullImageName,
1769
+ });
1770
+ return undefined;
1771
+ }
1772
+ // Try to get the data locally first
1773
+ const localData = await getImage(fullImageName);
1774
+ if (!localData) {
1775
+ return undefined;
1776
+ }
1777
+ const { registry, tag, digest } = parseImageName(fullImageName);
1778
+ // Fetch only the latest tag if none is specified
1779
+ if (tag === "" && digest === "") {
1780
+ fullImageName = `${fullImageName}:latest`;
1781
+ }
1782
+ const tempDir = safeMkdtempSync(join(getTmpDir(), "docker-images-"));
1783
+ const allLayersExplodedDir = join(tempDir, "all-layers");
1784
+ let manifestFile = join(tempDir, "manifest.json");
1785
+ // Windows containers use index.json
1786
+ const manifestIndexFile = join(tempDir, "index.json");
1787
+ // On Windows or on mac with Rancher Desktop, fallback to invoking cli
1788
+ if (needsCliFallback()) {
1789
+ const imageTarFile = join(tempDir, "image.tar");
1790
+ const dockerCmd = getContainerCliCmd();
1791
+ console.log(
1792
+ `About to export image ${fullImageName} to ${imageTarFile} using ${dockerCmd} cli`,
1793
+ );
1794
+ const result = safeSpawnSync(dockerCmd, [
1795
+ "save",
1796
+ "-o",
1797
+ imageTarFile,
1798
+ fullImageName,
1799
+ ]);
1800
+ if (result.status !== 0 || result.error) {
1801
+ if (result.stdout || result.stderr) {
1802
+ console.log(result.stdout, result.stderr);
1803
+ }
1804
+ return localData;
1805
+ }
1806
+ await extractTar(imageTarFile, tempDir, options);
1807
+ if (DEBUG_MODE) {
1808
+ console.log(`Cleaning up ${imageTarFile}`);
1809
+ }
1810
+ safeRmSync(imageTarFile, { force: true });
1811
+ } else {
1812
+ const client = await getConnection({}, registry);
1813
+ try {
1814
+ if (DEBUG_MODE) {
1815
+ if (registry?.trim().length) {
1816
+ console.log(
1817
+ `About to export image ${fullImageName} from ${registry} to ${tempDir}`,
1818
+ );
1819
+ } else {
1820
+ console.log(`About to export image ${fullImageName} to ${tempDir}`);
1821
+ }
1822
+ }
1823
+ await stream.pipeline(
1824
+ client.stream(`images/${fullImageName}/get`),
1825
+ x({
1826
+ sync: false,
1827
+ preserveOwner: false,
1828
+ noMtime: true,
1829
+ noChmod: true,
1830
+ strict: !NON_STRICT_TAR_EXTRACT,
1831
+ C: tempDir,
1832
+ portable: true,
1833
+ unlink: true,
1834
+ onwarn: handleTarWarning,
1835
+ onReadEntry: handleAbsolutePath,
1836
+ filter: tarFilter,
1837
+ }),
1838
+ );
1839
+ } catch (_err) {
1840
+ if (localData?.Id) {
1841
+ console.log(`Retrying with ${localData.Id}`);
1842
+ try {
1843
+ await stream.pipeline(
1844
+ client.stream(`images/${localData.Id}/get`),
1845
+ x({
1846
+ sync: true,
1847
+ preserveOwner: false,
1848
+ noMtime: true,
1849
+ noChmod: true,
1850
+ strict: !NON_STRICT_TAR_EXTRACT,
1851
+ C: tempDir,
1852
+ portable: true,
1853
+ onwarn: handleTarWarning,
1854
+ onReadEntry: handleAbsolutePath,
1855
+ filter: tarFilter,
1856
+ }),
1857
+ );
1858
+ } catch (_err) {
1859
+ // ignore
1860
+ }
1861
+ }
1862
+ }
1863
+ }
1864
+ // Continue with extracting the layers
1865
+ if (safeExistsSync(tempDir)) {
1866
+ if (safeExistsSync(manifestFile)) {
1867
+ // This is fine
1868
+ } else if (safeExistsSync(manifestIndexFile)) {
1869
+ manifestFile = manifestIndexFile;
1870
+ } else {
1871
+ console.log(
1872
+ `Manifest file ${manifestFile} was not found after export at ${tempDir}`,
1873
+ );
1874
+ return undefined;
1875
+ }
1876
+ if (DEBUG_MODE) {
1877
+ console.log(
1878
+ `Image ${fullImageName} successfully exported to directory ${tempDir}`,
1879
+ );
1880
+ }
1881
+ safeMkdirSync(allLayersExplodedDir);
1882
+ return await extractFromManifest(
1883
+ manifestFile,
1884
+ localData,
1885
+ tempDir,
1886
+ allLayersExplodedDir,
1887
+ options,
1888
+ );
1889
+ }
1890
+ console.log(`Unable to export image to ${tempDir}`);
1891
+ return undefined;
1892
+ };
1893
+
1894
+ /**
1895
+ * Method to retrieve path list for system-level packages
1896
+ */
1897
+ export const getPkgPathList = (exportData, lastWorkingDir) => {
1898
+ const allLayersExplodedDir = exportData.allLayersExplodedDir;
1899
+ const allLayersDir = exportData.allLayersDir;
1900
+ let pathList = [];
1901
+ let knownSysPaths = [];
1902
+ // Bug #3565. Try and detect the working directory
1903
+ if (lastWorkingDir === "/" || lastWorkingDir === "") {
1904
+ if (DEBUG_MODE) {
1905
+ console.log("Attempting to detect the work directories ...");
1906
+ }
1907
+ const possibleWorkDirs = getDirs(allLayersExplodedDir, "*", true, false);
1908
+ for (const adir of possibleWorkDirs) {
1909
+ if (safeExistsSync(adir) && !lstatSync(adir).isDirectory()) {
1910
+ break;
1911
+ }
1912
+ let ignoreDir = false;
1913
+ for (const nonWorkDirs of [
1914
+ "/var",
1915
+ "/usr",
1916
+ "/mnt",
1917
+ "/opt",
1918
+ "/root",
1919
+ "/home",
1920
+ ]) {
1921
+ if (adir.endsWith(nonWorkDirs)) {
1922
+ ignoreDir = true;
1923
+ break;
1924
+ }
1925
+ }
1926
+ if (!ignoreDir) {
1927
+ knownSysPaths.push(adir);
1928
+ }
1929
+ }
1930
+ if (DEBUG_MODE) {
1931
+ console.log("Possible work directories", knownSysPaths);
1932
+ }
1933
+ }
1934
+ if (allLayersExplodedDir && allLayersExplodedDir !== "") {
1935
+ knownSysPaths = knownSysPaths.concat([
1936
+ join(allLayersExplodedDir, "/usr/local/go"),
1937
+ join(allLayersExplodedDir, "/usr/local/lib"),
1938
+ join(allLayersExplodedDir, "/usr/local/lib64"),
1939
+ join(allLayersExplodedDir, "/opt"),
1940
+ join(allLayersExplodedDir, "/root"),
1941
+ join(allLayersExplodedDir, "/home"),
1942
+ join(allLayersExplodedDir, "/usr/share"),
1943
+ join(allLayersExplodedDir, "/usr/src"),
1944
+ join(allLayersExplodedDir, "/var/www/html"),
1945
+ join(allLayersExplodedDir, "/var/lib"),
1946
+ join(allLayersExplodedDir, "/mnt"),
1947
+ ]);
1948
+ } else if (allLayersExplodedDir === "") {
1949
+ knownSysPaths = knownSysPaths.concat([
1950
+ join(allLayersExplodedDir, "/usr/local/go"),
1951
+ join(allLayersExplodedDir, "/usr/local/lib"),
1952
+ join(allLayersExplodedDir, "/usr/local/lib64"),
1953
+ join(allLayersExplodedDir, "/opt"),
1954
+ join(allLayersExplodedDir, "/root"),
1955
+ join(allLayersExplodedDir, "/usr/share"),
1956
+ join(allLayersExplodedDir, "/usr/src"),
1957
+ join(allLayersExplodedDir, "/var/www/html"),
1958
+ join(allLayersExplodedDir, "/var/lib"),
1959
+ ]);
1960
+ }
1961
+ if (safeExistsSync(join(allLayersDir, "Files"))) {
1962
+ knownSysPaths.push(join(allLayersDir, "Files"));
1963
+ }
1964
+ /*
1965
+ // Too slow
1966
+ if (safeExistsSync(path.join(allLayersDir, "Users"))) {
1967
+ knownSysPaths.push(path.join(allLayersDir, "Users"));
1968
+ }
1969
+ */
1970
+ if (safeExistsSync(join(allLayersDir, "ProgramData"))) {
1971
+ knownSysPaths.push(join(allLayersDir, "ProgramData"));
1972
+ }
1973
+ const pyInstalls = getDirs(allLayersDir, "Python*/", false, false);
1974
+ if (pyInstalls?.length) {
1975
+ for (const pyiPath of pyInstalls) {
1976
+ const pyDirs = getOnlyDirs(pyiPath, "site-packages");
1977
+ if (pyDirs?.length) {
1978
+ pathList = pathList.concat(pyDirs);
1979
+ }
1980
+ }
1981
+ }
1982
+ if (lastWorkingDir && lastWorkingDir !== "") {
1983
+ if (
1984
+ lastWorkingDir !== "/" &&
1985
+ !lastWorkingDir.includes("/opt/") &&
1986
+ !lastWorkingDir.includes("/home/") &&
1987
+ !lastWorkingDir.includes("/root/")
1988
+ ) {
1989
+ knownSysPaths.push(lastWorkingDir);
1990
+ }
1991
+ // Some more common app dirs
1992
+ if (!lastWorkingDir.includes("/app/")) {
1993
+ knownSysPaths.push(join(allLayersExplodedDir, "/app"));
1994
+ }
1995
+ if (!lastWorkingDir.includes("/layers/")) {
1996
+ knownSysPaths.push(join(allLayersExplodedDir, "/layers"));
1997
+ }
1998
+ if (!lastWorkingDir.includes("/data/")) {
1999
+ knownSysPaths.push(join(allLayersExplodedDir, "/data"));
2000
+ }
2001
+ if (!lastWorkingDir.includes("/srv/")) {
2002
+ knownSysPaths.push(join(allLayersExplodedDir, "/srv"));
2003
+ }
2004
+ } else {
2005
+ // Bug #3426
2006
+ knownSysPaths.push(join(allLayersExplodedDir, "/app"));
2007
+ }
2008
+ // Known to cause EACCESS error
2009
+ knownSysPaths.push(join(allLayersExplodedDir, "/usr/lib"));
2010
+ knownSysPaths.push(join(allLayersExplodedDir, "/usr/lib64"));
2011
+ // Build path list
2012
+ const ecoDirNames = [
2013
+ "node_modules",
2014
+ "site-packages",
2015
+ "gems",
2016
+ ".cargo",
2017
+ ".composer",
2018
+ ];
2019
+ for (const wpath of knownSysPaths) {
2020
+ pathList = pathList.concat(wpath);
2021
+ const ecoDirs = getOnlyDirsMulti(wpath, ecoDirNames);
2022
+ if (ecoDirs.node_modules.length) {
2023
+ pathList.push(ecoDirs.node_modules[0]);
2024
+ }
2025
+ if (ecoDirs["site-packages"].length) {
2026
+ pathList = pathList.concat(ecoDirs["site-packages"]);
2027
+ }
2028
+ if (ecoDirs.gems.length) {
2029
+ pathList.push(ecoDirs.gems[0]);
2030
+ }
2031
+ if (ecoDirs[".cargo"].length) {
2032
+ pathList = pathList.concat(ecoDirs[".cargo"]);
2033
+ }
2034
+ if (ecoDirs[".composer"].length) {
2035
+ pathList = pathList.concat(ecoDirs[".composer"]);
2036
+ }
2037
+ }
2038
+ pathList = Array.from(new Set(pathList)).sort();
2039
+ if (DEBUG_MODE) {
2040
+ console.log("pathList", pathList);
2041
+ }
2042
+ return pathList;
2043
+ };
2044
+
2045
+ /**
2046
+ * Remove a container image from the local Docker / Podman daemon.
2047
+ *
2048
+ * @param {string} fullImageName Full image name including tag or digest (e.g. "ubuntu:22.04")
2049
+ * @param {boolean} [force=false] When `true`, force-remove the image even if it is in use
2050
+ * @returns {Promise<Buffer|undefined>} Raw response buffer from the daemon, or
2051
+ * `undefined` if no daemon connection is available
2052
+ */
2053
+ export const removeImage = async (fullImageName, force = false) => {
2054
+ return await makeRequest(`images/${fullImageName}?force=${force}`, "DELETE");
2055
+ };
2056
+
2057
+ /**
2058
+ * Retrieve a base64url-encoded authentication token for a registry server by
2059
+ * invoking the `docker-credential-<exeSuffix>` credential helper binary.
2060
+ * Results are cached in `registry_auth_keys` to avoid redundant subprocess
2061
+ * calls.
2062
+ *
2063
+ * @param {string} exeSuffix Credential helper name suffix (e.g. "osxkeychain", "wincred", "pass")
2064
+ * @param {string} serverAddress Registry server address (e.g. "https://index.docker.io/v1/")
2065
+ * @returns {string|undefined} Base64url-encoded JSON auth token, or `undefined`
2066
+ * if the helper is unavailable or returns an error
2067
+ */
2068
+ export const getCredsFromHelper = (exeSuffix, serverAddress) => {
2069
+ const cacheKey = `${exeSuffix}:${normalizeRegistryReference(serverAddress) ?? serverAddress}`;
2070
+ if (registry_auth_keys[cacheKey]) {
2071
+ return registry_auth_keys[cacheKey];
2072
+ }
2073
+ let credHelperExe = `docker-credential-${exeSuffix}`;
2074
+ if (isWin) {
2075
+ credHelperExe = `${credHelperExe}.exe`;
2076
+ }
2077
+ const result = safeSpawnSync(credHelperExe, ["get"], {
2078
+ input: serverAddress,
2079
+ });
2080
+ if (result.status !== 0 || result.error) {
2081
+ if (result.stdout || result.stderr) {
2082
+ console.log(result.stdout, result.stderr);
2083
+ }
2084
+ } else if (result.stdout) {
2085
+ const cmdOutput = Buffer.from(result.stdout).toString();
2086
+ try {
2087
+ const dockerUser = readEnvironmentVariable("DOCKER_USER", {
2088
+ sensitive: true,
2089
+ });
2090
+ const dockerPassword = readEnvironmentVariable("DOCKER_PASSWORD", {
2091
+ sensitive: true,
2092
+ });
2093
+ const authPayload = JSON.parse(cmdOutput);
2094
+ const fixedAuthPayload = {
2095
+ username: authPayload.username || authPayload.Username || dockerUser,
2096
+ password: authPayload.password || authPayload.Secret || dockerPassword,
2097
+ email: authPayload.email || authPayload.username || dockerUser,
2098
+ serveraddress: serverAddress,
2099
+ };
2100
+ const authKey = toBase64Url(JSON.stringify(fixedAuthPayload));
2101
+ registry_auth_keys[cacheKey] = authKey;
2102
+ return authKey;
2103
+ } catch (_err) {
2104
+ return undefined;
2105
+ }
2106
+ }
2107
+ return undefined;
2108
+ };
2109
+
2110
+ /**
2111
+ * Append skipped source-file entries to the `internal:SrcFile` properties of matching
2112
+ * components. A component matches when its `oci:SrcImage` property value
2113
+ * equals the skipped image's `image` field and the source file path is not
2114
+ * already listed.
2115
+ *
2116
+ * @param {Array<{image: string, src: string}>} skippedImageSrcs List of skipped image/source pairs
2117
+ * @param {Array<Object>} components CycloneDX component objects to update in place
2118
+ */
2119
+ export const addSkippedSrcFiles = (skippedImageSrcs, components) => {
2120
+ for (const skippedImage of skippedImageSrcs) {
2121
+ for (const co of components) {
2122
+ const srcFileValues = [];
2123
+ let srcImageValue;
2124
+ co.properties.forEach((property) => {
2125
+ if (property.name === "oci:SrcImage") {
2126
+ srcImageValue = property.value;
2127
+ }
2128
+
2129
+ if (property.name === "internal:SrcFile") {
2130
+ srcFileValues.push(property.value);
2131
+ }
2132
+ });
2133
+
2134
+ if (
2135
+ srcImageValue === skippedImage.image &&
2136
+ !srcFileValues.includes(skippedImage.src)
2137
+ ) {
2138
+ co.properties = co.properties.concat({
2139
+ name: "internal:SrcFile",
2140
+ value: skippedImage.src,
2141
+ });
2142
+ }
2143
+ }
2144
+ }
2145
+ };