@cdxgen/cdxgen 0.0.1 → 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (722) hide show
  1. package/README.md +921 -87
  2. package/bin/audit.js +239 -0
  3. package/bin/cdxgen.js +2324 -0
  4. package/bin/convert.js +213 -0
  5. package/bin/evinse.js +353 -0
  6. package/bin/hbom.js +495 -0
  7. package/bin/repl.js +2224 -0
  8. package/bin/sign.js +195 -0
  9. package/bin/tracebom.js +295 -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 +1833 -0
  85. package/lib/cli/cliOptions.js +513 -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 +142 -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 +876 -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 +575 -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 +1789 -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 +277 -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 +263 -0
  215. package/lib/inventory/traceRunner.js +439 -0
  216. package/lib/inventory/unicodeScan.js +147 -0
  217. package/lib/managers/binary.js +3307 -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 +315 -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 +28 -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 +120 -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 +19 -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 +76 -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,3307 @@
1
+ import {
2
+ lstatSync,
3
+ readdirSync,
4
+ readFileSync,
5
+ realpathSync,
6
+ statSync,
7
+ } from "node:fs";
8
+ import { platform as _platform, homedir } from "node:os";
9
+ import { basename, dirname, extname, join, relative, resolve } from "node:path";
10
+
11
+ import { build, Purl } from "@cdxgen/cdx-purl";
12
+
13
+ import {
14
+ DEBUG_MODE,
15
+ hasDangerousUnicode,
16
+ isDryRun,
17
+ readEnvironmentVariable,
18
+ recordActivity,
19
+ recordSymlinkResolution,
20
+ } from "../core/activity.js";
21
+ import { extractPathEnv } from "../core/env.js";
22
+ import {
23
+ getTmpDir,
24
+ multiChecksumFile,
25
+ safeExistsSync,
26
+ safeMkdirSync,
27
+ safeMkdtempSync,
28
+ safeRmSync,
29
+ safeSpawnSync,
30
+ } from "../core/fs.js";
31
+ import { mapWithConcurrency } from "../core/parallel.js";
32
+ import { isValidDriveRoot } from "../core/paths.js";
33
+ import { isCycloneDxComponentTypeEnabled } from "../inventory/bomUtils.js";
34
+ import { createContainerRiskProperties } from "../inventory/containerRisk.js";
35
+ import {
36
+ attachIdentityTools,
37
+ extractToolRefs,
38
+ } from "../inventory/evidenceUtils.js";
39
+ import { createGtfoBinsProperties } from "../inventory/gtfobins.js";
40
+ import { OS_DISTRO_ALIAS, readOsRelease } from "../inventory/osinfo.js";
41
+ import {
42
+ collectExecutables,
43
+ collectSharedLibs,
44
+ } from "../inventory/osPackageResolver.js";
45
+ import {
46
+ resolveCdxgenPlugins,
47
+ resolvePluginBinary,
48
+ setPluginsPathEnv,
49
+ } from "../inventory/plugins.js";
50
+ import { genericPurl, tryBuildPurl } from "../inventory/purl.js";
51
+ import {
52
+ adjustLicenseInformation,
53
+ findLicenseId,
54
+ isSpdxLicenseExpression,
55
+ } from "../inventory/spdx.js";
56
+ import { getDirs } from "./containerutils.js";
57
+
58
+ const isWin = _platform() === "win32";
59
+ const OS_PURL_TYPES = ["deb", "apk", "rpm", "alpm", "qpkg"];
60
+ // Canonical distro vendor namespace. Trivy emits pkg:rpm/alma/... but the
61
+ // vulnerability feeds (AppThreat vuln-list / dep-scan) key AlmaLinux under
62
+ // "almalinux"; align so the purls match. Rocky is emitted as "rocky" already.
63
+ const OS_DISTRO_NAMESPACE_ALIAS = {
64
+ alma: "almalinux",
65
+ "rocky-linux": "rocky",
66
+ };
67
+ const pluginRuntime = setPluginsPathEnv(resolveCdxgenPlugins());
68
+ const platform = pluginRuntime.platform;
69
+ const CDXGEN_PLUGINS_DIR = pluginRuntime.pluginsDir;
70
+ const PLUGIN_MANIFEST_FILE = pluginRuntime.pluginManifestFile;
71
+ let pluginManifest;
72
+
73
+ const MAX_PLUGIN_MANIFEST_BYTES = 1024 * 1024;
74
+ const MAX_PLUGIN_MANIFEST_PLUGINS = 32;
75
+ const MAX_PLUGIN_COMPONENT_HASHES = 16;
76
+ const MAX_PLUGIN_COMPONENT_REFERENCES = 32;
77
+ const MAX_PLUGIN_COMPONENT_PROPERTIES = 128;
78
+ const MAX_PLUGIN_COMPONENT_LICENSES = 8;
79
+ const MAX_PLUGIN_STRING_LENGTH = 4096;
80
+ const MAX_PLUGIN_LONG_STRING_LENGTH = 16384;
81
+
82
+ function sanitizeManifestString(value, maxLength = MAX_PLUGIN_STRING_LENGTH) {
83
+ if (typeof value !== "string") {
84
+ return undefined;
85
+ }
86
+ const trimmedValue = value.trim();
87
+ if (!trimmedValue || trimmedValue.length > maxLength) {
88
+ return undefined;
89
+ }
90
+ return trimmedValue;
91
+ }
92
+
93
+ function sanitizeManifestObjectList(values, limit, mapper) {
94
+ if (!Array.isArray(values) || !values.length) {
95
+ return undefined;
96
+ }
97
+ const mappedValues = values
98
+ .slice(0, limit)
99
+ .map((value) => mapper(value))
100
+ .filter(Boolean);
101
+ return mappedValues.length ? mappedValues : undefined;
102
+ }
103
+
104
+ function sanitizeManifestHash(hash) {
105
+ const alg = sanitizeManifestString(hash?.alg, 64);
106
+ const content = sanitizeManifestString(hash?.content, 512);
107
+ if (!alg || !content) {
108
+ return undefined;
109
+ }
110
+ return { alg, content };
111
+ }
112
+
113
+ function sanitizeManifestProperty(property) {
114
+ const name = sanitizeManifestString(property?.name, 256);
115
+ const value = sanitizeManifestString(
116
+ property?.value,
117
+ MAX_PLUGIN_LONG_STRING_LENGTH,
118
+ );
119
+ if (!name || !value) {
120
+ return undefined;
121
+ }
122
+ return { name, value };
123
+ }
124
+
125
+ function sanitizeManifestExternalReference(reference) {
126
+ const type = sanitizeManifestString(reference?.type, 64);
127
+ const url = sanitizeManifestString(
128
+ reference?.url,
129
+ MAX_PLUGIN_LONG_STRING_LENGTH,
130
+ );
131
+ if (!type || !url) {
132
+ return undefined;
133
+ }
134
+ const sanitizedReference = { type, url };
135
+ const comment = sanitizeManifestString(reference?.comment, 512);
136
+ if (comment) {
137
+ sanitizedReference.comment = comment;
138
+ }
139
+ return sanitizedReference;
140
+ }
141
+
142
+ function sanitizeManifestLicense(licenseEntry) {
143
+ const licenseId = sanitizeManifestString(licenseEntry?.license?.id, 128);
144
+ const licenseName = sanitizeManifestString(licenseEntry?.license?.name, 256);
145
+ const licenseUrl = sanitizeManifestString(
146
+ licenseEntry?.license?.url,
147
+ MAX_PLUGIN_LONG_STRING_LENGTH,
148
+ );
149
+ if (!licenseId && !licenseName) {
150
+ return undefined;
151
+ }
152
+ const license = {};
153
+ if (licenseId) {
154
+ license.id = licenseId;
155
+ }
156
+ if (licenseName) {
157
+ license.name = licenseName;
158
+ }
159
+ if (licenseUrl) {
160
+ license.url = licenseUrl;
161
+ }
162
+ return { license };
163
+ }
164
+
165
+ function sanitizeManifestComponent(component, fallbackName) {
166
+ if (!component || typeof component !== "object") {
167
+ return undefined;
168
+ }
169
+ const sanitizedComponent = {};
170
+ const stringFields = {
171
+ group: 256,
172
+ name: 256,
173
+ version: 256,
174
+ description: MAX_PLUGIN_LONG_STRING_LENGTH,
175
+ publisher: 256,
176
+ purl: MAX_PLUGIN_LONG_STRING_LENGTH,
177
+ "bom-ref": MAX_PLUGIN_LONG_STRING_LENGTH,
178
+ type: 64,
179
+ };
180
+ for (const [field, maxLength] of Object.entries(stringFields)) {
181
+ const sanitizedValue = sanitizeManifestString(component[field], maxLength);
182
+ if (sanitizedValue) {
183
+ sanitizedComponent[field] = sanitizedValue;
184
+ }
185
+ }
186
+ if (!sanitizedComponent.name && fallbackName) {
187
+ sanitizedComponent.name = fallbackName;
188
+ }
189
+ if (!sanitizedComponent.name || !sanitizedComponent["bom-ref"]) {
190
+ return undefined;
191
+ }
192
+ const hashes = sanitizeManifestObjectList(
193
+ component.hashes,
194
+ MAX_PLUGIN_COMPONENT_HASHES,
195
+ sanitizeManifestHash,
196
+ );
197
+ const externalReferences = sanitizeManifestObjectList(
198
+ component.externalReferences,
199
+ MAX_PLUGIN_COMPONENT_REFERENCES,
200
+ sanitizeManifestExternalReference,
201
+ );
202
+ const properties = sanitizeManifestObjectList(
203
+ component.properties,
204
+ MAX_PLUGIN_COMPONENT_PROPERTIES,
205
+ sanitizeManifestProperty,
206
+ );
207
+ const licenses = sanitizeManifestObjectList(
208
+ component.licenses,
209
+ MAX_PLUGIN_COMPONENT_LICENSES,
210
+ sanitizeManifestLicense,
211
+ );
212
+ if (hashes) {
213
+ sanitizedComponent.hashes = hashes;
214
+ }
215
+ if (externalReferences) {
216
+ sanitizedComponent.externalReferences = externalReferences;
217
+ }
218
+ if (properties) {
219
+ sanitizedComponent.properties = properties;
220
+ }
221
+ if (licenses) {
222
+ sanitizedComponent.licenses = licenses;
223
+ }
224
+ return sanitizedComponent;
225
+ }
226
+
227
+ function sanitizePluginManifest(manifest) {
228
+ if (!manifest || typeof manifest !== "object") {
229
+ return null;
230
+ }
231
+ const sanitizedManifest = {
232
+ plugins: [],
233
+ };
234
+ const generatedAt = sanitizeManifestString(manifest.generatedAt, 128);
235
+ if (generatedAt) {
236
+ sanitizedManifest.generatedAt = generatedAt;
237
+ }
238
+ if (manifest.package && typeof manifest.package === "object") {
239
+ const sanitizedPackage = {};
240
+ for (const [field, maxLength] of Object.entries({
241
+ name: 256,
242
+ version: 256,
243
+ repository: MAX_PLUGIN_LONG_STRING_LENGTH,
244
+ homepage: MAX_PLUGIN_LONG_STRING_LENGTH,
245
+ })) {
246
+ const sanitizedValue = sanitizeManifestString(
247
+ manifest.package[field],
248
+ maxLength,
249
+ );
250
+ if (sanitizedValue) {
251
+ sanitizedPackage[field] = sanitizedValue;
252
+ }
253
+ }
254
+ if (Object.keys(sanitizedPackage).length) {
255
+ sanitizedManifest.package = sanitizedPackage;
256
+ }
257
+ }
258
+ for (const pluginEntry of Array.isArray(manifest.plugins)
259
+ ? manifest.plugins.slice(0, MAX_PLUGIN_MANIFEST_PLUGINS)
260
+ : []) {
261
+ const name = sanitizeManifestString(pluginEntry?.name, 128);
262
+ const component = sanitizeManifestComponent(pluginEntry?.component, name);
263
+ if (!name || !component) {
264
+ continue;
265
+ }
266
+ const sanitizedPlugin = { name, component };
267
+ for (const [field, maxLength] of Object.entries({
268
+ version: 256,
269
+ binaryPath: MAX_PLUGIN_LONG_STRING_LENGTH,
270
+ sbomFile: MAX_PLUGIN_LONG_STRING_LENGTH,
271
+ sha256: 256,
272
+ })) {
273
+ const sanitizedValue = sanitizeManifestString(
274
+ pluginEntry?.[field],
275
+ maxLength,
276
+ );
277
+ if (sanitizedValue) {
278
+ sanitizedPlugin[field] = sanitizedValue;
279
+ }
280
+ }
281
+ sanitizedManifest.plugins.push(sanitizedPlugin);
282
+ }
283
+ return sanitizedManifest.plugins.length ? sanitizedManifest : null;
284
+ }
285
+
286
+ function loadPluginManifest() {
287
+ if (pluginManifest !== undefined) {
288
+ return pluginManifest;
289
+ }
290
+ if (!PLUGIN_MANIFEST_FILE) {
291
+ pluginManifest = null;
292
+ return pluginManifest;
293
+ }
294
+ try {
295
+ const manifestRealPath = realpathSync(PLUGIN_MANIFEST_FILE);
296
+ const manifestDirectory = dirname(manifestRealPath);
297
+ const expectedManifestDirectory = realpathSync(CDXGEN_PLUGINS_DIR);
298
+ const manifestStats = statSync(manifestRealPath);
299
+ if (
300
+ basename(manifestRealPath) !== "plugins-manifest.json" ||
301
+ manifestDirectory !== expectedManifestDirectory ||
302
+ !manifestStats.isFile() ||
303
+ manifestStats.size > MAX_PLUGIN_MANIFEST_BYTES
304
+ ) {
305
+ pluginManifest = null;
306
+ return pluginManifest;
307
+ }
308
+ pluginManifest = sanitizePluginManifest(
309
+ JSON.parse(readFileSync(manifestRealPath, { encoding: "utf-8" })),
310
+ );
311
+ } catch (_err) {
312
+ pluginManifest = null;
313
+ }
314
+ return pluginManifest;
315
+ }
316
+
317
+ function cloneSerializable(value) {
318
+ if (!value || typeof value !== "object") {
319
+ return value;
320
+ }
321
+ return JSON.parse(JSON.stringify(value));
322
+ }
323
+
324
+ function getPluginManifestEntry(toolName) {
325
+ const manifest = loadPluginManifest();
326
+ if (!manifest?.plugins?.length) {
327
+ return undefined;
328
+ }
329
+ return manifest.plugins.find((entry) => entry?.name === toolName);
330
+ }
331
+
332
+ function mergeToolComponent(manifestComponent, existingComponent) {
333
+ if (!manifestComponent) {
334
+ return cloneSerializable(existingComponent);
335
+ }
336
+ if (!existingComponent) {
337
+ return cloneSerializable(manifestComponent);
338
+ }
339
+ const merged = {
340
+ ...cloneSerializable(manifestComponent),
341
+ ...cloneSerializable(existingComponent),
342
+ };
343
+ for (const key of [
344
+ "group",
345
+ "name",
346
+ "version",
347
+ "description",
348
+ "publisher",
349
+ "purl",
350
+ "bom-ref",
351
+ "type",
352
+ ]) {
353
+ if (manifestComponent?.[key]) {
354
+ merged[key] = manifestComponent[key];
355
+ }
356
+ }
357
+ merged.hashes = manifestComponent?.hashes?.length
358
+ ? manifestComponent.hashes
359
+ : existingComponent?.hashes;
360
+ merged.externalReferences = uniqueExternalReferences(
361
+ (manifestComponent?.externalReferences || []).concat(
362
+ existingComponent?.externalReferences || [],
363
+ ),
364
+ );
365
+ merged.properties = uniqueProperties(
366
+ (manifestComponent?.properties || []).concat(
367
+ existingComponent?.properties || [],
368
+ ),
369
+ );
370
+ merged.licenses =
371
+ manifestComponent?.licenses?.length || !existingComponent?.licenses?.length
372
+ ? manifestComponent?.licenses
373
+ : existingComponent.licenses;
374
+ merged.evidence = manifestComponent?.evidence || existingComponent?.evidence;
375
+ return merged;
376
+ }
377
+
378
+ function uniqueExternalReferences(references) {
379
+ const seen = new Set();
380
+ const uniqueValues = [];
381
+ for (const reference of references || []) {
382
+ if (!reference?.url || !reference?.type) {
383
+ continue;
384
+ }
385
+ const key = `${reference.type}\u0000${reference.url}`;
386
+ if (seen.has(key)) {
387
+ continue;
388
+ }
389
+ seen.add(key);
390
+ uniqueValues.push(reference);
391
+ }
392
+ return uniqueValues;
393
+ }
394
+
395
+ /**
396
+ * Look up plugin manifest entries for the given tool names and return their
397
+ * CycloneDX tool component objects, de-duplicated by bom-ref.
398
+ *
399
+ * @param {string[]} [toolNames=[]] Plugin tool names to resolve.
400
+ * @returns {Object[]} De-duplicated CycloneDX tool component objects.
401
+ */
402
+ export function getPluginToolComponents(toolNames = []) {
403
+ const components = [];
404
+ const seenRefs = new Set();
405
+ for (const toolName of uniqueSortedStrings(toolNames)) {
406
+ const component = cloneSerializable(
407
+ getPluginManifestEntry(toolName)?.component,
408
+ );
409
+ if (!component?.["bom-ref"] || seenRefs.has(component["bom-ref"])) {
410
+ continue;
411
+ }
412
+ seenRefs.add(component["bom-ref"]);
413
+ components.push(component);
414
+ }
415
+ return components;
416
+ }
417
+
418
+ function enrichToolComponents(existingTools = [], toolNames = []) {
419
+ const manifestTools = getPluginToolComponents(toolNames);
420
+ if (!existingTools?.length) {
421
+ return manifestTools;
422
+ }
423
+ const toolMap = new Map();
424
+ for (const tool of existingTools) {
425
+ if (!tool) {
426
+ continue;
427
+ }
428
+ toolMap.set(tool["bom-ref"] || tool.name || JSON.stringify(tool), tool);
429
+ }
430
+ for (const manifestTool of manifestTools) {
431
+ const matchKey = Array.from(toolMap.keys()).find((key) => {
432
+ const existing = toolMap.get(key);
433
+ return (
434
+ existing?.["bom-ref"] === manifestTool["bom-ref"] ||
435
+ existing?.name === manifestTool.name
436
+ );
437
+ });
438
+ if (matchKey) {
439
+ toolMap.set(
440
+ matchKey,
441
+ mergeToolComponent(manifestTool, toolMap.get(matchKey)),
442
+ );
443
+ continue;
444
+ }
445
+ toolMap.set(manifestTool["bom-ref"], manifestTool);
446
+ }
447
+ return Array.from(toolMap.values());
448
+ }
449
+
450
+ const TRIVY_BIN = resolvePluginBinary("trivy", pluginRuntime);
451
+ const CARGO_AUDITABLE_BIN = resolvePluginBinary(
452
+ "cargo-auditable",
453
+ pluginRuntime,
454
+ );
455
+ const OSQUERY_BIN = resolvePluginBinary("osquery", pluginRuntime);
456
+ const DOSAI_BIN = resolvePluginBinary("dosai", pluginRuntime);
457
+ const TRUSTINSPECTOR_BIN = resolvePluginBinary("trustinspector", pluginRuntime);
458
+
459
+ // Blint bin
460
+ const BLINT_BIN = readEnvironmentVariable("BLINT_CMD") || "blint";
461
+
462
+ // sourcekitten
463
+ const SOURCEKITTEN_BIN = resolvePluginBinary("sourcekitten", pluginRuntime);
464
+
465
+ // TODO: Move the lists to a config file
466
+ const COMMON_RUNTIMES = [
467
+ "java",
468
+ "node",
469
+ "nodejs",
470
+ "nodejs-current",
471
+ "deno",
472
+ "bun",
473
+ "python",
474
+ "python3",
475
+ "ruby",
476
+ "ruby3",
477
+ "php",
478
+ "php7",
479
+ "php8",
480
+ "perl",
481
+ "openjdk",
482
+ "openjdk8",
483
+ "openjdk11",
484
+ "openjdk17",
485
+ "openjdk21",
486
+ "openjdk22",
487
+ "openjdk23",
488
+ "openjdk24",
489
+ "openjdk25",
490
+ "openjdk8-jdk",
491
+ "openjdk11-jdk",
492
+ "openjdk17-jdk",
493
+ "openjdk21-jdk",
494
+ "openjdk22-jdk",
495
+ "openjdk23-jdk",
496
+ "openjdk24-jdk",
497
+ "openjdk25-jdk",
498
+ "openjdk8-jre",
499
+ "openjdk11-jre",
500
+ "openjdk17-jre",
501
+ "openjdk21-jre",
502
+ "openjdk22-jre",
503
+ "openjdk23-jre",
504
+ "openjdk24-jre",
505
+ "openjdk25-jre",
506
+ ];
507
+
508
+ /**
509
+ * Run the `cargo-auditable` companion binary against the given source path and
510
+ * return its stdout.
511
+ *
512
+ * @param {string} src Path to the Rust binary or source to inspect.
513
+ * @returns {string|undefined} The tool's stdout, or undefined when the binary
514
+ * is unavailable or produced no output.
515
+ */
516
+ export function getCargoAuditableInfo(src) {
517
+ if (CARGO_AUDITABLE_BIN) {
518
+ const result = safeSpawnSync(CARGO_AUDITABLE_BIN, [src]);
519
+ if (result.status !== 0 || result.error) {
520
+ if (result.stdout || result.stderr) {
521
+ console.error(result.stdout, result.stderr);
522
+ }
523
+ }
524
+ if (result) {
525
+ const stdout = result.stdout;
526
+ if (stdout) {
527
+ return stdout;
528
+ }
529
+ }
530
+ }
531
+ return undefined;
532
+ }
533
+
534
+ /**
535
+ * Execute sourcekitten plugin with the given arguments
536
+ *
537
+ * @param args {Array} Arguments
538
+ * @returns {undefined|Object} Command output
539
+ */
540
+ export function executeSourcekitten(args) {
541
+ if (SOURCEKITTEN_BIN) {
542
+ const result = safeSpawnSync(SOURCEKITTEN_BIN, args);
543
+ if (result.status !== 0 || result.error) {
544
+ if (result.stdout || result.stderr) {
545
+ console.error(result.stdout, result.stderr);
546
+ }
547
+ }
548
+ if (result) {
549
+ const stdout = result.stdout;
550
+ if (stdout) {
551
+ return JSON.parse(stdout);
552
+ }
553
+ }
554
+ }
555
+ return undefined;
556
+ }
557
+
558
+ /**
559
+ * Get the packages installed in the container image filesystem.
560
+ *
561
+ * @param src {String} Source directory containing the extracted filesystem.
562
+ * @param imageConfig {Object} Image configuration containing environment variables, command, entrypoints etc
563
+ * @param options CLI options controlling inventory generation
564
+ *
565
+ * @returns {Object} Metadata containing packages, dependencies, etc
566
+ */
567
+ export async function getOSPackages(src, imageConfig, options = {}) {
568
+ if (isDryRun) {
569
+ recordActivity({
570
+ kind: "container",
571
+ reason:
572
+ "Dry run mode blocks Trivy-based OS package generation because it executes external tools and writes temporary output.",
573
+ status: "blocked",
574
+ target: src,
575
+ });
576
+ return {
577
+ allTypes: new Set(),
578
+ binPaths: [],
579
+ bundledRuntimes: new Set(),
580
+ bundledSdks: new Set(),
581
+ dependenciesList: [],
582
+ executables: [],
583
+ osPackages: [],
584
+ osPackageFiles: [],
585
+ sharedLibs: [],
586
+ services: [],
587
+ tools: [],
588
+ };
589
+ }
590
+ const pkgList = [];
591
+ const osPackageEntries = [];
592
+ const dependenciesList = [];
593
+ const allTypes = new Set();
594
+ const bundledSdks = new Set();
595
+ const bundledRuntimes = new Set();
596
+ let osPackageFiles = [];
597
+ let services = [];
598
+ let tools = [];
599
+ let binPaths = extractPathEnv(imageConfig?.Env);
600
+ if (!binPaths?.length) {
601
+ const rootBinPaths = getDirs(src, "{sbin,bin}", true, false);
602
+ const usrBinPaths = getDirs(
603
+ src,
604
+ "/{app,opt,usr,home}/**/{sbin,bin}",
605
+ true,
606
+ true,
607
+ );
608
+ binPaths = Array.from(
609
+ new Set(
610
+ rootBinPaths
611
+ .concat(usrBinPaths)
612
+ .map((f) => relative(src, f))
613
+ .filter(Boolean),
614
+ ),
615
+ ).sort();
616
+ if (DEBUG_MODE && binPaths.length) {
617
+ console.log(
618
+ `Falling back to inferred binary paths for ${src}: ${binPaths.join(", ")}`,
619
+ );
620
+ }
621
+ }
622
+ if (TRIVY_BIN) {
623
+ let imageType = "image";
624
+ const trivyCacheDir = join(homedir(), ".cache", "trivy");
625
+ try {
626
+ safeMkdirSync(join(trivyCacheDir, "db"), { recursive: true });
627
+ safeMkdirSync(join(trivyCacheDir, "java-db"), { recursive: true });
628
+ } catch (_err) {
629
+ // ignore errors
630
+ }
631
+ if (safeExistsSync(src)) {
632
+ imageType = "rootfs";
633
+ }
634
+ const tempDir = safeMkdtempSync(join(getTmpDir(), "trivy-cdxgen-"));
635
+ const bomJsonFile = join(tempDir, "trivy-bom.json");
636
+ const args = [
637
+ imageType,
638
+ "--cache-dir",
639
+ trivyCacheDir,
640
+ "--output",
641
+ bomJsonFile,
642
+ ];
643
+ if (DEBUG_MODE) {
644
+ args.push("--debug");
645
+ }
646
+ args.push(src);
647
+ if (DEBUG_MODE) {
648
+ console.log("Executing", TRIVY_BIN, args.join(" "));
649
+ }
650
+ const result = safeSpawnSync(TRIVY_BIN, args);
651
+ if (result.status !== 0 || result.error) {
652
+ if (result.stdout || result.stderr) {
653
+ console.error(result.stdout, result.stderr);
654
+ }
655
+ }
656
+ if (safeExistsSync(bomJsonFile)) {
657
+ let tmpBom = {};
658
+ try {
659
+ tmpBom = JSON.parse(
660
+ readFileSync(bomJsonFile, {
661
+ encoding: "utf-8",
662
+ }),
663
+ );
664
+ } catch (_e) {
665
+ // ignore errors
666
+ }
667
+ // Clean up
668
+ if (tempDir?.startsWith(getTmpDir())) {
669
+ if (DEBUG_MODE) {
670
+ console.log(`Cleaning up ${tempDir}`);
671
+ }
672
+ safeRmSync(tempDir, { recursive: true, force: true });
673
+ }
674
+ const osReleaseData = readOsRelease(src);
675
+ if (DEBUG_MODE) {
676
+ console.log(osReleaseData);
677
+ }
678
+ // distro_name must be a real release codename/token (bookworm, jammy,
679
+ // rhel-9, alpine-3.19), never a distro *display name*. VERSION_CODENAME is
680
+ // authoritative when present; otherwise the RHEL/CentOS product-name
681
+ // fallbacks are only accepted when they resolve through OS_DISTRO_ALIAS
682
+ // (e.g. "red hat enterprise linux 9" -> "rhel-9"). Display names such as
683
+ // "Rocky Linux" or "AlmaLinux" have no alias entry and are dropped so we
684
+ // never emit a bogus distro_name qualifier (RPM distros track packages
685
+ // flat, without a release segment, so folding a display name breaks
686
+ // downstream vuln-db matching).
687
+ let distro_codename = (
688
+ osReleaseData["VERSION_CODENAME"] || ""
689
+ ).toLowerCase();
690
+ if (!distro_codename) {
691
+ const distro_display_name = (
692
+ osReleaseData["CENTOS_MANTISBT_PROJECT"] ||
693
+ osReleaseData["REDHAT_BUGZILLA_PRODUCT"] ||
694
+ osReleaseData["REDHAT_SUPPORT_PRODUCT"] ||
695
+ ""
696
+ ).toLowerCase();
697
+ if (distro_display_name && OS_DISTRO_ALIAS[distro_display_name]) {
698
+ distro_codename = OS_DISTRO_ALIAS[distro_display_name];
699
+ }
700
+ }
701
+ let distro_id = osReleaseData["ID"] || "";
702
+ const distro_id_like = osReleaseData["ID_LIKE"] || "";
703
+ let purl_type = "rpm";
704
+ switch (distro_id) {
705
+ case "debian":
706
+ case "ubuntu":
707
+ case "pop":
708
+ purl_type = "deb";
709
+ break;
710
+ case "sles":
711
+ case "suse":
712
+ case "opensuse":
713
+ purl_type = "rpm";
714
+ break;
715
+ case "alpine":
716
+ purl_type = "apk";
717
+ if (osReleaseData.VERSION_ID) {
718
+ const versionParts = osReleaseData["VERSION_ID"].split(".");
719
+ if (versionParts.length >= 2) {
720
+ distro_codename = `alpine-${versionParts[0]}.${versionParts[1]}`;
721
+ }
722
+ }
723
+ break;
724
+ default:
725
+ if (distro_id_like.includes("debian")) {
726
+ purl_type = "deb";
727
+ } else if (
728
+ distro_id_like.includes("rhel") ||
729
+ distro_id_like.includes("centos") ||
730
+ distro_id_like.includes("fedora")
731
+ ) {
732
+ purl_type = "rpm";
733
+ }
734
+ break;
735
+ }
736
+ if (osReleaseData["VERSION_ID"]) {
737
+ distro_id = `${distro_id}-${osReleaseData["VERSION_ID"]}`;
738
+ if (OS_DISTRO_ALIAS[distro_id]) {
739
+ distro_codename = OS_DISTRO_ALIAS[distro_id];
740
+ }
741
+ }
742
+ const tmpDependencies = {};
743
+ tools = enrichToolComponents(
744
+ (Array.isArray(tmpBom?.metadata?.tools)
745
+ ? tmpBom.metadata.tools
746
+ : tmpBom?.metadata?.tools?.components || []
747
+ ).filter((tool) => tool?.["bom-ref"] && tool?.name !== "cdxgen"),
748
+ ["trivy"].concat(imageType === "rootfs" ? ["trustinspector"] : []),
749
+ );
750
+ const toolRefs = extractToolRefs(
751
+ { components: tools },
752
+ (tool) => tool?.name !== "cdxgen",
753
+ );
754
+ (tmpBom.dependencies || []).forEach((d) => {
755
+ tmpDependencies[d.ref] = d.dependsOn;
756
+ });
757
+ if (tmpBom?.components) {
758
+ for (const comp of tmpBom.components) {
759
+ if (comp.purl) {
760
+ const origBomRef = comp["bom-ref"];
761
+ // Fix the group
762
+ let group = dirname(comp.name);
763
+ const name = basename(comp.name);
764
+ let purlObj;
765
+ if (group === ".") {
766
+ group = "";
767
+ }
768
+ comp.group = group;
769
+ comp.name = name;
770
+ const modularityLabel = extractModularityLabel(comp);
771
+ try {
772
+ purlObj = Purl.parse(comp.purl);
773
+ purlObj.qualifiers = purlObj.qualifiers || {};
774
+ } catch (_err) {
775
+ // continue regardless of error
776
+ }
777
+ // Canonicalise the distro vendor namespace for every OS component,
778
+ // regardless of whether trivy expressed the vendor via the purl
779
+ // namespace or the component group (it is inconsistent between
780
+ // packages). e.g. pkg:rpm/alma/... -> pkg:rpm/almalinux/... so the
781
+ // purls match the vulnerability feeds. Also aligns the ``distro``
782
+ // qualifier vendor prefix (alma-9.8 -> almalinux-9.8).
783
+ if (purlObj && OS_PURL_TYPES.includes(purlObj.type)) {
784
+ const os_ns = purlObj.namespace || group || "";
785
+ const os_ns_alias = OS_DISTRO_NAMESPACE_ALIAS[os_ns];
786
+ if (os_ns_alias) {
787
+ group = os_ns_alias;
788
+ comp.group = os_ns_alias;
789
+ purlObj.namespace = os_ns_alias;
790
+ const distroQual = purlObj.qualifiers?.distro;
791
+ if (distroQual?.startsWith(`${os_ns}-`)) {
792
+ purlObj.qualifiers.distro = `${os_ns_alias}-${distroQual.slice(
793
+ os_ns.length + 1,
794
+ )}`;
795
+ }
796
+ comp.purl = build({
797
+ type: purlObj.type,
798
+ namespace: os_ns_alias || null,
799
+ name: name,
800
+ version: purlObj.version || null,
801
+ qualifiers: purlObj.qualifiers || null,
802
+ subpath: purlObj.subpath || null,
803
+ });
804
+ comp["bom-ref"] = decodeURIComponent(comp.purl);
805
+ }
806
+ }
807
+ if (
808
+ group === "" &&
809
+ purlObj &&
810
+ OS_PURL_TYPES.includes(purlObj["type"])
811
+ ) {
812
+ try {
813
+ if (purlObj?.namespace && purlObj.namespace !== "") {
814
+ group =
815
+ OS_DISTRO_NAMESPACE_ALIAS[purlObj.namespace] ||
816
+ purlObj.namespace;
817
+ comp.group = group;
818
+ purlObj.namespace = group;
819
+ }
820
+ if (distro_id?.length) {
821
+ purlObj.qualifiers["distro"] = distro_id;
822
+ }
823
+ if (distro_codename?.length) {
824
+ purlObj.qualifiers["distro_name"] = distro_codename;
825
+ }
826
+ // Bug fix for mageia and oracle linux
827
+ // Type is being returned as none for ubuntu as well!
828
+ if (purlObj?.type === "none") {
829
+ purlObj["type"] = purl_type;
830
+ purlObj["namespace"] = "";
831
+ comp.group = "";
832
+ if (comp.purl?.includes(".mga")) {
833
+ purlObj["namespace"] = "mageia";
834
+ comp.group = "mageia";
835
+ purlObj.qualifiers["distro"] = "mageia";
836
+ distro_codename = "mga";
837
+ }
838
+ comp.purl = build({
839
+ type: purlObj.type,
840
+ namespace: purlObj.namespace || null,
841
+ name: name,
842
+ version: purlObj.version || null,
843
+ qualifiers: purlObj.qualifiers || null,
844
+ subpath: purlObj.subpath || null,
845
+ });
846
+ comp["bom-ref"] = decodeURIComponent(comp.purl);
847
+ }
848
+ if (purlObj?.type !== "none") {
849
+ allTypes.add(purlObj.type);
850
+ }
851
+ // Prefix distro codename for ubuntu
852
+ if (purlObj?.qualifiers?.distro) {
853
+ allTypes.add(purlObj.qualifiers.distro);
854
+ if (OS_DISTRO_ALIAS[purlObj.qualifiers.distro]) {
855
+ distro_codename =
856
+ OS_DISTRO_ALIAS[purlObj.qualifiers.distro];
857
+ } else if (group === "alpine") {
858
+ const dtmpA = purlObj.qualifiers.distro.split(".");
859
+ if (dtmpA && dtmpA.length > 2) {
860
+ distro_codename = `${dtmpA[0]}.${dtmpA[1]}`;
861
+ }
862
+ } else if (group === "photon") {
863
+ const dtmpA = purlObj.qualifiers.distro.split("-");
864
+ if (dtmpA && dtmpA.length > 1) {
865
+ distro_codename = dtmpA[0];
866
+ }
867
+ } else if (group === "redhat") {
868
+ const dtmpA = purlObj.qualifiers.distro.split(".");
869
+ if (dtmpA && dtmpA.length > 1) {
870
+ distro_codename = dtmpA[0].replace(
871
+ "redhat",
872
+ "enterprise_linux",
873
+ );
874
+ }
875
+ }
876
+ }
877
+ if (distro_codename !== "") {
878
+ allTypes.add(distro_codename);
879
+ allTypes.add(purlObj.namespace);
880
+ comp.purl = build({
881
+ type: purlObj.type,
882
+ namespace: purlObj.namespace || null,
883
+ name: name,
884
+ version: purlObj.version || null,
885
+ qualifiers: purlObj.qualifiers || null,
886
+ subpath: purlObj.subpath || null,
887
+ });
888
+ comp["bom-ref"] = decodeURIComponent(comp.purl);
889
+ }
890
+ } catch (_err) {
891
+ // continue regardless of error
892
+ }
893
+ }
894
+ if (comp.purl.includes("epoch=")) {
895
+ try {
896
+ const epoch = purlObj.qualifiers?.epoch;
897
+ // trivy seems to be removing the epoch from the version and moving it to a qualifier
898
+ // let's fix this hack to improve confidence.
899
+ if (epoch) {
900
+ purlObj.version = `${epoch}:${purlObj.version}`;
901
+ comp.version = purlObj.version;
902
+ }
903
+ comp.evidence = {
904
+ identity: [
905
+ {
906
+ field: "purl",
907
+ confidence: 1,
908
+ methods: [
909
+ {
910
+ technique: "other",
911
+ confidence: 1,
912
+ value: comp.purl,
913
+ },
914
+ ],
915
+ },
916
+ ],
917
+ };
918
+ if (distro_id?.length) {
919
+ purlObj.qualifiers["distro"] = distro_id;
920
+ }
921
+ if (distro_codename?.length) {
922
+ purlObj.qualifiers["distro_name"] = distro_codename;
923
+ }
924
+ allTypes.add(purlObj.namespace);
925
+ comp.purl = build({
926
+ type: purlObj.type,
927
+ namespace: purlObj.namespace || null,
928
+ name: name,
929
+ version: purlObj.version || null,
930
+ qualifiers: purlObj.qualifiers || null,
931
+ subpath: purlObj.subpath || null,
932
+ });
933
+ comp["bom-ref"] = decodeURIComponent(comp.purl);
934
+ } catch (err) {
935
+ // continue regardless of error
936
+ console.log(err);
937
+ }
938
+ }
939
+ attachIdentityTools(comp, toolRefs);
940
+ // Fix licenses
941
+ if (
942
+ comp.licenses &&
943
+ Array.isArray(comp.licenses) &&
944
+ comp.licenses.length
945
+ ) {
946
+ const newLicenses = [];
947
+ for (const aLic of comp.licenses) {
948
+ if (aLic?.license?.name) {
949
+ if (isSpdxLicenseExpression(aLic.license.name)) {
950
+ newLicenses.push({ expression: aLic.license.name });
951
+ } else {
952
+ const possibleId = findLicenseId(aLic.license.name);
953
+ if (possibleId !== aLic.license.name) {
954
+ newLicenses.push({ license: { id: possibleId } });
955
+ } else {
956
+ newLicenses.push({
957
+ license: { name: aLic.license.name },
958
+ });
959
+ }
960
+ }
961
+ } else if (
962
+ aLic?.license &&
963
+ Object.keys(aLic).length &&
964
+ Object.keys(aLic.license).length
965
+ ) {
966
+ newLicenses.push(aLic);
967
+ }
968
+ }
969
+ comp.licenses = adjustLicenseInformation(newLicenses);
970
+ }
971
+ // Fix hashes
972
+ if (
973
+ comp.hashes &&
974
+ Array.isArray(comp.hashes) &&
975
+ comp.hashes.length
976
+ ) {
977
+ const hashContent = comp.hashes[0].content;
978
+ if (!hashContent || hashContent.length < 32) {
979
+ delete comp.hashes;
980
+ }
981
+ }
982
+ const compProperties = comp.properties;
983
+ const trivyMetadata = extractTrivyOsPackageMetadata(compProperties);
984
+ const fallbackIdentityProperties = promoteTrivyOsPackageIdentity(
985
+ comp,
986
+ trivyMetadata,
987
+ );
988
+ let { srcName, srcVersion, srcRelease, epoch } = trivyMetadata;
989
+ // See issue #2067
990
+ if (srcVersion && srcRelease) {
991
+ srcVersion = `${srcVersion}-${srcRelease}`;
992
+ }
993
+ if (epoch) {
994
+ srcVersion = `${epoch}:${srcVersion}`;
995
+ }
996
+ const labelProperties = modularityLabel
997
+ ? [{ name: "cdx:os:modularityLabel", value: modularityLabel }]
998
+ : [];
999
+ if (
1000
+ trivyMetadata.retainedProperties.length ||
1001
+ fallbackIdentityProperties.length ||
1002
+ labelProperties.length
1003
+ ) {
1004
+ comp.properties = uniqueProperties(
1005
+ trivyMetadata.retainedProperties
1006
+ .concat(fallbackIdentityProperties)
1007
+ .concat(labelProperties),
1008
+ );
1009
+ } else {
1010
+ delete comp.properties;
1011
+ }
1012
+ // A golang purl carries the module path in its namespace, and the
1013
+ // Go standard library has no module path — trivy reports it as the
1014
+ // bare name `stdlib`, which is not a valid purl. The component is
1015
+ // worth keeping, since the toolchain version is a real signal, so
1016
+ // the purl is dropped rather than given an invented namespace.
1017
+ if (
1018
+ comp.purl?.startsWith("pkg:golang/") &&
1019
+ !tryBuildPurl({
1020
+ type: "golang",
1021
+ namespace: comp.group || null,
1022
+ name: comp.name,
1023
+ version: comp.version || null,
1024
+ })
1025
+ ) {
1026
+ comp["bom-ref"] = decodeURIComponent(comp.purl);
1027
+ delete comp.purl;
1028
+ }
1029
+ // Bug fix: We can get bom-ref like this: pkg:rpm/sles/libstdc%2B%2B6@14.2.0+git10526-150000.1.6.1?arch=x86_64&distro=sles-15.5
1030
+ if (
1031
+ comp["bom-ref"] &&
1032
+ comp.purl &&
1033
+ comp["bom-ref"] !== decodeURIComponent(comp.purl)
1034
+ ) {
1035
+ comp["bom-ref"] = decodeURIComponent(comp.purl);
1036
+ }
1037
+ pkgList.push(comp);
1038
+ if (trivyMetadata.installedFiles.length) {
1039
+ osPackageEntries.push({
1040
+ capabilities: trivyMetadata.capabilities,
1041
+ commandPaths: trivyMetadata.installedCommandPaths,
1042
+ commands: trivyMetadata.installedCommands,
1043
+ files: trivyMetadata.installedFiles,
1044
+ packageName: comp.name,
1045
+ packageRef: comp["bom-ref"],
1046
+ packageVersion: comp.version,
1047
+ });
1048
+ }
1049
+ detectSdksRuntimes(comp, bundledSdks, bundledRuntimes);
1050
+ const compDeps = retrieveDependencies(
1051
+ tmpDependencies,
1052
+ origBomRef,
1053
+ comp,
1054
+ );
1055
+ if (compDeps) {
1056
+ dependenciesList.push(compDeps);
1057
+ }
1058
+ // HACK: Many vulnerability databases, including vdb, track vulnerabilities based on source package names :(
1059
+ // If there is a source package defined we include it as well to make such SCA scanners work.
1060
+ // As a compromise, we reduce the confidence to zero so that there is a way to filter these out.
1061
+ if (srcName && srcVersion && srcName !== comp.name) {
1062
+ const newComp = Object.assign({}, comp);
1063
+ newComp.name = srcName;
1064
+ newComp.version = srcVersion;
1065
+ newComp.tags = ["source"];
1066
+ newComp.evidence = {
1067
+ identity: [
1068
+ {
1069
+ field: "purl",
1070
+ confidence: 0,
1071
+ methods: [
1072
+ {
1073
+ technique: "filename",
1074
+ confidence: 0,
1075
+ value: comp.name,
1076
+ },
1077
+ ],
1078
+ },
1079
+ ],
1080
+ };
1081
+ // Track upstream and source versions as qualifiers
1082
+ if (purlObj) {
1083
+ const newCompQualifiers = {
1084
+ ...purlObj.qualifiers,
1085
+ };
1086
+ delete newCompQualifiers.epoch;
1087
+ if (epoch) {
1088
+ newCompQualifiers.epoch = epoch;
1089
+ }
1090
+ newComp.purl = build({
1091
+ type: purlObj.type,
1092
+ namespace: purlObj.namespace || null,
1093
+ name: srcName,
1094
+ version: srcVersion || null,
1095
+ qualifiers: newCompQualifiers || null,
1096
+ subpath: purlObj.subpath || null,
1097
+ });
1098
+ }
1099
+ newComp["bom-ref"] = decodeURIComponent(newComp.purl);
1100
+ delete newComp.properties;
1101
+ attachIdentityTools(newComp, toolRefs);
1102
+ pkgList.push(newComp);
1103
+ detectSdksRuntimes(newComp, bundledSdks, bundledRuntimes);
1104
+ }
1105
+ }
1106
+ }
1107
+ }
1108
+ }
1109
+ }
1110
+ const rootfsRepositoryInventory = await collectRootfsRepositoryInventory(
1111
+ src,
1112
+ options,
1113
+ );
1114
+ if (rootfsRepositoryInventory.components.length) {
1115
+ pkgList.push(...rootfsRepositoryInventory.components);
1116
+ }
1117
+ if (rootfsRepositoryInventory.dependenciesList.length) {
1118
+ dependenciesList.push(...rootfsRepositoryInventory.dependenciesList);
1119
+ }
1120
+ const {
1121
+ components: ownedFileComponents,
1122
+ dependenciesList: ownedFileDependencies,
1123
+ ownedFilePaths,
1124
+ services: ownedServices,
1125
+ } = await createOSPackageFileComponents(src, osPackageEntries);
1126
+ if (ownedFileComponents.length) {
1127
+ osPackageFiles = ownedFileComponents;
1128
+ }
1129
+ if (ownedFileDependencies.length) {
1130
+ dependenciesList.push(...ownedFileDependencies);
1131
+ }
1132
+ if (ownedServices.length) {
1133
+ services = ownedServices;
1134
+ }
1135
+ let executables = [];
1136
+ if (binPaths?.length) {
1137
+ executables = await fileComponents(
1138
+ src,
1139
+ collectExecutables(src, binPaths, ownedFilePaths),
1140
+ "executable",
1141
+ );
1142
+ }
1143
+ // Directories containing shared libraries
1144
+ const defaultLibPaths = [
1145
+ "/lib",
1146
+ "/lib64",
1147
+ "/usr/lib",
1148
+ "/usr/lib64",
1149
+ "/usr/local/lib64",
1150
+ "/usr/local/lib",
1151
+ "/lib/x86_64-linux-gnu",
1152
+ "/usr/lib/x86_64-linux-gnu",
1153
+ "/lib/i386-linux-gnu",
1154
+ "/usr/lib/i386-linux-gnu",
1155
+ "/lib/arm-linux-gnueabihf",
1156
+ "/usr/lib/arm-linux-gnueabihf",
1157
+ "/opt/**/lib",
1158
+ "/root/**/lib",
1159
+ ];
1160
+ const sharedLibs = await fileComponents(
1161
+ src,
1162
+ collectSharedLibs(
1163
+ src,
1164
+ defaultLibPaths,
1165
+ "/etc/ld.so.conf",
1166
+ "/etc/ld.so.conf.d/*.conf",
1167
+ ownedFilePaths,
1168
+ ),
1169
+ "shared_library",
1170
+ );
1171
+ return {
1172
+ osPackages: pkgList,
1173
+ osPackageFiles,
1174
+ dependenciesList,
1175
+ allTypes: Array.from(allTypes).sort(),
1176
+ bundledSdks: Array.from(bundledSdks).sort(),
1177
+ bundledRuntimes: Array.from(bundledRuntimes).sort(),
1178
+ binPaths,
1179
+ executables,
1180
+ sharedLibs,
1181
+ services,
1182
+ tools,
1183
+ };
1184
+ }
1185
+
1186
+ function extractTrivyOsPackageMetadata(compProperties) {
1187
+ const metadata = {
1188
+ capabilities: [],
1189
+ installedCommandPaths: [],
1190
+ installedCommands: [],
1191
+ installedFiles: [],
1192
+ packageMaintainer: undefined,
1193
+ packageVendor: undefined,
1194
+ retainedProperties: [],
1195
+ srcName: undefined,
1196
+ srcRelease: undefined,
1197
+ srcVersion: undefined,
1198
+ epoch: undefined,
1199
+ };
1200
+ if (!Array.isArray(compProperties) || !compProperties.length) {
1201
+ return metadata;
1202
+ }
1203
+ for (const aprop of compProperties) {
1204
+ if (!aprop?.name) {
1205
+ continue;
1206
+ }
1207
+ if (aprop.name.endsWith("SrcName")) {
1208
+ metadata.srcName = aprop.value;
1209
+ continue;
1210
+ }
1211
+ if (aprop.name.endsWith("SrcVersion")) {
1212
+ metadata.srcVersion = aprop.value;
1213
+ continue;
1214
+ }
1215
+ if (aprop.name.endsWith("SrcRelease")) {
1216
+ metadata.srcRelease = aprop.value;
1217
+ continue;
1218
+ }
1219
+ if (aprop.name.endsWith("SrcEpoch")) {
1220
+ metadata.epoch = aprop.value;
1221
+ continue;
1222
+ }
1223
+ if (aprop.name.endsWith("PackageMaintainer")) {
1224
+ metadata.packageMaintainer = aprop.value;
1225
+ continue;
1226
+ }
1227
+ if (aprop.name.endsWith("PackageVendor")) {
1228
+ metadata.packageVendor = aprop.value;
1229
+ continue;
1230
+ }
1231
+ if (aprop.name.endsWith("InstalledFile")) {
1232
+ metadata.installedFiles.push(aprop.value);
1233
+ continue;
1234
+ }
1235
+ if (aprop.name.endsWith("InstalledCommandPath")) {
1236
+ metadata.installedCommandPaths.push(aprop.value);
1237
+ metadata.retainedProperties.push(aprop);
1238
+ continue;
1239
+ }
1240
+ if (aprop.name.endsWith("InstalledCommand")) {
1241
+ metadata.installedCommands.push(aprop.value);
1242
+ metadata.retainedProperties.push(aprop);
1243
+ continue;
1244
+ }
1245
+ if (aprop.name.endsWith("Capability")) {
1246
+ metadata.capabilities.push(aprop.value);
1247
+ metadata.retainedProperties.push(aprop);
1248
+ continue;
1249
+ }
1250
+ if (
1251
+ aprop.name.endsWith("CapabilityCount") ||
1252
+ aprop.name.endsWith("InstalledFileCount") ||
1253
+ aprop.name.endsWith("InstalledCommandCount")
1254
+ ) {
1255
+ metadata.retainedProperties.push(aprop);
1256
+ continue;
1257
+ }
1258
+ metadata.retainedProperties.push(aprop);
1259
+ }
1260
+ metadata.capabilities = uniqueSortedStrings(metadata.capabilities);
1261
+ metadata.installedCommandPaths = uniqueSortedStrings(
1262
+ metadata.installedCommandPaths,
1263
+ );
1264
+ metadata.installedCommands = uniqueSortedStrings(metadata.installedCommands);
1265
+ metadata.installedFiles = uniqueSortedStrings(metadata.installedFiles);
1266
+ metadata.retainedProperties = uniqueProperties(metadata.retainedProperties);
1267
+ return metadata;
1268
+ }
1269
+
1270
+ function getOrganizationalEntityName(entity) {
1271
+ if (!entity) {
1272
+ return undefined;
1273
+ }
1274
+ if (typeof entity === "string") {
1275
+ return entity.trim() || undefined;
1276
+ }
1277
+ if (typeof entity === "object" && typeof entity.name === "string") {
1278
+ return entity.name.trim() || undefined;
1279
+ }
1280
+ return undefined;
1281
+ }
1282
+
1283
+ function sameOrganizationalEntity(entity, expectedName) {
1284
+ const currentName = getOrganizationalEntityName(entity);
1285
+ return Boolean(
1286
+ currentName &&
1287
+ expectedName &&
1288
+ currentName.localeCompare(expectedName, undefined, {
1289
+ sensitivity: "accent",
1290
+ }) === 0,
1291
+ );
1292
+ }
1293
+
1294
+ function mergeOrganizationalEntityField(component, fieldName, entityName) {
1295
+ const normalizedName = `${entityName || ""}`.trim();
1296
+ if (!normalizedName) {
1297
+ return { applied: false, represented: false };
1298
+ }
1299
+ if (!component?.[fieldName]) {
1300
+ component[fieldName] = { name: normalizedName };
1301
+ return { applied: true, represented: true };
1302
+ }
1303
+ if (sameOrganizationalEntity(component[fieldName], normalizedName)) {
1304
+ return { applied: false, represented: true };
1305
+ }
1306
+ return { applied: false, represented: false };
1307
+ }
1308
+
1309
+ function parseOrganizationalContact(value) {
1310
+ const normalizedValue = `${value || ""}`.trim();
1311
+ if (!normalizedValue) {
1312
+ return undefined;
1313
+ }
1314
+ const match = normalizedValue.match(/^([^<>]+?)\s*<([^<>\s]+@[^<>\s]+)>$/);
1315
+ if (match) {
1316
+ return {
1317
+ name: match[1].trim(),
1318
+ email: match[2].trim(),
1319
+ };
1320
+ }
1321
+ return { name: normalizedValue };
1322
+ }
1323
+
1324
+ function sameOrganizationalContact(left, right) {
1325
+ if (!left || !right) {
1326
+ return false;
1327
+ }
1328
+ const leftName = `${left.name || ""}`.trim();
1329
+ const rightName = `${right.name || ""}`.trim();
1330
+ const leftEmail = `${left.email || ""}`.trim().toLowerCase();
1331
+ const rightEmail = `${right.email || ""}`.trim().toLowerCase();
1332
+ return leftName === rightName && leftEmail === rightEmail;
1333
+ }
1334
+
1335
+ function mergeAuthorsFromMaintainer(component, maintainerValue) {
1336
+ const authorContact = parseOrganizationalContact(maintainerValue);
1337
+ if (!authorContact?.name) {
1338
+ return { applied: false, represented: false };
1339
+ }
1340
+ if (!Array.isArray(component?.authors) || !component.authors.length) {
1341
+ component.authors = [authorContact];
1342
+ return { applied: true, represented: true };
1343
+ }
1344
+ if (
1345
+ component.authors.some((author) =>
1346
+ sameOrganizationalContact(author, authorContact),
1347
+ )
1348
+ ) {
1349
+ return { applied: false, represented: true };
1350
+ }
1351
+ return { applied: false, represented: false };
1352
+ }
1353
+
1354
+ function promoteTrivyOsPackageIdentity(component, trivyMetadata) {
1355
+ const fallbackProperties = [];
1356
+ const vendorValue = `${trivyMetadata?.packageVendor || ""}`.trim();
1357
+ const supplierName = getOrganizationalEntityName(component?.supplier);
1358
+ const maintainerValue = `${
1359
+ trivyMetadata?.packageMaintainer || supplierName || ""
1360
+ }`.trim();
1361
+
1362
+ const maintainerAuthorResult = mergeAuthorsFromMaintainer(
1363
+ component,
1364
+ maintainerValue,
1365
+ );
1366
+ const maintainerSupplierResult = mergeOrganizationalEntityField(
1367
+ component,
1368
+ "supplier",
1369
+ maintainerValue,
1370
+ );
1371
+ if (
1372
+ trivyMetadata?.packageMaintainer &&
1373
+ !maintainerAuthorResult.represented &&
1374
+ !maintainerSupplierResult.represented
1375
+ ) {
1376
+ fallbackProperties.push({
1377
+ name: "internal:PackageMaintainer",
1378
+ value: trivyMetadata.packageMaintainer,
1379
+ });
1380
+ }
1381
+
1382
+ const vendorSupplierResult = mergeOrganizationalEntityField(
1383
+ component,
1384
+ "supplier",
1385
+ vendorValue,
1386
+ );
1387
+ const vendorManufacturerResult = mergeOrganizationalEntityField(
1388
+ component,
1389
+ "manufacturer",
1390
+ vendorValue,
1391
+ );
1392
+ if (
1393
+ vendorValue &&
1394
+ !vendorSupplierResult.represented &&
1395
+ !vendorManufacturerResult.represented
1396
+ ) {
1397
+ fallbackProperties.push({
1398
+ name: "internal:PackageVendor",
1399
+ value: vendorValue,
1400
+ });
1401
+ }
1402
+ return fallbackProperties;
1403
+ }
1404
+
1405
+ async function collectRootfsRepositoryInventory(basePath, options = {}) {
1406
+ let trustedKeyComponents = [];
1407
+ let refsByPath = new Map();
1408
+ if (isCycloneDxComponentTypeEnabled("cryptographic-asset", options)) {
1409
+ ({ components: trustedKeyComponents, refsByPath } =
1410
+ await collectTrustedKeyComponents(basePath));
1411
+ trustedKeyComponents = applyTrustMaterialEnhancements(
1412
+ trustedKeyComponents,
1413
+ collectTrustInspectorRootfsInventory(basePath),
1414
+ );
1415
+ }
1416
+ refsByPath = new Map(
1417
+ trustedKeyComponents
1418
+ .map((component) => {
1419
+ const srcFile = (component.properties || []).find(
1420
+ (property) => property.name === "internal:SrcFile",
1421
+ )?.value;
1422
+ return srcFile
1423
+ ? [normalizeContainerPath(srcFile), component["bom-ref"]]
1424
+ : undefined;
1425
+ })
1426
+ .filter(Boolean),
1427
+ );
1428
+ const repositoryEntries = uniqueRepositoryEntries(
1429
+ parseAptRepositorySources(basePath).concat(
1430
+ parseYumRepositorySources(basePath),
1431
+ ),
1432
+ );
1433
+ const components = [...trustedKeyComponents];
1434
+ const dependenciesList = [];
1435
+ const seenComponentRefs = new Set(components.map((comp) => comp["bom-ref"]));
1436
+ for (const entry of repositoryEntries) {
1437
+ const component = createRepositorySourceComponent(entry);
1438
+ if (seenComponentRefs.has(component["bom-ref"])) {
1439
+ continue;
1440
+ }
1441
+ seenComponentRefs.add(component["bom-ref"]);
1442
+ components.push(component);
1443
+ const dependsOn = uniqueSortedStrings(
1444
+ (entry.keyReferences || [])
1445
+ .map((keyRef) => normalizeLocalRepositoryReference(keyRef))
1446
+ .map((keyRef) => refsByPath.get(keyRef))
1447
+ .filter(Boolean),
1448
+ );
1449
+ if (dependsOn.length) {
1450
+ dependenciesList.push({
1451
+ ref: component["bom-ref"],
1452
+ dependsOn,
1453
+ });
1454
+ }
1455
+ }
1456
+ return { components, dependenciesList };
1457
+ }
1458
+
1459
+ async function collectTrustedKeyComponents(basePath) {
1460
+ const refsByPath = new Map();
1461
+ const components = [];
1462
+ for (const normalizedPath of collectTrustedKeyPaths(basePath)) {
1463
+ const component = await createTrustedKeyComponent(basePath, normalizedPath);
1464
+ if (!component?.["bom-ref"]) {
1465
+ continue;
1466
+ }
1467
+ refsByPath.set(normalizedPath, component["bom-ref"]);
1468
+ components.push(component);
1469
+ }
1470
+ return { components, refsByPath };
1471
+ }
1472
+
1473
+ function collectTrustedKeyPaths(basePath) {
1474
+ const results = new Set();
1475
+ for (const candidate of [
1476
+ "/etc/apt/trusted.gpg",
1477
+ "/etc/apt/trusted.gpg.d",
1478
+ "/usr/share/keyrings",
1479
+ "/etc/pki/rpm-gpg",
1480
+ "/usr/share/distribution-gpg-keys",
1481
+ "/etc/apk/keys",
1482
+ ]) {
1483
+ const normalizedCandidate = normalizeContainerPath(candidate);
1484
+ const absoluteCandidate = join(
1485
+ basePath,
1486
+ normalizedCandidate.replace(/^\/+/, ""),
1487
+ );
1488
+ if (!safeExistsSync(absoluteCandidate)) {
1489
+ continue;
1490
+ }
1491
+ const stats = statSync(absoluteCandidate, { throwIfNoEntry: false });
1492
+ if (!stats) {
1493
+ continue;
1494
+ }
1495
+ if (stats.isDirectory()) {
1496
+ for (const filePath of walkRootfsFiles(basePath, normalizedCandidate)) {
1497
+ if (isTrustedKeyPath(filePath)) {
1498
+ results.add(filePath);
1499
+ }
1500
+ }
1501
+ continue;
1502
+ }
1503
+ if (isTrustedKeyPath(normalizedCandidate)) {
1504
+ results.add(normalizedCandidate);
1505
+ }
1506
+ }
1507
+ return Array.from(results).sort();
1508
+ }
1509
+
1510
+ function walkRootfsFiles(basePath, normalizedDir) {
1511
+ const results = [];
1512
+ const absoluteDir = join(basePath, normalizedDir.replace(/^\/+/, ""));
1513
+ if (!safeExistsSync(absoluteDir)) {
1514
+ return results;
1515
+ }
1516
+ let entries;
1517
+ try {
1518
+ entries = readdirSync(absoluteDir, { withFileTypes: true });
1519
+ } catch (_e) {
1520
+ return results;
1521
+ }
1522
+ for (const entry of entries) {
1523
+ const normalizedPath = normalizeContainerPath(
1524
+ `${normalizedDir.replace(/\/+$/, "")}/${entry.name}`,
1525
+ );
1526
+ if (entry.isDirectory()) {
1527
+ results.push(...walkRootfsFiles(basePath, normalizedPath));
1528
+ continue;
1529
+ }
1530
+ if (entry.isFile()) {
1531
+ results.push(normalizedPath);
1532
+ }
1533
+ }
1534
+ return results;
1535
+ }
1536
+
1537
+ function isTrustedKeyPath(normalizedPath) {
1538
+ const lowerPath = normalizeContainerPath(normalizedPath)?.toLowerCase();
1539
+ if (!lowerPath) {
1540
+ return false;
1541
+ }
1542
+ return (
1543
+ lowerPath === "/etc/apt/trusted.gpg" ||
1544
+ lowerPath.includes("/trusted.gpg.d/") ||
1545
+ lowerPath.includes("/keyrings/") ||
1546
+ lowerPath.includes("/rpm-gpg/") ||
1547
+ lowerPath.includes("/distribution-gpg-keys/") ||
1548
+ lowerPath.includes("/apk/keys/")
1549
+ );
1550
+ }
1551
+
1552
+ async function createTrustedKeyComponent(basePath, normalizedPath) {
1553
+ const absolutePath = join(basePath, normalizedPath.replace(/^\/+/, ""));
1554
+ const stats = statSync(absolutePath, { throwIfNoEntry: false });
1555
+ if (!stats || stats.isDirectory()) {
1556
+ return undefined;
1557
+ }
1558
+ let hashValues = {};
1559
+ try {
1560
+ hashValues = await multiChecksumFile(["sha1", "sha256"], absolutePath);
1561
+ } catch (_err) {
1562
+ // ignore
1563
+ }
1564
+ const version = hashValues.sha256 || hashValues.sha1 || `${stats.mtimeMs}`;
1565
+ const hashes = [];
1566
+ if (hashValues.sha1) {
1567
+ hashes.push({ alg: "SHA-1", content: hashValues.sha1 });
1568
+ }
1569
+ if (hashValues.sha256) {
1570
+ hashes.push({ alg: "SHA-256", content: hashValues.sha256 });
1571
+ }
1572
+ return {
1573
+ "bom-ref": `crypto/related-crypto-material/public-key/${encodeURIComponent(normalizedPath)}@${hashValues.sha256 ? `sha256:${hashValues.sha256}` : version}`,
1574
+ name: basename(normalizedPath),
1575
+ type: "cryptographic-asset",
1576
+ version,
1577
+ hashes,
1578
+ cryptoProperties: {
1579
+ assetType: "related-crypto-material",
1580
+ relatedCryptoMaterialProperties: {
1581
+ type: "public-key",
1582
+ id: hashValues.sha256 || hashValues.sha1 || normalizedPath,
1583
+ state: "active",
1584
+ },
1585
+ },
1586
+ properties: uniqueProperties([
1587
+ { name: "internal:SrcFile", value: normalizedPath },
1588
+ {
1589
+ name: "cdx:crypto:trustDomain",
1590
+ value: deriveTrustedKeyDomain(normalizedPath),
1591
+ },
1592
+ { name: "cdx:crypto:keyPath", value: normalizedPath },
1593
+ {
1594
+ name: "cdx:crypto:fileExtension",
1595
+ value: extname(normalizedPath).replace(/^\./, "") || "gpg",
1596
+ },
1597
+ ]),
1598
+ };
1599
+ }
1600
+
1601
+ function deriveTrustedKeyDomain(normalizedPath) {
1602
+ const lowerPath = normalizeContainerPath(normalizedPath)?.toLowerCase() || "";
1603
+ if (lowerPath.includes("/apt/") || lowerPath.includes("/keyrings/")) {
1604
+ return "apt";
1605
+ }
1606
+ if (
1607
+ lowerPath.includes("/rpm-gpg/") ||
1608
+ lowerPath.includes("/distribution-gpg-keys/")
1609
+ ) {
1610
+ return "rpm";
1611
+ }
1612
+ if (lowerPath.includes("/apk/keys/")) {
1613
+ return "apk";
1614
+ }
1615
+ return "generic";
1616
+ }
1617
+
1618
+ function trustInspectorToolRefs() {
1619
+ return extractToolRefs({
1620
+ components: getPluginToolComponents(["trustinspector"]),
1621
+ });
1622
+ }
1623
+
1624
+ function executeTrustInspector(args, activity) {
1625
+ if (isDryRun) {
1626
+ recordActivity({
1627
+ kind: "trustinspector",
1628
+ reason:
1629
+ "Dry run mode blocks trustinspector execution and reports the requested inspection instead.",
1630
+ status: "blocked",
1631
+ ...activity,
1632
+ });
1633
+ return undefined;
1634
+ }
1635
+ if (!TRUSTINSPECTOR_BIN) {
1636
+ return undefined;
1637
+ }
1638
+ if (DEBUG_MODE) {
1639
+ console.log("Executing", TRUSTINSPECTOR_BIN, args.join(" "));
1640
+ }
1641
+ const result = safeSpawnSync(TRUSTINSPECTOR_BIN, args);
1642
+ if (result?.status !== 0 || result?.error) {
1643
+ if (DEBUG_MODE && (result?.stdout || result?.stderr)) {
1644
+ console.error(result.stdout, result.stderr);
1645
+ }
1646
+ return undefined;
1647
+ }
1648
+ if (!result?.stdout) {
1649
+ return undefined;
1650
+ }
1651
+ try {
1652
+ return JSON.parse(result.stdout);
1653
+ } catch (_err) {
1654
+ return undefined;
1655
+ }
1656
+ }
1657
+
1658
+ function normalizeTrustInspectorTargetPath(basePath) {
1659
+ if (typeof basePath !== "string") {
1660
+ return undefined;
1661
+ }
1662
+ const trimmedPath = basePath.trim();
1663
+ if (
1664
+ !trimmedPath ||
1665
+ hasDangerousUnicode(trimmedPath) ||
1666
+ /[\r\n]/.test(trimmedPath)
1667
+ ) {
1668
+ return undefined;
1669
+ }
1670
+ const resolvedPath = resolve(trimmedPath);
1671
+ if (
1672
+ !resolvedPath ||
1673
+ hasDangerousUnicode(resolvedPath) ||
1674
+ /[\r\n]/.test(resolvedPath)
1675
+ ) {
1676
+ return undefined;
1677
+ }
1678
+ if (
1679
+ (isWin &&
1680
+ !(
1681
+ resolvedPath.startsWith("\\\\") ||
1682
+ isValidDriveRoot(resolvedPath.slice(0, 3))
1683
+ )) ||
1684
+ (!isWin && !resolvedPath.startsWith("/")) ||
1685
+ !safeExistsSync(resolvedPath)
1686
+ ) {
1687
+ return undefined;
1688
+ }
1689
+ const targetStats = statSync(resolvedPath, { throwIfNoEntry: false });
1690
+ if (!targetStats?.isDirectory()) {
1691
+ return undefined;
1692
+ }
1693
+ let canonicalPath;
1694
+ try {
1695
+ canonicalPath = realpathSync(resolvedPath);
1696
+ } catch (_err) {
1697
+ return undefined;
1698
+ }
1699
+ if (
1700
+ !canonicalPath ||
1701
+ hasDangerousUnicode(canonicalPath) ||
1702
+ /[\r\n]/.test(canonicalPath)
1703
+ ) {
1704
+ return undefined;
1705
+ }
1706
+ return resolvedPath;
1707
+ }
1708
+
1709
+ function trustMaterialHashes(material) {
1710
+ const hashes = [];
1711
+ if (material?.sha1) {
1712
+ hashes.push({ alg: "SHA-1", content: material.sha1 });
1713
+ }
1714
+ if (material?.sha256) {
1715
+ hashes.push({ alg: "SHA-256", content: material.sha256 });
1716
+ }
1717
+ return hashes;
1718
+ }
1719
+
1720
+ function trustMaterialState(material) {
1721
+ const expiresAt = material?.expiresAt
1722
+ ? Date.parse(material.expiresAt)
1723
+ : Number.NaN;
1724
+ if (!Number.isNaN(expiresAt) && expiresAt < Date.now()) {
1725
+ return "expired";
1726
+ }
1727
+ return "active";
1728
+ }
1729
+
1730
+ function createTrustMaterialComponent(material) {
1731
+ const normalizedPath = normalizeContainerPath(material?.path);
1732
+ if (!normalizedPath || !material?.kind) {
1733
+ return undefined;
1734
+ }
1735
+ const hashes = trustMaterialHashes(material);
1736
+ const sharedProperties = uniqueProperties([
1737
+ { name: "internal:SrcFile", value: normalizedPath },
1738
+ ...(material?.properties || []),
1739
+ ...(material?.fingerprint
1740
+ ? [{ name: "cdx:crypto:fingerprint", value: material.fingerprint }]
1741
+ : []),
1742
+ ...(material?.algorithm
1743
+ ? [{ name: "cdx:crypto:algorithm", value: material.algorithm }]
1744
+ : []),
1745
+ ...(material?.keyStrength
1746
+ ? [{ name: "cdx:crypto:keyStrength", value: `${material.keyStrength}` }]
1747
+ : []),
1748
+ ...(material?.createdAt
1749
+ ? [{ name: "cdx:crypto:createdAt", value: material.createdAt }]
1750
+ : []),
1751
+ ...(material?.expiresAt
1752
+ ? [{ name: "cdx:crypto:expiresAt", value: material.expiresAt }]
1753
+ : []),
1754
+ ]);
1755
+ let component;
1756
+ if (material.kind === "certificate") {
1757
+ component = {
1758
+ "bom-ref": `crypto/certificate/${encodeURIComponent(material.name || normalizedPath)}@${material.sha256 ? `sha256:${material.sha256}` : material.serial || material.expiresAt || "unknown"}`,
1759
+ name: material.name || basename(normalizedPath),
1760
+ type: "cryptographic-asset",
1761
+ version:
1762
+ material.sha256 ||
1763
+ material.serial ||
1764
+ material.expiresAt ||
1765
+ "configured",
1766
+ hashes,
1767
+ description: material.subject || normalizedPath,
1768
+ cryptoProperties: {
1769
+ assetType: "certificate",
1770
+ algorithmProperties: {
1771
+ executionEnvironment: "unknown",
1772
+ implementationPlatform: "unknown",
1773
+ },
1774
+ certificateProperties: {
1775
+ serialNumber: material.serial || undefined,
1776
+ subjectName: material.subject || undefined,
1777
+ issuerName: material.issuer || undefined,
1778
+ notValidBefore: material.createdAt || undefined,
1779
+ notValidAfter: material.expiresAt || undefined,
1780
+ certificateFormat: material.format || "X.509",
1781
+ certificateFileExtension: material.fileExtension || undefined,
1782
+ fingerprint: material.fingerprint
1783
+ ? { alg: "SHA-256", content: material.fingerprint }
1784
+ : undefined,
1785
+ },
1786
+ },
1787
+ properties: uniqueProperties(
1788
+ sharedProperties.concat(
1789
+ material.trustDomain
1790
+ ? [{ name: "cdx:crypto:trustDomain", value: material.trustDomain }]
1791
+ : [],
1792
+ ),
1793
+ ),
1794
+ };
1795
+ } else {
1796
+ component = {
1797
+ "bom-ref": `crypto/related-crypto-material/public-key/${encodeURIComponent(normalizedPath)}@${material.sha256 ? `sha256:${material.sha256}` : material.keyId || "unknown"}`,
1798
+ name: material.name || basename(normalizedPath),
1799
+ type: "cryptographic-asset",
1800
+ version: material.sha256 || material.keyId || normalizedPath,
1801
+ hashes,
1802
+ cryptoProperties: {
1803
+ assetType: "related-crypto-material",
1804
+ relatedCryptoMaterialProperties: {
1805
+ type: "public-key",
1806
+ id:
1807
+ material.keyId ||
1808
+ material.fingerprint ||
1809
+ material.sha256 ||
1810
+ normalizedPath,
1811
+ state: trustMaterialState(material),
1812
+ },
1813
+ },
1814
+ properties: uniqueProperties(
1815
+ sharedProperties.concat([
1816
+ {
1817
+ name: "cdx:crypto:trustDomain",
1818
+ value:
1819
+ material.trustDomain || deriveTrustedKeyDomain(normalizedPath),
1820
+ },
1821
+ { name: "cdx:crypto:keyPath", value: normalizedPath },
1822
+ {
1823
+ name: "cdx:crypto:fileExtension",
1824
+ value:
1825
+ material.fileExtension ||
1826
+ extname(normalizedPath).replace(/^\./, "") ||
1827
+ "gpg",
1828
+ },
1829
+ ...(material?.keyId
1830
+ ? [{ name: "cdx:crypto:keyId", value: material.keyId }]
1831
+ : []),
1832
+ ...(material?.userIds || []).map((value) => ({
1833
+ name: "cdx:crypto:userId",
1834
+ value,
1835
+ })),
1836
+ ]),
1837
+ ),
1838
+ };
1839
+ }
1840
+ attachIdentityTools(component, trustInspectorToolRefs());
1841
+ return component;
1842
+ }
1843
+
1844
+ function enhanceComponentFromTrustMaterial(component, material) {
1845
+ if (!component || !material) {
1846
+ return component;
1847
+ }
1848
+ component.hashes = component.hashes?.length
1849
+ ? component.hashes
1850
+ : trustMaterialHashes(material);
1851
+ component.properties = uniqueProperties(
1852
+ (component.properties || []).concat(
1853
+ createTrustMaterialComponent(material)?.properties || [],
1854
+ ),
1855
+ );
1856
+ if (!component.cryptoProperties) {
1857
+ component.cryptoProperties =
1858
+ createTrustMaterialComponent(material)?.cryptoProperties;
1859
+ }
1860
+ attachIdentityTools(component, trustInspectorToolRefs());
1861
+ return component;
1862
+ }
1863
+
1864
+ function applyTrustMaterialEnhancements(components, materials) {
1865
+ if (!materials?.length) {
1866
+ return components || [];
1867
+ }
1868
+ const componentList = [...(components || [])];
1869
+ const bySrcFile = new Map();
1870
+ const seenRefs = new Set();
1871
+ for (const component of componentList) {
1872
+ if (component?.["bom-ref"]) {
1873
+ seenRefs.add(component["bom-ref"]);
1874
+ }
1875
+ const srcFile = (component?.properties || []).find(
1876
+ (property) => property.name === "internal:SrcFile",
1877
+ )?.value;
1878
+ if (srcFile) {
1879
+ bySrcFile.set(normalizeContainerPath(srcFile), component);
1880
+ }
1881
+ }
1882
+ for (const material of materials) {
1883
+ const srcFile = normalizeContainerPath(material?.path);
1884
+ const existing = bySrcFile.get(srcFile);
1885
+ if (
1886
+ existing &&
1887
+ existing.type === "cryptographic-asset" &&
1888
+ material?.kind === "public-key"
1889
+ ) {
1890
+ enhanceComponentFromTrustMaterial(existing, material);
1891
+ continue;
1892
+ }
1893
+ const component = createTrustMaterialComponent(material);
1894
+ if (!component?.["bom-ref"] || seenRefs.has(component["bom-ref"])) {
1895
+ continue;
1896
+ }
1897
+ seenRefs.add(component["bom-ref"]);
1898
+ componentList.push(component);
1899
+ if (srcFile) {
1900
+ bySrcFile.set(srcFile, component);
1901
+ }
1902
+ }
1903
+ return componentList;
1904
+ }
1905
+
1906
+ function collectTrustInspectorRootfsInventory(basePath) {
1907
+ const targetPath = normalizeTrustInspectorTargetPath(basePath);
1908
+ if (!targetPath) {
1909
+ return [];
1910
+ }
1911
+ const trustData = executeTrustInspector(["rootfs", targetPath], {
1912
+ target: targetPath,
1913
+ });
1914
+ return trustData?.materials || [];
1915
+ }
1916
+
1917
+ function parseAptRepositorySources(basePath) {
1918
+ const sourceFiles = [
1919
+ "/etc/apt/sources.list",
1920
+ ...walkRootfsFiles(basePath, "/etc/apt/sources.list.d").filter(
1921
+ (filePath) => filePath.endsWith(".list") || filePath.endsWith(".sources"),
1922
+ ),
1923
+ ].filter((filePath, index, values) => values.indexOf(filePath) === index);
1924
+ const entries = [];
1925
+ for (const sourceFile of sourceFiles) {
1926
+ const data = readRootfsTextFile(basePath, sourceFile);
1927
+ if (!data) {
1928
+ continue;
1929
+ }
1930
+ if (sourceFile.endsWith(".sources")) {
1931
+ entries.push(...parseDeb822AptRepositorySources(data, sourceFile));
1932
+ continue;
1933
+ }
1934
+ entries.push(...parseLegacyAptRepositorySources(data, sourceFile));
1935
+ }
1936
+ return entries;
1937
+ }
1938
+
1939
+ function parseLegacyAptRepositorySources(data, sourceFile) {
1940
+ const entries = [];
1941
+ for (const rawLine of data.split(/\r?\n/)) {
1942
+ const line = rawLine.split("#")[0].trim();
1943
+ if (!line || (!line.startsWith("deb ") && !line.startsWith("deb-src "))) {
1944
+ continue;
1945
+ }
1946
+ const match = line.match(
1947
+ /^(deb(?:-src)?)\s+(?:\[(?<options>[^\]]+)\]\s+)?(?<uri>\S+)\s+(?<suite>\S+)(?:\s+(?<components>.+))?$/,
1948
+ );
1949
+ if (!match?.groups?.uri) {
1950
+ continue;
1951
+ }
1952
+ const options = parseRepositoryOptionString(match.groups.options);
1953
+ entries.push({
1954
+ name: deriveRepositoryDisplayName(match.groups.uri, sourceFile),
1955
+ path: sourceFile,
1956
+ repoType: isPpaRepository(match.groups.uri) ? "ppa-source" : "apt-source",
1957
+ release: match.groups.suite,
1958
+ url: match.groups.uri,
1959
+ description: line,
1960
+ enabled: true,
1961
+ keyReferences: extractRepositoryKeyReferences(options["signed-by"]),
1962
+ properties: uniqueProperties([
1963
+ { name: "internal:SrcFile", value: sourceFile },
1964
+ { name: "cdx:os:repo:kind", value: match[1] },
1965
+ { name: "cdx:os:repo:url", value: match.groups.uri },
1966
+ { name: "cdx:os:repo:release", value: match.groups.suite },
1967
+ ...(match.groups.components
1968
+ ? [
1969
+ {
1970
+ name: "cdx:os:repo:components",
1971
+ value: match.groups.components,
1972
+ },
1973
+ ]
1974
+ : []),
1975
+ ...(options.arch
1976
+ ? [{ name: "cdx:os:repo:architectures", value: options.arch }]
1977
+ : []),
1978
+ ...(options["signed-by"]
1979
+ ? [{ name: "cdx:os:repo:signedBy", value: options["signed-by"] }]
1980
+ : []),
1981
+ ]),
1982
+ });
1983
+ }
1984
+ return entries;
1985
+ }
1986
+
1987
+ function parseDeb822AptRepositorySources(data, sourceFile) {
1988
+ const entries = [];
1989
+ for (const stanza of data.split(/\n\s*\n/)) {
1990
+ const fields = parseDeb822Fields(stanza);
1991
+ const uris = splitRepositoryField(fields.uris);
1992
+ const suites = splitRepositoryField(fields.suites || fields.suite);
1993
+ if (!uris.length) {
1994
+ continue;
1995
+ }
1996
+ for (const uri of uris) {
1997
+ entries.push({
1998
+ name: deriveRepositoryDisplayName(uri, sourceFile),
1999
+ path: sourceFile,
2000
+ repoType: isPpaRepository(uri) ? "ppa-source" : "apt-source",
2001
+ release: suites.join(",") || "configured",
2002
+ url: uri,
2003
+ description: stanza.trim(),
2004
+ enabled: !["no", "false", "0"].includes(
2005
+ `${fields.enabled || "yes"}`.toLowerCase(),
2006
+ ),
2007
+ keyReferences: extractRepositoryKeyReferences(fields["signed-by"]),
2008
+ properties: uniqueProperties([
2009
+ { name: "internal:SrcFile", value: sourceFile },
2010
+ { name: "cdx:os:repo:kind", value: fields.types || "deb" },
2011
+ { name: "cdx:os:repo:url", value: uri },
2012
+ ...(suites.length
2013
+ ? [{ name: "cdx:os:repo:release", value: suites.join(",") }]
2014
+ : []),
2015
+ ...(fields.components
2016
+ ? [{ name: "cdx:os:repo:components", value: fields.components }]
2017
+ : []),
2018
+ ...(fields.architectures
2019
+ ? [
2020
+ {
2021
+ name: "cdx:os:repo:architectures",
2022
+ value: fields.architectures,
2023
+ },
2024
+ ]
2025
+ : []),
2026
+ ...(fields["signed-by"]
2027
+ ? [
2028
+ {
2029
+ name: "cdx:os:repo:signedBy",
2030
+ value: fields["signed-by"],
2031
+ },
2032
+ ]
2033
+ : []),
2034
+ ]),
2035
+ });
2036
+ }
2037
+ }
2038
+ return entries;
2039
+ }
2040
+
2041
+ function parseYumRepositorySources(basePath) {
2042
+ const entries = [];
2043
+ for (const repoFile of walkRootfsFiles(basePath, "/etc/yum.repos.d").filter(
2044
+ (f) => f.endsWith(".repo"),
2045
+ )) {
2046
+ const data = readRootfsTextFile(basePath, repoFile);
2047
+ if (!data) {
2048
+ continue;
2049
+ }
2050
+ let currentSection;
2051
+ let currentConfig = {};
2052
+ const flushCurrentSection = () => {
2053
+ if (!currentSection) {
2054
+ return;
2055
+ }
2056
+ const url =
2057
+ currentConfig.baseurl ||
2058
+ currentConfig.mirrorlist ||
2059
+ currentConfig.metalink;
2060
+ if (!url) {
2061
+ return;
2062
+ }
2063
+ entries.push({
2064
+ name: currentSection,
2065
+ path: repoFile,
2066
+ repoType: "yum-source",
2067
+ release:
2068
+ `${currentConfig.enabled || "1"}` === "1" ? "enabled" : "disabled",
2069
+ url,
2070
+ description: `${repoFile}#${currentSection}`,
2071
+ enabled: `${currentConfig.enabled || "1"}` === "1",
2072
+ keyReferences: extractRepositoryKeyReferences(currentConfig.gpgkey),
2073
+ properties: uniqueProperties([
2074
+ { name: "internal:SrcFile", value: repoFile },
2075
+ { name: "cdx:os:repo:url", value: url },
2076
+ ...(currentConfig.baseurl
2077
+ ? [{ name: "cdx:os:repo:baseurl", value: currentConfig.baseurl }]
2078
+ : []),
2079
+ ...(currentConfig.mirrorlist
2080
+ ? [
2081
+ {
2082
+ name: "cdx:os:repo:mirrorlist",
2083
+ value: currentConfig.mirrorlist,
2084
+ },
2085
+ ]
2086
+ : []),
2087
+ ...(currentConfig.metalink
2088
+ ? [{ name: "cdx:os:repo:metalink", value: currentConfig.metalink }]
2089
+ : []),
2090
+ {
2091
+ name: "cdx:os:repo:enabled",
2092
+ value: `${currentConfig.enabled || "1"}`,
2093
+ },
2094
+ ...(currentConfig.gpgcheck
2095
+ ? [{ name: "cdx:os:repo:gpgcheck", value: currentConfig.gpgcheck }]
2096
+ : []),
2097
+ ...(currentConfig.gpgkey
2098
+ ? [{ name: "cdx:os:repo:gpgkey", value: currentConfig.gpgkey }]
2099
+ : []),
2100
+ ]),
2101
+ });
2102
+ };
2103
+ for (const rawLine of data.split(/\r?\n/)) {
2104
+ const line = rawLine.trim();
2105
+ if (!line || line.startsWith("#") || line.startsWith(";")) {
2106
+ continue;
2107
+ }
2108
+ if (line.startsWith("[") && line.endsWith("]")) {
2109
+ flushCurrentSection();
2110
+ currentSection = line.slice(1, -1).trim();
2111
+ currentConfig = {};
2112
+ continue;
2113
+ }
2114
+ const equalsIndex = line.indexOf("=");
2115
+ if (equalsIndex === -1) {
2116
+ continue;
2117
+ }
2118
+ currentConfig[line.slice(0, equalsIndex).trim().toLowerCase()] = line
2119
+ .slice(equalsIndex + 1)
2120
+ .trim();
2121
+ }
2122
+ flushCurrentSection();
2123
+ }
2124
+ return entries;
2125
+ }
2126
+
2127
+ function createRepositorySourceComponent(entry) {
2128
+ const version = entry.release || "configured";
2129
+ const purl = build({
2130
+ type: "generic",
2131
+ namespace: "os-repository",
2132
+ name: entry.name,
2133
+ version: version || null,
2134
+ });
2135
+ return {
2136
+ "bom-ref": decodeURIComponent(purl),
2137
+ purl,
2138
+ name: entry.name,
2139
+ type: "data",
2140
+ version,
2141
+ description: entry.description || entry.url,
2142
+ properties: uniqueProperties(
2143
+ [
2144
+ { name: "internal:SrcFile", value: entry.path },
2145
+ { name: "cdx:os:repo:type", value: entry.repoType },
2146
+ { name: "cdx:os:repo:url", value: entry.url },
2147
+ {
2148
+ name: "cdx:os:repo:enabled",
2149
+ value: entry.enabled === false ? "false" : "true",
2150
+ },
2151
+ ].concat(entry.properties || []),
2152
+ ),
2153
+ };
2154
+ }
2155
+
2156
+ function uniqueRepositoryEntries(entries) {
2157
+ const seen = new Set();
2158
+ const results = [];
2159
+ for (const entry of entries || []) {
2160
+ if (!entry?.name || !entry?.path || !entry?.url) {
2161
+ continue;
2162
+ }
2163
+ const key = `${entry.repoType}\u0000${entry.path}\u0000${entry.url}\u0000${entry.release || ""}`;
2164
+ if (seen.has(key)) {
2165
+ continue;
2166
+ }
2167
+ seen.add(key);
2168
+ results.push(entry);
2169
+ }
2170
+ return results;
2171
+ }
2172
+
2173
+ function deriveRepositoryDisplayName(url, sourceFile) {
2174
+ try {
2175
+ const parsedUrl = new URL(url);
2176
+ const repoPath = parsedUrl.pathname.replace(/\/+$/, "") || "/";
2177
+ return `${parsedUrl.hostname}${repoPath}`;
2178
+ } catch (_err) {
2179
+ return basename(sourceFile);
2180
+ }
2181
+ }
2182
+
2183
+ function isPpaRepository(url) {
2184
+ try {
2185
+ const hostname = new URL(`${url || ""}`).hostname.toLowerCase();
2186
+ return (
2187
+ hostname === "ppa.launchpadcontent.net" ||
2188
+ hostname === "ppa.launchpad.net"
2189
+ );
2190
+ } catch (_err) {
2191
+ return false;
2192
+ }
2193
+ }
2194
+
2195
+ function parseRepositoryOptionString(optionString) {
2196
+ const options = {};
2197
+ for (const token of `${optionString || ""}`.split(/\s+/).filter(Boolean)) {
2198
+ const [key, ...valueParts] = token.split("=");
2199
+ if (!key || !valueParts.length) {
2200
+ continue;
2201
+ }
2202
+ options[key.toLowerCase()] = valueParts.join("=");
2203
+ }
2204
+ return options;
2205
+ }
2206
+
2207
+ function parseDeb822Fields(stanza) {
2208
+ const fields = {};
2209
+ let currentKey;
2210
+ for (const rawLine of stanza.split(/\r?\n/)) {
2211
+ if (!rawLine.trim()) {
2212
+ continue;
2213
+ }
2214
+ if (/^[ \t]/.test(rawLine) && currentKey) {
2215
+ fields[currentKey] = `${fields[currentKey]} ${rawLine.trim()}`.trim();
2216
+ continue;
2217
+ }
2218
+ const separatorIndex = rawLine.indexOf(":");
2219
+ if (separatorIndex === -1) {
2220
+ continue;
2221
+ }
2222
+ currentKey = rawLine.slice(0, separatorIndex).trim().toLowerCase();
2223
+ fields[currentKey] = rawLine.slice(separatorIndex + 1).trim();
2224
+ }
2225
+ return fields;
2226
+ }
2227
+
2228
+ function splitRepositoryField(value) {
2229
+ return `${value || ""}`.split(/\s+/).filter(Boolean);
2230
+ }
2231
+
2232
+ function extractRepositoryKeyReferences(value) {
2233
+ return uniqueSortedStrings(
2234
+ `${value || ""}`
2235
+ .split(/[\s,]+/)
2236
+ .map((part) => normalizeLocalRepositoryReference(part))
2237
+ .filter(Boolean),
2238
+ );
2239
+ }
2240
+
2241
+ function normalizeLocalRepositoryReference(value) {
2242
+ if (!value) {
2243
+ return undefined;
2244
+ }
2245
+ const trimmedValue = `${value}`.trim();
2246
+ if (!trimmedValue || trimmedValue.includes("BEGIN PGP PUBLIC KEY BLOCK")) {
2247
+ return undefined;
2248
+ }
2249
+ if (trimmedValue.startsWith("file://")) {
2250
+ return normalizeContainerPath(trimmedValue.slice("file://".length));
2251
+ }
2252
+ if (trimmedValue.startsWith("/")) {
2253
+ return normalizeContainerPath(trimmedValue);
2254
+ }
2255
+ return undefined;
2256
+ }
2257
+
2258
+ function readRootfsTextFile(basePath, normalizedPath) {
2259
+ const absolutePath = join(basePath, normalizedPath.replace(/^\/+/, ""));
2260
+ if (!safeExistsSync(absolutePath)) {
2261
+ return undefined;
2262
+ }
2263
+ try {
2264
+ return readFileSync(absolutePath, "utf-8");
2265
+ } catch (_err) {
2266
+ return undefined;
2267
+ }
2268
+ }
2269
+
2270
+ async function createOSPackageFileComponents(basePath, osPackageEntries) {
2271
+ const components = [];
2272
+ const dependenciesList = [];
2273
+ const ownedFilePaths = new Set();
2274
+ const services = [];
2275
+ const componentByPath = new Map();
2276
+ for (const packageEntry of osPackageEntries) {
2277
+ const commandPathSet = new Set(packageEntry.commandPaths || []);
2278
+ const providedRefs = new Set();
2279
+ for (const filePath of packageEntry.files || []) {
2280
+ if (!filePath) {
2281
+ continue;
2282
+ }
2283
+ ownedFilePaths.add(filePath);
2284
+ let fileComponent = componentByPath.get(filePath);
2285
+ if (!fileComponent) {
2286
+ fileComponent = await createOSPackageFileComponent(
2287
+ basePath,
2288
+ filePath,
2289
+ commandPathSet,
2290
+ );
2291
+ if (!fileComponent) {
2292
+ continue;
2293
+ }
2294
+ componentByPath.set(filePath, fileComponent);
2295
+ components.push(fileComponent);
2296
+ }
2297
+ providedRefs.add(fileComponent["bom-ref"]);
2298
+ }
2299
+ const serviceResults = await createOSPackageServices(
2300
+ basePath,
2301
+ packageEntry,
2302
+ componentByPath,
2303
+ );
2304
+ for (const service of serviceResults.services) {
2305
+ services.push(service);
2306
+ providedRefs.add(service["bom-ref"]);
2307
+ }
2308
+ if (serviceResults.dependenciesList.length) {
2309
+ dependenciesList.push(...serviceResults.dependenciesList);
2310
+ }
2311
+ if (providedRefs.size) {
2312
+ dependenciesList.push({
2313
+ ref: packageEntry.packageRef,
2314
+ provides: Array.from(providedRefs).sort(),
2315
+ });
2316
+ }
2317
+ }
2318
+ return {
2319
+ components,
2320
+ dependenciesList,
2321
+ ownedFilePaths: Array.from(ownedFilePaths).sort(),
2322
+ services: dedupeServices(services),
2323
+ };
2324
+ }
2325
+
2326
+ async function createOSPackageFileComponent(
2327
+ basePath,
2328
+ filePath,
2329
+ commandPathSet,
2330
+ ) {
2331
+ const normalizedFilePath = normalizeContainerPath(filePath);
2332
+ if (!normalizedFilePath) {
2333
+ return undefined;
2334
+ }
2335
+ let hashes;
2336
+ try {
2337
+ const hashValues = await multiChecksumFile(
2338
+ ["md5", "sha1"],
2339
+ join(basePath, normalizedFilePath.replace(/^\/+/, "")),
2340
+ );
2341
+ hashes = [
2342
+ { alg: "MD5", content: hashValues.md5 },
2343
+ { alg: "SHA-1", content: hashValues.sha1 },
2344
+ ];
2345
+ } catch (_e) {
2346
+ // ignore
2347
+ }
2348
+ const fileName = basename(normalizedFilePath);
2349
+ const stats = statSync(
2350
+ join(basePath, normalizedFilePath.replace(/^\/+/, "")),
2351
+ {
2352
+ throwIfNoEntry: false,
2353
+ },
2354
+ );
2355
+ if (!stats || stats.isDirectory()) {
2356
+ return undefined;
2357
+ }
2358
+ let linkedName;
2359
+ try {
2360
+ const resolvedPath = realpathSync(
2361
+ join(basePath, normalizedFilePath.replace(/^\/+/, "")),
2362
+ );
2363
+ const linkStats = lstatSync(
2364
+ join(basePath, normalizedFilePath.replace(/^\/+/, "")),
2365
+ );
2366
+ if (linkStats?.isSymbolicLink()) {
2367
+ linkedName = basename(resolvedPath);
2368
+ recordSymlinkResolution(
2369
+ join(basePath, normalizedFilePath.replace(/^\/+/, "")),
2370
+ resolvedPath,
2371
+ {
2372
+ basePath,
2373
+ metadata: {
2374
+ resolutionKind: "container-os-package-file",
2375
+ },
2376
+ },
2377
+ );
2378
+ }
2379
+ } catch (_e) {
2380
+ // ignore
2381
+ }
2382
+ const fileType = determineOwnedFileType(
2383
+ normalizedFilePath,
2384
+ stats,
2385
+ commandPathSet,
2386
+ );
2387
+ const properties = [{ name: "internal:SrcFile", value: normalizedFilePath }];
2388
+ if (fileType === "executable") {
2389
+ properties.push({ name: "internal:is_executable", value: "true" });
2390
+ } else if (fileType === "shared_library") {
2391
+ properties.push({ name: "internal:is_shared_library", value: "true" });
2392
+ } else {
2393
+ properties.push({ name: "internal:is_file", value: "true" });
2394
+ }
2395
+ properties.push(...createContainerRiskProperties(fileName, linkedName));
2396
+ properties.push(...createGtfoBinsProperties(fileName, linkedName));
2397
+ // The file path is a purl subpath, not a qualifier: `path` is not among the
2398
+ // qualifiers the generic type allows. The SrcFile property above already
2399
+ // carries the absolute path, so the subpath is stripped to a relative one as
2400
+ // the spec requires.
2401
+ const purl = tryBuildPurl({
2402
+ type: "generic",
2403
+ name: fileName,
2404
+ subpath: normalizedFilePath ? normalizedFilePath.replace(/^\/+/, "") : null,
2405
+ });
2406
+ return {
2407
+ name: fileName,
2408
+ type: "file",
2409
+ ...(purl ? { purl, "bom-ref": purl } : {}),
2410
+ hashes,
2411
+ properties,
2412
+ evidence: {
2413
+ identity: [
2414
+ {
2415
+ field: "purl",
2416
+ confidence: 0,
2417
+ methods: [
2418
+ {
2419
+ technique: "filename",
2420
+ confidence: 0,
2421
+ value: normalizedFilePath,
2422
+ },
2423
+ ],
2424
+ concludedValue: normalizedFilePath,
2425
+ },
2426
+ ],
2427
+ },
2428
+ };
2429
+ }
2430
+
2431
+ async function createOSPackageServices(
2432
+ basePath,
2433
+ packageEntry,
2434
+ componentByPath,
2435
+ ) {
2436
+ const services = [];
2437
+ const dependenciesList = [];
2438
+ for (const filePath of packageEntry.files || []) {
2439
+ const serviceDescriptor = parseOwnedServiceFile(basePath, filePath);
2440
+ if (!serviceDescriptor) {
2441
+ continue;
2442
+ }
2443
+ const service = buildOwnedServiceComponent(packageEntry, serviceDescriptor);
2444
+ services.push(service);
2445
+ const dependsOn = new Set([packageEntry.packageRef]);
2446
+ const fileComponent = componentByPath.get(filePath);
2447
+ if (fileComponent?.["bom-ref"]) {
2448
+ dependsOn.add(fileComponent["bom-ref"]);
2449
+ }
2450
+ for (const execPath of serviceDescriptor.execPaths) {
2451
+ const execComponent = componentByPath.get(execPath);
2452
+ if (execComponent?.["bom-ref"]) {
2453
+ dependsOn.add(execComponent["bom-ref"]);
2454
+ }
2455
+ }
2456
+ dependenciesList.push({
2457
+ ref: service["bom-ref"],
2458
+ dependsOn: Array.from(dependsOn).sort(),
2459
+ });
2460
+ }
2461
+ return { services, dependenciesList };
2462
+ }
2463
+
2464
+ function parseOwnedServiceFile(basePath, filePath) {
2465
+ const normalizedFilePath = normalizeContainerPath(filePath);
2466
+ if (!normalizedFilePath) {
2467
+ return undefined;
2468
+ }
2469
+ const fileContentPath = join(
2470
+ basePath,
2471
+ normalizedFilePath.replace(/^\/+/, ""),
2472
+ );
2473
+ if (!safeExistsSync(fileContentPath)) {
2474
+ return undefined;
2475
+ }
2476
+ if (isSystemdServiceFile(normalizedFilePath)) {
2477
+ const unitData = readFileSync(fileContentPath, "utf-8");
2478
+ const unitMetadata = parseSystemdUnitFile(unitData);
2479
+ return {
2480
+ description: unitMetadata.description,
2481
+ execPaths: unitMetadata.execPaths,
2482
+ filePath: normalizedFilePath,
2483
+ manager: "systemd",
2484
+ name: basename(normalizedFilePath).replace(/\.[^.]+$/, ""),
2485
+ properties: [
2486
+ { name: "cdx:service:manager", value: "systemd" },
2487
+ {
2488
+ name: "cdx:service:unitType",
2489
+ value: basename(normalizedFilePath).split(".").pop() || "service",
2490
+ },
2491
+ ...unitMetadata.properties,
2492
+ ],
2493
+ };
2494
+ }
2495
+ if (isInitServiceFile(normalizedFilePath)) {
2496
+ const initData = readFileSync(fileContentPath, "utf-8");
2497
+ const initMetadata = parseInitScriptMetadata(initData);
2498
+ return {
2499
+ description: initMetadata.description,
2500
+ execPaths: initMetadata.execPaths,
2501
+ filePath: normalizedFilePath,
2502
+ manager: "sysvinit",
2503
+ name: initMetadata.name || basename(normalizedFilePath),
2504
+ properties: [
2505
+ { name: "cdx:service:manager", value: "sysvinit" },
2506
+ ...initMetadata.properties,
2507
+ ],
2508
+ };
2509
+ }
2510
+ return undefined;
2511
+ }
2512
+
2513
+ function buildOwnedServiceComponent(packageEntry, serviceDescriptor) {
2514
+ const serviceName = sanitizeServiceName(serviceDescriptor.name);
2515
+ const manager = serviceDescriptor.manager;
2516
+ return {
2517
+ "bom-ref": `urn:service:${manager}:${sanitizeServiceRefToken(packageEntry.packageRef)}:${sanitizeServiceRefToken(serviceName)}`,
2518
+ name: serviceName,
2519
+ version: packageEntry.packageVersion || "latest",
2520
+ group: packageEntry.packageName || manager,
2521
+ description: serviceDescriptor.description,
2522
+ properties: uniqueProperties(
2523
+ [
2524
+ { name: "internal:SrcFile", value: serviceDescriptor.filePath },
2525
+ { name: "cdx:service:packageRef", value: packageEntry.packageRef },
2526
+ { name: "cdx:service:packageName", value: packageEntry.packageName },
2527
+ ].concat(serviceDescriptor.properties || []),
2528
+ ),
2529
+ };
2530
+ }
2531
+
2532
+ function parseSystemdUnitFile(unitData) {
2533
+ const properties = [];
2534
+ const execPaths = new Set();
2535
+ let description;
2536
+ let currentSection = "";
2537
+ for (const rawLine of unitData.split(/\r?\n/)) {
2538
+ const line = rawLine.trim();
2539
+ if (!line || line.startsWith("#") || line.startsWith(";")) {
2540
+ continue;
2541
+ }
2542
+ if (line.startsWith("[") && line.endsWith("]")) {
2543
+ currentSection = line.slice(1, -1);
2544
+ continue;
2545
+ }
2546
+ const equalsIndex = line.indexOf("=");
2547
+ if (equalsIndex === -1) {
2548
+ continue;
2549
+ }
2550
+ const key = line.slice(0, equalsIndex).trim();
2551
+ const value = line.slice(equalsIndex + 1).trim();
2552
+ if (!value) {
2553
+ continue;
2554
+ }
2555
+ if (currentSection === "Unit" && key === "Description") {
2556
+ description = value;
2557
+ }
2558
+ if (currentSection === "Service") {
2559
+ if (key.startsWith("Exec")) {
2560
+ properties.push({ name: `cdx:service:${key}`, value });
2561
+ const execPath = extractExecPath(value);
2562
+ if (execPath) {
2563
+ execPaths.add(execPath);
2564
+ }
2565
+ } else if (
2566
+ ["Type", "User", "Group", "WorkingDirectory", "Restart"].includes(key)
2567
+ ) {
2568
+ properties.push({ name: `cdx:service:${key}`, value });
2569
+ }
2570
+ }
2571
+ if (
2572
+ currentSection === "Install" &&
2573
+ ["WantedBy", "RequiredBy", "Alias"].includes(key)
2574
+ ) {
2575
+ properties.push({ name: `cdx:service:${key}`, value });
2576
+ }
2577
+ if (
2578
+ currentSection === "Unit" &&
2579
+ ["After", "Requires", "Wants"].includes(key)
2580
+ ) {
2581
+ properties.push({ name: `cdx:service:${key}`, value });
2582
+ }
2583
+ }
2584
+ return {
2585
+ description,
2586
+ execPaths: Array.from(execPaths).sort(),
2587
+ properties: uniqueProperties(properties),
2588
+ };
2589
+ }
2590
+
2591
+ function parseInitScriptMetadata(initData) {
2592
+ const properties = [];
2593
+ const execPaths = new Set();
2594
+ let description;
2595
+ let name;
2596
+ for (const rawLine of initData.split(/\r?\n/)) {
2597
+ const line = rawLine.trim();
2598
+ if (!line.startsWith("#")) {
2599
+ const execPath = extractExecPath(line);
2600
+ if (execPath) {
2601
+ execPaths.add(execPath);
2602
+ }
2603
+ continue;
2604
+ }
2605
+ const normalized = line.replace(/^#+\s*/, "");
2606
+ if (normalized.startsWith("Provides:")) {
2607
+ const providedName = normalized
2608
+ .replace(/^Provides:\s*/, "")
2609
+ .split(/\s+/)[0];
2610
+ if (providedName) {
2611
+ name = providedName;
2612
+ properties.push({ name: "cdx:service:Provides", value: providedName });
2613
+ }
2614
+ continue;
2615
+ }
2616
+ if (normalized.startsWith("Short-Description:")) {
2617
+ description = normalized.replace(/^Short-Description:\s*/, "");
2618
+ continue;
2619
+ }
2620
+ if (normalized.startsWith("Description:")) {
2621
+ description = normalized.replace(/^Description:\s*/, "");
2622
+ continue;
2623
+ }
2624
+ if (normalized.startsWith("Required-Start:")) {
2625
+ properties.push({
2626
+ name: "cdx:service:RequiredStart",
2627
+ value: normalized.replace(/^Required-Start:\s*/, ""),
2628
+ });
2629
+ continue;
2630
+ }
2631
+ if (normalized.startsWith("Default-Start:")) {
2632
+ properties.push({
2633
+ name: "cdx:service:DefaultStart",
2634
+ value: normalized.replace(/^Default-Start:\s*/, ""),
2635
+ });
2636
+ }
2637
+ }
2638
+ return {
2639
+ description,
2640
+ execPaths: Array.from(execPaths).sort(),
2641
+ name,
2642
+ properties: uniqueProperties(properties),
2643
+ };
2644
+ }
2645
+
2646
+ function extractExecPath(commandLine) {
2647
+ if (!commandLine) {
2648
+ return undefined;
2649
+ }
2650
+ const sanitized = commandLine.replace(/^[\-@:+!|]+/, "").trim();
2651
+ const match = sanitized.match(/^("[^"]+"|'[^']+'|\S+)/);
2652
+ if (!match?.[1]) {
2653
+ return undefined;
2654
+ }
2655
+ const candidate = match[1].replace(/^['"]|['"]$/g, "");
2656
+ return candidate.startsWith("/")
2657
+ ? normalizeContainerPath(candidate)
2658
+ : undefined;
2659
+ }
2660
+
2661
+ function determineOwnedFileType(filePath, stats, commandPathSet) {
2662
+ if (!stats) {
2663
+ return "file";
2664
+ }
2665
+ if (isSharedLibraryPath(filePath)) {
2666
+ return "shared_library";
2667
+ }
2668
+ if (commandPathSet?.has(filePath)) {
2669
+ return "executable";
2670
+ }
2671
+ if (stats.mode & 0o111) {
2672
+ return "executable";
2673
+ }
2674
+ return "file";
2675
+ }
2676
+
2677
+ function isSharedLibraryPath(filePath) {
2678
+ return /(?:^|\/)[^/]+\.(?:so(?:\.[^/]+)?|a|lib|dll)$/i.test(filePath);
2679
+ }
2680
+
2681
+ function isSystemdServiceFile(filePath) {
2682
+ return /\/(?:etc|lib|usr\/lib)\/systemd\/system\/.+\.(?:service|socket|timer|mount|path|target|slice|automount)$/i.test(
2683
+ filePath,
2684
+ );
2685
+ }
2686
+
2687
+ function isInitServiceFile(filePath) {
2688
+ return /\/(?:etc\/init\.d|etc\/rc\.d\/init\.d)\/.+/i.test(filePath);
2689
+ }
2690
+
2691
+ function sanitizeServiceName(value) {
2692
+ return String(value || "service").trim() || "service";
2693
+ }
2694
+
2695
+ function sanitizeServiceRefToken(value) {
2696
+ return (
2697
+ String(value || "service")
2698
+ .toLowerCase()
2699
+ .replace(/[^a-z0-9._:-]+/g, "-")
2700
+ .replace(/^-+|-+$/g, "") || "service"
2701
+ );
2702
+ }
2703
+
2704
+ function uniqueProperties(properties) {
2705
+ const seen = new Set();
2706
+ const uniqueValues = [];
2707
+ for (const property of properties || []) {
2708
+ if (!property?.name || !property?.value) {
2709
+ continue;
2710
+ }
2711
+ const key = `${property.name}\u0000${property.value}`;
2712
+ if (seen.has(key)) {
2713
+ continue;
2714
+ }
2715
+ seen.add(key);
2716
+ uniqueValues.push(property);
2717
+ }
2718
+ return uniqueValues;
2719
+ }
2720
+
2721
+ function uniqueSortedStrings(values) {
2722
+ return Array.from(new Set((values || []).filter(Boolean))).sort();
2723
+ }
2724
+
2725
+ function dedupeServices(services) {
2726
+ const serviceMap = new Map();
2727
+ for (const service of services || []) {
2728
+ if (!service?.["bom-ref"]) {
2729
+ continue;
2730
+ }
2731
+ if (!serviceMap.has(service["bom-ref"])) {
2732
+ serviceMap.set(service["bom-ref"], {
2733
+ ...service,
2734
+ properties: uniqueProperties(service.properties),
2735
+ });
2736
+ continue;
2737
+ }
2738
+ const existing = serviceMap.get(service["bom-ref"]);
2739
+ existing.properties = uniqueProperties(
2740
+ (existing.properties || []).concat(service.properties || []),
2741
+ );
2742
+ }
2743
+ return Array.from(serviceMap.values());
2744
+ }
2745
+
2746
+ function normalizeContainerPath(filePath) {
2747
+ if (!filePath) {
2748
+ return undefined;
2749
+ }
2750
+ const normalized = filePath.replace(/\\/g, "/").replace(/^\/+/, "/");
2751
+ return normalized.startsWith("/") ? normalized : `/${normalized}`;
2752
+ }
2753
+
2754
+ /**
2755
+ * Remove the `modularitylabel` qualifier from a component's purl and return its
2756
+ * decoded value.
2757
+ *
2758
+ * The label is a Red Hat modularity concept that trivy expresses as a purl
2759
+ * qualifier. It is not a qualifier the rpm purl type defines, and its value is
2760
+ * a colon-separated stream that trivy percent-encodes, which a canonical purl
2761
+ * does not. Either alone makes the purl unparseable, so the label travels as a
2762
+ * component property instead.
2763
+ *
2764
+ * @param {object} comp Component whose purl is rewritten in place
2765
+ * @returns {string|undefined} The decoded label, when one was present
2766
+ */
2767
+ function extractModularityLabel(comp) {
2768
+ const [base, query] = `${comp.purl}`.split("?");
2769
+ if (!query) {
2770
+ return undefined;
2771
+ }
2772
+ let label;
2773
+ const kept = query.split("&").filter((pair) => {
2774
+ if (!pair.startsWith("modularitylabel=")) {
2775
+ return true;
2776
+ }
2777
+ try {
2778
+ label = decodeURIComponent(pair.slice("modularitylabel=".length));
2779
+ } catch {
2780
+ label = pair.slice("modularitylabel=".length);
2781
+ }
2782
+ return false;
2783
+ });
2784
+ if (label === undefined) {
2785
+ return undefined;
2786
+ }
2787
+ comp.purl = kept.length ? `${base}?${kept.join("&")}` : base;
2788
+ return label;
2789
+ }
2790
+
2791
+ // Detect common sdks and runtimes from the name
2792
+ function detectSdksRuntimes(comp, bundledSdks, bundledRuntimes) {
2793
+ if (!comp?.name) {
2794
+ return;
2795
+ }
2796
+ if (/dotnet[6-9]?-sdk/.test(comp.name)) {
2797
+ bundledSdks.add(comp.name);
2798
+ }
2799
+ if (
2800
+ /dotnet[6-9]?-runtime/.test(comp.name) ||
2801
+ comp.name.includes("aspnet-runtime") ||
2802
+ /aspnetcore[6-9]?-runtime/.test(comp.name)
2803
+ ) {
2804
+ bundledRuntimes.add(comp.name);
2805
+ }
2806
+ // TODO: Need to test this for a range of base images
2807
+ if (COMMON_RUNTIMES.includes(comp.name)) {
2808
+ bundledRuntimes.add(comp.name);
2809
+ }
2810
+ }
2811
+
2812
+ const retrieveDependencies = (tmpDependencies, origBomRef, comp) => {
2813
+ try {
2814
+ const tmpDependsOn =
2815
+ tmpDependencies[origBomRef] || tmpDependencies[comp["bom-ref"]] || [];
2816
+ const dependsOn = new Set();
2817
+ tmpDependsOn.forEach((d) => {
2818
+ try {
2819
+ const compPurl = Purl.parse(comp.purl);
2820
+ const tmpPurl = Purl.parse(d.replace("none", compPurl.type));
2821
+ tmpPurl.type = compPurl.type;
2822
+ // FIXME: Check if this hack is still needed with the latest trivy
2823
+ if (OS_PURL_TYPES.includes(compPurl.type)) {
2824
+ tmpPurl.namespace = compPurl.namespace;
2825
+ tmpPurl.qualifiers = tmpPurl.qualifiers || {};
2826
+ if (compPurl.qualifiers) {
2827
+ if (compPurl.qualifiers.distro_name) {
2828
+ tmpPurl.qualifiers.distro_name = compPurl.qualifiers.distro_name;
2829
+ }
2830
+ if (compPurl.qualifiers.distro) {
2831
+ tmpPurl.qualifiers.distro = compPurl.qualifiers.distro;
2832
+ }
2833
+ }
2834
+ }
2835
+ if (tmpPurl.qualifiers) {
2836
+ if (
2837
+ tmpPurl.qualifiers.epoch &&
2838
+ !tmpPurl.version.startsWith(`${tmpPurl.qualifiers.epoch}:`)
2839
+ ) {
2840
+ tmpPurl.version = `${tmpPurl.qualifiers.epoch}:${tmpPurl.version}`;
2841
+ }
2842
+ }
2843
+ // Prevents purls ending with ?
2844
+ if (!Object.keys(tmpPurl.qualifiers).length) {
2845
+ tmpPurl.qualifiers = undefined;
2846
+ }
2847
+ dependsOn.add(decodeURIComponent(tmpPurl.toString()));
2848
+ } catch (_e) {
2849
+ // ignore
2850
+ }
2851
+ });
2852
+ return { ref: comp["bom-ref"], dependsOn: Array.from(dependsOn).sort() };
2853
+ } catch (_e) {
2854
+ // ignore
2855
+ }
2856
+ return undefined;
2857
+ };
2858
+
2859
+ function isHostInspectionPath(value) {
2860
+ if (!value) {
2861
+ return false;
2862
+ }
2863
+ if (platform === "darwin") {
2864
+ return value.startsWith("/");
2865
+ }
2866
+ if (platform === "windows") {
2867
+ return /^[a-z]:\\/i.test(value) || /^\\\\/.test(value);
2868
+ }
2869
+ return false;
2870
+ }
2871
+
2872
+ function isDarwinSystemHostPath(value) {
2873
+ if (typeof value !== "string") {
2874
+ return false;
2875
+ }
2876
+ return (
2877
+ value.startsWith("/bin/") ||
2878
+ value.startsWith("/sbin/") ||
2879
+ value.startsWith("/System/") ||
2880
+ value.startsWith("/usr/bin/") ||
2881
+ value.startsWith("/usr/libexec/") ||
2882
+ value.startsWith("/usr/sbin/") ||
2883
+ value.startsWith("/Library/Apple/System/") ||
2884
+ value.startsWith("/System/Volumes/Preboot/Cryptexes/")
2885
+ );
2886
+ }
2887
+
2888
+ function shouldInspectComponentHostPath(value) {
2889
+ if (!isHostInspectionPath(value)) {
2890
+ return false;
2891
+ }
2892
+ if (platform === "darwin") {
2893
+ return !(
2894
+ value.toLowerCase().endsWith(".plist") || isDarwinSystemHostPath(value)
2895
+ );
2896
+ }
2897
+ return true;
2898
+ }
2899
+
2900
+ function shouldInspectComponentTrust(component) {
2901
+ if (platform !== "darwin") {
2902
+ return true;
2903
+ }
2904
+ const queryCategory = `${
2905
+ (component?.properties || []).find(
2906
+ (property) => property?.name === "cdx:osquery:category",
2907
+ )?.value || ""
2908
+ }`.trim();
2909
+ if (!queryCategory) {
2910
+ return true;
2911
+ }
2912
+ return new Set(["launchd_services", "startup_items", "running_apps"]).has(
2913
+ queryCategory,
2914
+ );
2915
+ }
2916
+
2917
+ function extractComponentHostPaths(component) {
2918
+ if (!shouldInspectComponentTrust(component)) {
2919
+ return [];
2920
+ }
2921
+ const pathPropertyNames = new Set([
2922
+ "path",
2923
+ "bundle_path",
2924
+ "bundle_executable",
2925
+ "executable",
2926
+ "program",
2927
+ "image_path",
2928
+ "binary_path",
2929
+ "action_path",
2930
+ ]);
2931
+ const paths = [];
2932
+ for (const property of component?.properties || []) {
2933
+ const propertyName = `${property?.name || ""}`.toLowerCase();
2934
+ if (!pathPropertyNames.has(propertyName)) {
2935
+ continue;
2936
+ }
2937
+ const normalizedValue = `${property?.value || ""}`.trim();
2938
+ if (shouldInspectComponentHostPath(normalizedValue)) {
2939
+ paths.push(normalizedValue);
2940
+ }
2941
+ }
2942
+ return uniqueSortedStrings(paths);
2943
+ }
2944
+
2945
+ function createHostTrustFindingComponent(finding) {
2946
+ if (!finding?.kind || !finding?.name) {
2947
+ return undefined;
2948
+ }
2949
+ const purl = build({
2950
+ type: "generic",
2951
+ namespace: "host-trust",
2952
+ name: finding.name,
2953
+ version: finding.version || "observed",
2954
+ });
2955
+ const component = {
2956
+ "bom-ref": decodeURIComponent(purl),
2957
+ name: finding.name,
2958
+ version: finding.version || "observed",
2959
+ description: finding.description,
2960
+ purl,
2961
+ type: "data",
2962
+ properties: uniqueProperties(
2963
+ (finding.properties || []).concat([
2964
+ { name: "cdx:trustinspector:kind", value: finding.kind },
2965
+ ...(finding.path
2966
+ ? [{ name: "internal:SrcFile", value: finding.path }]
2967
+ : []),
2968
+ ]),
2969
+ ),
2970
+ };
2971
+ if (finding.sha256) {
2972
+ component.hashes = [{ alg: "SHA-256", content: finding.sha256 }];
2973
+ }
2974
+ attachIdentityTools(component, trustInspectorToolRefs());
2975
+ return component;
2976
+ }
2977
+
2978
+ /**
2979
+ * Batch-enrich operating-system components with host-path trust data using the
2980
+ * `trustinspector` helper on darwin and Windows.
2981
+ *
2982
+ * @param {Object[]} [components=[]] OS components to enrich.
2983
+ * @returns {{components: Object[], tools: Object[]}} The enriched components
2984
+ * and any trust-inspector tool components that should be attached to the BOM
2985
+ * metadata.
2986
+ */
2987
+ export function enrichOSComponentsWithTrustData(components = []) {
2988
+ if (!["darwin", "windows"].includes(platform) || !TRUSTINSPECTOR_BIN) {
2989
+ return { components, tools: [] };
2990
+ }
2991
+ const mergedComponents = [...components];
2992
+ const pathInspectionCandidates = uniqueSortedStrings(
2993
+ components.flatMap((component) => extractComponentHostPaths(component)),
2994
+ );
2995
+ if (pathInspectionCandidates.length) {
2996
+ const inspectionMap = new Map();
2997
+ const batchSize = 200;
2998
+ for (
2999
+ let offset = 0;
3000
+ offset < pathInspectionCandidates.length;
3001
+ offset += batchSize
3002
+ ) {
3003
+ const inspectionBatch = pathInspectionCandidates.slice(
3004
+ offset,
3005
+ offset + batchSize,
3006
+ );
3007
+ const inspectionData = executeTrustInspector(
3008
+ ["paths", ...inspectionBatch],
3009
+ {
3010
+ target: `${inspectionBatch.length} path(s)`,
3011
+ },
3012
+ );
3013
+ for (const inspection of inspectionData?.inspections || []) {
3014
+ inspectionMap.set(
3015
+ inspection.path,
3016
+ uniqueProperties(inspection.properties || []),
3017
+ );
3018
+ }
3019
+ }
3020
+ if (inspectionMap.size) {
3021
+ for (const component of mergedComponents) {
3022
+ const matchingProperties = extractComponentHostPaths(component)
3023
+ .map((candidatePath) => inspectionMap.get(candidatePath))
3024
+ .filter(Boolean)
3025
+ .flat();
3026
+ if (!matchingProperties.length) {
3027
+ continue;
3028
+ }
3029
+ component.properties = uniqueProperties(
3030
+ (component.properties || []).concat(matchingProperties),
3031
+ );
3032
+ attachIdentityTools(component, trustInspectorToolRefs());
3033
+ }
3034
+ }
3035
+ }
3036
+ const hostData = executeTrustInspector(["host"], { target: platform });
3037
+ const hostComponents = (hostData?.hostFindings || [])
3038
+ .map((finding) => createHostTrustFindingComponent(finding))
3039
+ .filter(Boolean);
3040
+ if (hostComponents.length) {
3041
+ mergedComponents.push(...hostComponents);
3042
+ }
3043
+ return {
3044
+ components: mergedComponents,
3045
+ tools:
3046
+ pathInspectionCandidates.length || hostComponents.length
3047
+ ? getPluginToolComponents(["trustinspector"])
3048
+ : [],
3049
+ };
3050
+ }
3051
+
3052
+ /**
3053
+ * Execute a SQL query against the bundled osquery binary and return the parsed
3054
+ * JSON result.
3055
+ *
3056
+ * @param {string} query The SQL query string to run.
3057
+ * @returns {Object|undefined} The parsed JSON result, or undefined when the
3058
+ * binary is unavailable, dry-run blocks execution, or the output cannot be
3059
+ * parsed.
3060
+ */
3061
+ export function executeOsQuery(query) {
3062
+ if (isDryRun) {
3063
+ recordActivity({
3064
+ kind: "osquery",
3065
+ reason:
3066
+ "Dry run mode blocks osquery execution and reports the query instead.",
3067
+ status: "blocked",
3068
+ target: query,
3069
+ });
3070
+ return undefined;
3071
+ }
3072
+ if (OSQUERY_BIN) {
3073
+ if (!query.endsWith(";")) {
3074
+ query = `${query};`;
3075
+ }
3076
+ const args = ["--S", "--disable_database", "--json", query];
3077
+ // On darwin, we need to disable the safety check and run cdxgen with sudo
3078
+ // https://github.com/osquery/osquery/issues/1382
3079
+ if (platform === "darwin") {
3080
+ args.push("--allow_unsafe");
3081
+ args.push("--disable_logging");
3082
+ args.push("--disable_events");
3083
+ }
3084
+ if (DEBUG_MODE) {
3085
+ console.log("Executing", OSQUERY_BIN, args.join(" "));
3086
+ }
3087
+ const result = safeSpawnSync(OSQUERY_BIN, args);
3088
+ if (result.status !== 0 || result.error) {
3089
+ if (
3090
+ DEBUG_MODE &&
3091
+ result.stderr &&
3092
+ !result.stderr.includes("no such table")
3093
+ ) {
3094
+ console.error(result.stdout, result.stderr);
3095
+ }
3096
+ }
3097
+ if (result) {
3098
+ const stdout = result.stdout;
3099
+ if (stdout) {
3100
+ const cmdOutput = stdout;
3101
+ if (cmdOutput !== "") {
3102
+ try {
3103
+ return JSON.parse(cmdOutput);
3104
+ } catch (_err) {
3105
+ // ignore
3106
+ if (DEBUG_MODE) {
3107
+ console.log("Unable to parse the output from query", query);
3108
+ console.log(
3109
+ "This could be due to the amount of data returned or the query being invalid for the given platform.",
3110
+ );
3111
+ }
3112
+ }
3113
+ }
3114
+ return undefined;
3115
+ }
3116
+ }
3117
+ }
3118
+ return undefined;
3119
+ }
3120
+
3121
+ /**
3122
+ * Method to execute dosai to create slices for dotnet
3123
+ *
3124
+ * @param {string} src Source Path
3125
+ * @param {string} slicesFile Slices file name
3126
+ * @returns boolean
3127
+ */
3128
+ export function getDotnetSlices(src, slicesFile) {
3129
+ if (!DOSAI_BIN) {
3130
+ return false;
3131
+ }
3132
+ const args = ["methods", "--path", src, "--o", slicesFile];
3133
+ if (DEBUG_MODE) {
3134
+ console.log("Executing", DOSAI_BIN, args.join(" "));
3135
+ }
3136
+ const result = safeSpawnSync(DOSAI_BIN, args, {
3137
+ cwd: src,
3138
+ });
3139
+ if (
3140
+ result?.stdout?.includes(
3141
+ "You must install or update .NET to run this application",
3142
+ ) ||
3143
+ result?.stderr?.includes(
3144
+ "You must install or update .NET to run this application",
3145
+ )
3146
+ ) {
3147
+ console.log(
3148
+ "Dotnet SDK is not installed. Please use the cdxgen dotnet container images to generate slices for this project.",
3149
+ );
3150
+ console.log(
3151
+ "Alternatively, download the dosai self-contained binary (-full suffix) from https://github.com/owasp-dep-scan/dosai/releases and set the environment variable DOSAI_CMD with its location.",
3152
+ );
3153
+ }
3154
+ if (result.status !== 0 || result.error) {
3155
+ if (DEBUG_MODE && result.error) {
3156
+ if (result.stderr) {
3157
+ console.error(result.stdout, result.stderr);
3158
+ } else {
3159
+ console.log("Check if dosai plugin was installed successfully.");
3160
+ }
3161
+ }
3162
+ return false;
3163
+ }
3164
+ return true;
3165
+ }
3166
+
3167
+ /**
3168
+ * Method to generate binary SBOM using blint
3169
+ *
3170
+ * @param {string} src Path to binary or its directory
3171
+ * @param {string} binaryBomFile Path to binary
3172
+ * @param {boolean} deepMode Deep mode flag
3173
+ *
3174
+ * @return {boolean} Result of the generation
3175
+ */
3176
+ export function getBinaryBom(src, binaryBomFile, deepMode) {
3177
+ if (!BLINT_BIN) {
3178
+ return false;
3179
+ }
3180
+ const args = ["sbom", "-i", resolve(src), "-o", binaryBomFile];
3181
+ if (deepMode) {
3182
+ args.push("--deep");
3183
+ }
3184
+ if (DEBUG_MODE) {
3185
+ console.log("Executing", BLINT_BIN, args.join(" "));
3186
+ }
3187
+ const lstatResult = lstatSync(src, { throwIfNoEntry: false });
3188
+ if (!lstatResult) {
3189
+ console.log(`Source path ${src} does not exist`);
3190
+ return false;
3191
+ }
3192
+ const cwd = lstatResult.isDirectory() ? src : dirname(src);
3193
+ const result = safeSpawnSync(BLINT_BIN, args, {
3194
+ cwd,
3195
+ });
3196
+ if (result.status !== 0 || result.error) {
3197
+ if (result.stderr) {
3198
+ console.error(result.stdout, result.stderr);
3199
+ } else {
3200
+ console.log(
3201
+ "Install blint using 'pip install blint' or use the cdxgen container image.",
3202
+ );
3203
+ }
3204
+ return false;
3205
+ }
3206
+ return true;
3207
+ }
3208
+
3209
+ async function fileComponents(basePath, fileList, fileType) {
3210
+ const hashResults = await mapWithConcurrency(fileList, (f) =>
3211
+ multiChecksumFile(["md5", "sha1"], join(basePath, f))
3212
+ .then((hashValues) => [
3213
+ { alg: "MD5", content: hashValues["md5"] },
3214
+ { alg: "SHA-1", content: hashValues["sha1"] },
3215
+ ])
3216
+ .catch(() => undefined),
3217
+ );
3218
+ const components = [];
3219
+ for (let i = 0; i < fileList.length; i++) {
3220
+ let f = fileList[i];
3221
+ const hashes = hashResults[i];
3222
+ if (!f.startsWith("/")) {
3223
+ f = `/${f}`;
3224
+ }
3225
+ const name = basename(f);
3226
+ let linkedName;
3227
+ try {
3228
+ const resolvedPath = realpathSync(join(basePath, f.replace(/^\/+/, "")));
3229
+ const linkStats = lstatSync(join(basePath, f.replace(/^\/+/, "")));
3230
+ if (linkStats?.isSymbolicLink()) {
3231
+ linkedName = basename(resolvedPath);
3232
+ recordSymlinkResolution(
3233
+ join(basePath, f.replace(/^\/+/, "")),
3234
+ resolvedPath,
3235
+ {
3236
+ basePath,
3237
+ metadata: {
3238
+ resolutionKind: "container-binary",
3239
+ },
3240
+ },
3241
+ );
3242
+ }
3243
+ } catch (_e) {
3244
+ // ignore
3245
+ }
3246
+ const purl = genericPurl(name);
3247
+ let isExecutable;
3248
+ let isSetuid;
3249
+ let isSetgid;
3250
+ let isSticky;
3251
+ try {
3252
+ const stats = statSync(join(basePath, f.replace(/^\/+/, "")));
3253
+ const mode = stats.mode;
3254
+ isExecutable = !!(mode & 0o111);
3255
+ isSetuid = !!(mode & 0o4000);
3256
+ isSetgid = !!(mode & 0o2000);
3257
+ isSticky = !!(mode & 0o1000);
3258
+ } catch (_e) {
3259
+ // ignore
3260
+ }
3261
+ const properties = [{ name: "internal:SrcFile", value: f }];
3262
+ if (fileType === "executable" && isExecutable !== undefined) {
3263
+ properties.push({
3264
+ name: `internal:is_${fileType}`,
3265
+ value: isExecutable.toString(),
3266
+ });
3267
+ } else {
3268
+ properties.push({ name: `internal:is_${fileType}`, value: "true" });
3269
+ }
3270
+ if (isSetuid) {
3271
+ properties.push({ name: "internal:has_setuid", value: "true" });
3272
+ }
3273
+ if (isSetgid) {
3274
+ properties.push({ name: "internal:has_setgid", value: "true" });
3275
+ }
3276
+ if (isSticky) {
3277
+ properties.push({ name: "internal:has_sticky", value: "true" });
3278
+ }
3279
+ properties.push(...createContainerRiskProperties(name, linkedName));
3280
+ properties.push(...createGtfoBinsProperties(name, linkedName));
3281
+ components.push({
3282
+ name,
3283
+ type: "file",
3284
+ purl,
3285
+ "bom-ref": purl,
3286
+ hashes,
3287
+ properties,
3288
+ evidence: {
3289
+ identity: [
3290
+ {
3291
+ field: "purl",
3292
+ confidence: 0,
3293
+ methods: [
3294
+ {
3295
+ technique: "filename",
3296
+ confidence: 0,
3297
+ value: f,
3298
+ },
3299
+ ],
3300
+ concludedValue: f,
3301
+ },
3302
+ ],
3303
+ },
3304
+ });
3305
+ }
3306
+ return components;
3307
+ }