@cdxgen/cdxgen 0.0.1 → 13.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (722) hide show
  1. package/README.md +921 -87
  2. package/bin/audit.js +239 -0
  3. package/bin/cdxgen.js +2335 -0
  4. package/bin/convert.js +220 -0
  5. package/bin/evinse.js +353 -0
  6. package/bin/hbom.js +504 -0
  7. package/bin/repl.js +2224 -0
  8. package/bin/sign.js +195 -0
  9. package/bin/tracebom.js +303 -0
  10. package/bin/validate.js +268 -0
  11. package/bin/verify.js +189 -0
  12. package/data/README.md +307 -0
  13. package/data/bom-1.4.schema.json +1605 -0
  14. package/data/bom-1.5.schema.json +3660 -0
  15. package/data/bom-1.6.schema.json +5511 -0
  16. package/data/bom-1.7.schema.json +6700 -0
  17. package/data/cbomosdb-queries.json +68 -0
  18. package/data/component-tags.json +339 -0
  19. package/data/container-knowledge-index.json +125 -0
  20. package/data/cosdb-queries.json +17 -0
  21. package/data/crypto-oid.json +1998 -0
  22. package/data/cryptography-defs.json +3793 -0
  23. package/data/cryptography-defs.schema.json +576 -0
  24. package/data/cyclonedx-2.0-bundled.schema.json +7182 -0
  25. package/data/frameworks-list.json +219 -0
  26. package/data/glibc-stdlib.json +117 -0
  27. package/data/gtfobins-index.json +6296 -0
  28. package/data/helpers/init.gradle +71 -0
  29. package/data/jsf-0.82.schema.json +211 -0
  30. package/data/known-licenses.json +82 -0
  31. package/data/lic-mapping.json +338 -0
  32. package/data/license-aliases.json +6624 -0
  33. package/data/license-db.json +39059 -0
  34. package/data/license-deprecations.json +302 -0
  35. package/data/lolbas-index.json +150 -0
  36. package/data/predictive-audit-allowlist.json +11 -0
  37. package/data/pypi-pkg-aliases.json +1199 -0
  38. package/data/python-stdlib.json +308 -0
  39. package/data/queries-darwin.json +193 -0
  40. package/data/queries-win.json +257 -0
  41. package/data/queries.json +268 -0
  42. package/data/ruby-known-modules.json +281 -0
  43. package/data/rules/ai-agent-governance.yaml +323 -0
  44. package/data/rules/ai-bom.yaml +268 -0
  45. package/data/rules/ai-oversight.yaml +186 -0
  46. package/data/rules/ai-provenance.yaml +92 -0
  47. package/data/rules/asar-archives.yaml +150 -0
  48. package/data/rules/cbom-security.yaml +165 -0
  49. package/data/rules/chrome-extensions.yaml +248 -0
  50. package/data/rules/ci-permissions.yaml +802 -0
  51. package/data/rules/container-risk.yaml +353 -0
  52. package/data/rules/dependency-sources.yaml +236 -0
  53. package/data/rules/golem-go.yaml +229 -0
  54. package/data/rules/hbom-compliance.yaml +325 -0
  55. package/data/rules/hbom-performance.yaml +307 -0
  56. package/data/rules/hbom-security.yaml +248 -0
  57. package/data/rules/host-topology.yaml +165 -0
  58. package/data/rules/mcp-servers.yaml +388 -0
  59. package/data/rules/obom-runtime.yaml +1776 -0
  60. package/data/rules/package-integrity.yaml +364 -0
  61. package/data/rules/rootfs-hardening.yaml +179 -0
  62. package/data/rules/vscode-extensions.yaml +237 -0
  63. package/data/spdx-export.schema.json +6794 -0
  64. package/data/spdx-license-list.json +7449 -0
  65. package/data/spdx-licenses.json +759 -0
  66. package/data/spdx.schema.json +819 -0
  67. package/data/templates/README.md +3 -0
  68. package/data/templates/asvs-4.0.3.cdx.json +2943 -0
  69. package/data/templates/asvs-5.0.cdx.json +3046 -0
  70. package/data/templates/bsimm-v13.cdx.json +1008 -0
  71. package/data/templates/masvs-2.0.0.cdx.json +225 -0
  72. package/data/templates/nist-ssdf-1.1.cdx.json +828 -0
  73. package/data/templates/pcissc-secure-slc-1.1.cdx.json +1027 -0
  74. package/data/templates/scvs-1.0.0.cdx.json +813 -0
  75. package/data/templates/ssaf-DRAFT-2023-11.cdx.json +127 -0
  76. package/data/vendor-alias.json +41967 -0
  77. package/data/wrapdb-releases.json +4227 -0
  78. package/index.cjs +15 -0
  79. package/lib/audit/index.js +2800 -0
  80. package/lib/audit/progress.js +114 -0
  81. package/lib/audit/reporters.js +937 -0
  82. package/lib/audit/scoring.js +560 -0
  83. package/lib/audit/targets.js +798 -0
  84. package/lib/cli/bomAssembly.js +1882 -0
  85. package/lib/cli/cliOptions.js +543 -0
  86. package/lib/cli/index.js +2759 -0
  87. package/lib/cli/jsBom.js +2087 -0
  88. package/lib/cli/jvmBom.js +1858 -0
  89. package/lib/cli/managedBom.js +2083 -0
  90. package/lib/cli/nativeBom.js +2125 -0
  91. package/lib/core/activity.js +1087 -0
  92. package/lib/core/env.js +719 -0
  93. package/lib/core/fs.js +1058 -0
  94. package/lib/core/httpClient.js +730 -0
  95. package/lib/core/logger.js +280 -0
  96. package/lib/core/parallel.js +323 -0
  97. package/lib/core/paths.js +70 -0
  98. package/lib/core/projectConfig.js +122 -0
  99. package/lib/core/propertySanitizer.js +174 -0
  100. package/lib/core/spdx.js +5 -0
  101. package/lib/core/state.js +59 -0
  102. package/lib/core/text.js +207 -0
  103. package/lib/core/ui.js +920 -0
  104. package/lib/core/urls.js +84 -0
  105. package/lib/ecosystems/asarutils.js +1636 -0
  106. package/lib/ecosystems/bunutils.js +402 -0
  107. package/lib/ecosystems/caxa.js +77 -0
  108. package/lib/ecosystems/chromextutils.js +1174 -0
  109. package/lib/ecosystems/cmakeResolver.js +587 -0
  110. package/lib/ecosystems/cppEvidence.js +305 -0
  111. package/lib/ecosystems/denoutils.js +1094 -0
  112. package/lib/ecosystems/dotnetutils.js +132 -0
  113. package/lib/ecosystems/ecosystems.js +2772 -0
  114. package/lib/ecosystems/golem.js +1807 -0
  115. package/lib/ecosystems/gradleutils.js +1048 -0
  116. package/lib/ecosystems/jsEvidence.js +317 -0
  117. package/lib/ecosystems/npmutils.js +586 -0
  118. package/lib/ecosystems/parsers-bazel.js +875 -0
  119. package/lib/ecosystems/parsers-dotnet.js +1430 -0
  120. package/lib/ecosystems/parsers-gleam.js +302 -0
  121. package/lib/ecosystems/parsers-go.js +818 -0
  122. package/lib/ecosystems/parsers-js.js +4222 -0
  123. package/lib/ecosystems/parsers-jvm.js +948 -0
  124. package/lib/ecosystems/parsers-misc.js +3191 -0
  125. package/lib/ecosystems/parsers-python.js +2070 -0
  126. package/lib/ecosystems/parsers-rust.js +1294 -0
  127. package/lib/ecosystems/parsers-zig.js +861 -0
  128. package/lib/ecosystems/pep770.js +341 -0
  129. package/lib/ecosystems/pylockutils.js +427 -0
  130. package/lib/ecosystems/pythonutils.js +1281 -0
  131. package/lib/ecosystems/registryProvenance.js +1003 -0
  132. package/lib/ecosystems/remote/huggingface.js +1343 -0
  133. package/lib/ecosystems/rubyutils.js +1629 -0
  134. package/lib/ecosystems/rusi.js +820 -0
  135. package/lib/ecosystems/sbtutils.js +972 -0
  136. package/lib/ecosystems/tea.js +607 -0
  137. package/lib/ecosystems/utils.js +583 -0
  138. package/lib/ecosystems/zigResolver.js +410 -0
  139. package/lib/evinser/db.js +145 -0
  140. package/lib/evinser/evinser.js +2362 -0
  141. package/lib/evinser/scalasem.js +65 -0
  142. package/lib/evinser/swiftsem.js +767 -0
  143. package/lib/helpers/annotationFormatter.js +84 -0
  144. package/lib/helpers/bomSigner.js +312 -0
  145. package/lib/helpers/exportUtils.js +160 -0
  146. package/lib/helpers/remote/dependency-track.js +117 -0
  147. package/lib/helpers/versutils.js +202 -0
  148. package/lib/helpers/vsixutils.js +1069 -0
  149. package/lib/inventory/agentFormulationParser.js +322 -0
  150. package/lib/inventory/aiCollector.js +2140 -0
  151. package/lib/inventory/aiInventory.js +414 -0
  152. package/lib/inventory/aiModelVariants.js +121 -0
  153. package/lib/inventory/aiOversightCollector.js +661 -0
  154. package/lib/inventory/aiProvenanceCollector.js +822 -0
  155. package/lib/inventory/analyzer.js +6089 -0
  156. package/lib/inventory/analyzerScope.js +807 -0
  157. package/lib/inventory/astWorker.js +20 -0
  158. package/lib/inventory/atomUtils.js +1092 -0
  159. package/lib/inventory/auditCategories.js +192 -0
  160. package/lib/inventory/bomUtils.js +369 -0
  161. package/lib/inventory/cacheCommands.js +182 -0
  162. package/lib/inventory/cacheDir.js +104 -0
  163. package/lib/inventory/cbomutils.js +590 -0
  164. package/lib/inventory/cdxrs.js +434 -0
  165. package/lib/inventory/ciParsers/azurePipelines.js +295 -0
  166. package/lib/inventory/ciParsers/circleCi.js +286 -0
  167. package/lib/inventory/ciParsers/common.js +24 -0
  168. package/lib/inventory/ciParsers/githubActions.js +2434 -0
  169. package/lib/inventory/ciParsers/gitlabCi.js +213 -0
  170. package/lib/inventory/ciParsers/jenkins.js +180 -0
  171. package/lib/inventory/citations.js +234 -0
  172. package/lib/inventory/communityAiConfigParser.js +756 -0
  173. package/lib/inventory/containerRisk.js +204 -0
  174. package/lib/inventory/cryptoAlgorithmFamily.js +258 -0
  175. package/lib/inventory/deps.js +807 -0
  176. package/lib/inventory/depsUtils.js +588 -0
  177. package/lib/inventory/display.js +2128 -0
  178. package/lib/inventory/dosai.js +601 -0
  179. package/lib/inventory/dosaiParsers.js +154 -0
  180. package/lib/inventory/dynamic.js +145 -0
  181. package/lib/inventory/envcontext.js +1793 -0
  182. package/lib/inventory/evidenceUtils.js +774 -0
  183. package/lib/inventory/fetchBatch.js +779 -0
  184. package/lib/inventory/fetchRate.js +255 -0
  185. package/lib/inventory/forgeEnricher.js +300 -0
  186. package/lib/inventory/formulationParsers.js +422 -0
  187. package/lib/inventory/gtfobins.js +339 -0
  188. package/lib/inventory/hbom.js +719 -0
  189. package/lib/inventory/hbomAnalysis.js +310 -0
  190. package/lib/inventory/hbomLoader.js +35 -0
  191. package/lib/inventory/hostTopology.js +835 -0
  192. package/lib/inventory/inventoryStats.js +100 -0
  193. package/lib/inventory/jsonLike.js +121 -0
  194. package/lib/inventory/licenseEnhancer.js +918 -0
  195. package/lib/inventory/lolbas.js +285 -0
  196. package/lib/inventory/mcp.js +253 -0
  197. package/lib/inventory/mcpConfigParser.js +683 -0
  198. package/lib/inventory/mcpDiscovery.js +112 -0
  199. package/lib/inventory/mcpPinning.js +209 -0
  200. package/lib/inventory/osPackageResolver.js +493 -0
  201. package/lib/inventory/osinfo.js +383 -0
  202. package/lib/inventory/osqueryTransform.js +274 -0
  203. package/lib/inventory/plugins.js +466 -0
  204. package/lib/inventory/protobom.js +231 -0
  205. package/lib/inventory/protobomLoader.js +43 -0
  206. package/lib/inventory/provenanceUtils.js +224 -0
  207. package/lib/inventory/purl.js +749 -0
  208. package/lib/inventory/rustFormulationParser.js +339 -0
  209. package/lib/inventory/sbomDocument.js +340 -0
  210. package/lib/inventory/source.js +1488 -0
  211. package/lib/inventory/spdx.js +361 -0
  212. package/lib/inventory/spdxExpression.js +512 -0
  213. package/lib/inventory/spdxUtils.js +107 -0
  214. package/lib/inventory/table.js +267 -0
  215. package/lib/inventory/traceRunner.js +439 -0
  216. package/lib/inventory/unicodeScan.js +147 -0
  217. package/lib/managers/binary.js +3459 -0
  218. package/lib/managers/containerutils.js +125 -0
  219. package/lib/managers/docker.js +2145 -0
  220. package/lib/managers/dockerConnection.js +162 -0
  221. package/lib/managers/oci.js +704 -0
  222. package/lib/managers/piptree.js +267 -0
  223. package/lib/parsers/cmakeCache.js +199 -0
  224. package/lib/parsers/gguf.js +492 -0
  225. package/lib/parsers/gitAiNotes.js +233 -0
  226. package/lib/parsers/gitmodules.js +239 -0
  227. package/lib/parsers/htmlExtract.js +610 -0
  228. package/lib/parsers/huggingfaceManifest.js +551 -0
  229. package/lib/parsers/huggingfaceUtils.js +506 -0
  230. package/lib/parsers/iri.js +618 -0
  231. package/lib/parsers/npmrc.js +99 -0
  232. package/lib/parsers/ollama.js +101 -0
  233. package/lib/parsers/xml.js +841 -0
  234. package/lib/server/openapi.yaml +433 -0
  235. package/lib/server/server.js +616 -0
  236. package/lib/stages/postgen/annotator.js +977 -0
  237. package/lib/stages/postgen/auditBom.js +468 -0
  238. package/lib/stages/postgen/postgen.js +1239 -0
  239. package/lib/stages/postgen/ruleEngine.js +731 -0
  240. package/lib/stages/postgen/sortBom.js +195 -0
  241. package/lib/stages/postgen/spdxConverter.js +796 -0
  242. package/lib/stages/postgen/specVersionCompat.js +703 -0
  243. package/lib/stages/pregen/envAudit.js +388 -0
  244. package/lib/stages/pregen/pregen.js +637 -0
  245. package/lib/third-party/README.md +9 -0
  246. package/lib/third-party/arborist/CHANGELOG.md +1109 -0
  247. package/lib/third-party/arborist/LICENSE.md +20 -0
  248. package/lib/third-party/arborist/README.md +315 -0
  249. package/lib/third-party/arborist/lib/arborist/index.js +258 -0
  250. package/lib/third-party/arborist/lib/arborist/load-actual.js +605 -0
  251. package/lib/third-party/arborist/lib/arborist/load-virtual.js +300 -0
  252. package/lib/third-party/arborist/lib/bin-links.js +120 -0
  253. package/lib/third-party/arborist/lib/calc-dep-flags.js +130 -0
  254. package/lib/third-party/arborist/lib/case-insensitive-map.js +51 -0
  255. package/lib/third-party/arborist/lib/consistent-resolve.js +42 -0
  256. package/lib/third-party/arborist/lib/debug.js +50 -0
  257. package/lib/third-party/arborist/lib/dep-valid.js +190 -0
  258. package/lib/third-party/arborist/lib/edge.js +417 -0
  259. package/lib/third-party/arborist/lib/from-path.js +36 -0
  260. package/lib/third-party/arborist/lib/gather-dep-set.js +43 -0
  261. package/lib/third-party/arborist/lib/index.js +22 -0
  262. package/lib/third-party/arborist/lib/inventory.js +138 -0
  263. package/lib/third-party/arborist/lib/link.js +169 -0
  264. package/lib/third-party/arborist/lib/node.js +1673 -0
  265. package/lib/third-party/arborist/lib/npm-extension.js +45 -0
  266. package/lib/third-party/arborist/lib/override-resolves.js +11 -0
  267. package/lib/third-party/arborist/lib/override-set.js +319 -0
  268. package/lib/third-party/arborist/lib/package-extensions.js +289 -0
  269. package/lib/third-party/arborist/lib/printable.js +210 -0
  270. package/lib/third-party/arborist/lib/proc-log.js +30 -0
  271. package/lib/third-party/arborist/lib/realpath.js +100 -0
  272. package/lib/third-party/arborist/lib/relpath.js +4 -0
  273. package/lib/third-party/arborist/lib/shrinkwrap.js +1291 -0
  274. package/lib/third-party/arborist/lib/spec-from-lock.js +34 -0
  275. package/lib/third-party/arborist/lib/tree-check.js +165 -0
  276. package/lib/third-party/arborist/lib/validate-npm-package-name.js +107 -0
  277. package/lib/third-party/arborist/lib/version-from-tgz.js +51 -0
  278. package/lib/third-party/arborist/lib/yarn-lock.js +397 -0
  279. package/lib/validator/bomValidator.js +985 -0
  280. package/lib/validator/complianceEngine.js +241 -0
  281. package/lib/validator/complianceRules.js +1680 -0
  282. package/lib/validator/generated/runtime.mjs +17 -0
  283. package/lib/validator/generated/validate-1.6.mjs +21258 -0
  284. package/lib/validator/generated/validate-1.7.mjs +24803 -0
  285. package/lib/validator/generated/validate-2.0.mjs +22931 -0
  286. package/lib/validator/index.js +246 -0
  287. package/lib/validator/reporters/annotations.js +142 -0
  288. package/lib/validator/reporters/console.js +160 -0
  289. package/lib/validator/reporters/index.js +41 -0
  290. package/lib/validator/reporters/json.js +37 -0
  291. package/lib/validator/reporters/sarif.js +184 -0
  292. package/package.json +209 -26
  293. package/types/bin/audit.d.ts +3 -0
  294. package/types/bin/audit.d.ts.map +1 -0
  295. package/types/bin/cdxgen.d.ts +3 -0
  296. package/types/bin/cdxgen.d.ts.map +1 -0
  297. package/types/bin/convert.d.ts +3 -0
  298. package/types/bin/convert.d.ts.map +1 -0
  299. package/types/bin/evinse.d.ts +3 -0
  300. package/types/bin/evinse.d.ts.map +1 -0
  301. package/types/bin/hbom.d.ts +3 -0
  302. package/types/bin/hbom.d.ts.map +1 -0
  303. package/types/bin/repl.d.ts +3 -0
  304. package/types/bin/repl.d.ts.map +1 -0
  305. package/types/bin/sign.d.ts +3 -0
  306. package/types/bin/sign.d.ts.map +1 -0
  307. package/types/bin/tracebom.d.ts +3 -0
  308. package/types/bin/tracebom.d.ts.map +1 -0
  309. package/types/bin/validate.d.ts +14 -0
  310. package/types/bin/validate.d.ts.map +1 -0
  311. package/types/bin/verify.d.ts +3 -0
  312. package/types/bin/verify.d.ts.map +1 -0
  313. package/types/lib/audit/index.d.ts +153 -0
  314. package/types/lib/audit/index.d.ts.map +1 -0
  315. package/types/lib/audit/progress.d.ts +31 -0
  316. package/types/lib/audit/progress.d.ts.map +1 -0
  317. package/types/lib/audit/reporters.d.ts +62 -0
  318. package/types/lib/audit/reporters.d.ts.map +1 -0
  319. package/types/lib/audit/scoring.d.ts +36 -0
  320. package/types/lib/audit/scoring.d.ts.map +1 -0
  321. package/types/lib/audit/targets.d.ts +77 -0
  322. package/types/lib/audit/targets.d.ts.map +1 -0
  323. package/types/lib/cli/bomAssembly.d.ts +329 -0
  324. package/types/lib/cli/bomAssembly.d.ts.map +1 -0
  325. package/types/lib/cli/cliOptions.d.ts +156 -0
  326. package/types/lib/cli/cliOptions.d.ts.map +1 -0
  327. package/types/lib/cli/index.d.ts +107 -0
  328. package/types/lib/cli/index.d.ts.map +1 -0
  329. package/types/lib/cli/jsBom.d.ts +94 -0
  330. package/types/lib/cli/jsBom.d.ts.map +1 -0
  331. package/types/lib/cli/jvmBom.d.ts +54 -0
  332. package/types/lib/cli/jvmBom.d.ts.map +1 -0
  333. package/types/lib/cli/managedBom.d.ts +105 -0
  334. package/types/lib/cli/managedBom.d.ts.map +1 -0
  335. package/types/lib/cli/nativeBom.d.ts +143 -0
  336. package/types/lib/cli/nativeBom.d.ts.map +1 -0
  337. package/types/lib/core/activity.d.ts +178 -0
  338. package/types/lib/core/activity.d.ts.map +1 -0
  339. package/types/lib/core/env.d.ts +192 -0
  340. package/types/lib/core/env.d.ts.map +1 -0
  341. package/types/lib/core/fs.d.ts +158 -0
  342. package/types/lib/core/fs.d.ts.map +1 -0
  343. package/types/lib/core/httpClient.d.ts +149 -0
  344. package/types/lib/core/httpClient.d.ts.map +1 -0
  345. package/types/lib/core/logger.d.ts +50 -0
  346. package/types/lib/core/logger.d.ts.map +1 -0
  347. package/types/lib/core/parallel.d.ts +95 -0
  348. package/types/lib/core/parallel.d.ts.map +1 -0
  349. package/types/lib/core/paths.d.ts +36 -0
  350. package/types/lib/core/paths.d.ts.map +1 -0
  351. package/types/lib/core/projectConfig.d.ts +23 -0
  352. package/types/lib/core/projectConfig.d.ts.map +1 -0
  353. package/types/lib/core/propertySanitizer.d.ts +23 -0
  354. package/types/lib/core/propertySanitizer.d.ts.map +1 -0
  355. package/types/lib/core/spdx.d.ts +5 -0
  356. package/types/lib/core/spdx.d.ts.map +1 -0
  357. package/types/lib/core/state.d.ts +20 -0
  358. package/types/lib/core/state.d.ts.map +1 -0
  359. package/types/lib/core/text.d.ts +78 -0
  360. package/types/lib/core/text.d.ts.map +1 -0
  361. package/types/lib/core/ui.d.ts +87 -0
  362. package/types/lib/core/ui.d.ts.map +1 -0
  363. package/types/lib/core/urls.d.ts +43 -0
  364. package/types/lib/core/urls.d.ts.map +1 -0
  365. package/types/lib/ecosystems/asarutils.d.ts +71 -0
  366. package/types/lib/ecosystems/asarutils.d.ts.map +1 -0
  367. package/types/lib/ecosystems/bunutils.d.ts +38 -0
  368. package/types/lib/ecosystems/bunutils.d.ts.map +1 -0
  369. package/types/lib/ecosystems/caxa.d.ts +8 -0
  370. package/types/lib/ecosystems/caxa.d.ts.map +1 -0
  371. package/types/lib/ecosystems/chromextutils.d.ts +97 -0
  372. package/types/lib/ecosystems/chromextutils.d.ts.map +1 -0
  373. package/types/lib/ecosystems/cmakeResolver.d.ts +176 -0
  374. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -0
  375. package/types/lib/ecosystems/cppEvidence.d.ts +35 -0
  376. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -0
  377. package/types/lib/ecosystems/denoutils.d.ts +76 -0
  378. package/types/lib/ecosystems/denoutils.d.ts.map +1 -0
  379. package/types/lib/ecosystems/dotnetutils.d.ts +45 -0
  380. package/types/lib/ecosystems/dotnetutils.d.ts.map +1 -0
  381. package/types/lib/ecosystems/ecosystems.d.ts +279 -0
  382. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -0
  383. package/types/lib/ecosystems/golem.d.ts +60 -0
  384. package/types/lib/ecosystems/golem.d.ts.map +1 -0
  385. package/types/lib/ecosystems/gradleutils.d.ts +139 -0
  386. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -0
  387. package/types/lib/ecosystems/jsEvidence.d.ts +17 -0
  388. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -0
  389. package/types/lib/ecosystems/npmutils.d.ts +183 -0
  390. package/types/lib/ecosystems/npmutils.d.ts.map +1 -0
  391. package/types/lib/ecosystems/parsers-bazel.d.ts +36 -0
  392. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -0
  393. package/types/lib/ecosystems/parsers-dotnet.d.ts +116 -0
  394. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -0
  395. package/types/lib/ecosystems/parsers-gleam.d.ts +33 -0
  396. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -0
  397. package/types/lib/ecosystems/parsers-go.d.ts +92 -0
  398. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -0
  399. package/types/lib/ecosystems/parsers-js.d.ts +242 -0
  400. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -0
  401. package/types/lib/ecosystems/parsers-jvm.d.ts +100 -0
  402. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -0
  403. package/types/lib/ecosystems/parsers-misc.d.ts +450 -0
  404. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -0
  405. package/types/lib/ecosystems/parsers-python.d.ts +145 -0
  406. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -0
  407. package/types/lib/ecosystems/parsers-rust.d.ts +56 -0
  408. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -0
  409. package/types/lib/ecosystems/parsers-zig.d.ts +83 -0
  410. package/types/lib/ecosystems/parsers-zig.d.ts.map +1 -0
  411. package/types/lib/ecosystems/pep770.d.ts +38 -0
  412. package/types/lib/ecosystems/pep770.d.ts.map +1 -0
  413. package/types/lib/ecosystems/pylockutils.d.ts +68 -0
  414. package/types/lib/ecosystems/pylockutils.d.ts.map +1 -0
  415. package/types/lib/ecosystems/pythonutils.d.ts +70 -0
  416. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -0
  417. package/types/lib/ecosystems/registryProvenance.d.ts +26 -0
  418. package/types/lib/ecosystems/registryProvenance.d.ts.map +1 -0
  419. package/types/lib/ecosystems/remote/huggingface.d.ts +31 -0
  420. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -0
  421. package/types/lib/ecosystems/rubyutils.d.ts +182 -0
  422. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -0
  423. package/types/lib/ecosystems/rusi.d.ts +40 -0
  424. package/types/lib/ecosystems/rusi.d.ts.map +1 -0
  425. package/types/lib/ecosystems/sbtutils.d.ts +179 -0
  426. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -0
  427. package/types/lib/ecosystems/tea.d.ts +180 -0
  428. package/types/lib/ecosystems/tea.d.ts.map +1 -0
  429. package/types/lib/ecosystems/utils.d.ts +41 -0
  430. package/types/lib/ecosystems/utils.d.ts.map +1 -0
  431. package/types/lib/ecosystems/zigResolver.d.ts +70 -0
  432. package/types/lib/ecosystems/zigResolver.d.ts.map +1 -0
  433. package/types/lib/evinser/db.d.ts +17 -0
  434. package/types/lib/evinser/db.d.ts.map +1 -0
  435. package/types/lib/evinser/evinser.d.ts +354 -0
  436. package/types/lib/evinser/evinser.d.ts.map +1 -0
  437. package/types/lib/evinser/scalasem.d.ts +15 -0
  438. package/types/lib/evinser/scalasem.d.ts.map +1 -0
  439. package/types/lib/evinser/swiftsem.d.ts +103 -0
  440. package/types/lib/evinser/swiftsem.d.ts.map +1 -0
  441. package/types/lib/helpers/annotationFormatter.d.ts +23 -0
  442. package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
  443. package/types/lib/helpers/bomSigner.d.ts +28 -0
  444. package/types/lib/helpers/bomSigner.d.ts.map +1 -0
  445. package/types/lib/helpers/exportUtils.d.ts +52 -0
  446. package/types/lib/helpers/exportUtils.d.ts.map +1 -0
  447. package/types/lib/helpers/remote/dependency-track.d.ts +23 -0
  448. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
  449. package/types/lib/helpers/versutils.d.ts +8 -0
  450. package/types/lib/helpers/versutils.d.ts.map +1 -0
  451. package/types/lib/helpers/vsixutils.d.ts +130 -0
  452. package/types/lib/helpers/vsixutils.d.ts.map +1 -0
  453. package/types/lib/inventory/agentFormulationParser.d.ts +21 -0
  454. package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -0
  455. package/types/lib/inventory/aiCollector.d.ts +61 -0
  456. package/types/lib/inventory/aiCollector.d.ts.map +1 -0
  457. package/types/lib/inventory/aiInventory.d.ts +103 -0
  458. package/types/lib/inventory/aiInventory.d.ts.map +1 -0
  459. package/types/lib/inventory/aiModelVariants.d.ts +31 -0
  460. package/types/lib/inventory/aiModelVariants.d.ts.map +1 -0
  461. package/types/lib/inventory/aiOversightCollector.d.ts +28 -0
  462. package/types/lib/inventory/aiOversightCollector.d.ts.map +1 -0
  463. package/types/lib/inventory/aiProvenanceCollector.d.ts +13 -0
  464. package/types/lib/inventory/aiProvenanceCollector.d.ts.map +1 -0
  465. package/types/lib/inventory/analyzer.d.ts +157 -0
  466. package/types/lib/inventory/analyzer.d.ts.map +1 -0
  467. package/types/lib/inventory/analyzerScope.d.ts +106 -0
  468. package/types/lib/inventory/analyzerScope.d.ts.map +1 -0
  469. package/types/lib/inventory/astWorker.d.ts +9 -0
  470. package/types/lib/inventory/astWorker.d.ts.map +1 -0
  471. package/types/lib/inventory/atomUtils.d.ts +159 -0
  472. package/types/lib/inventory/atomUtils.d.ts.map +1 -0
  473. package/types/lib/inventory/auditCategories.d.ts +80 -0
  474. package/types/lib/inventory/auditCategories.d.ts.map +1 -0
  475. package/types/lib/inventory/bomUtils.d.ts +145 -0
  476. package/types/lib/inventory/bomUtils.d.ts.map +1 -0
  477. package/types/lib/inventory/cacheCommands.d.ts +28 -0
  478. package/types/lib/inventory/cacheCommands.d.ts.map +1 -0
  479. package/types/lib/inventory/cacheDir.d.ts +55 -0
  480. package/types/lib/inventory/cacheDir.d.ts.map +1 -0
  481. package/types/lib/inventory/cbomutils.d.ts +37 -0
  482. package/types/lib/inventory/cbomutils.d.ts.map +1 -0
  483. package/types/lib/inventory/cdxrs.d.ts +90 -0
  484. package/types/lib/inventory/cdxrs.d.ts.map +1 -0
  485. package/types/lib/inventory/ciParsers/azurePipelines.d.ts +27 -0
  486. package/types/lib/inventory/ciParsers/azurePipelines.d.ts.map +1 -0
  487. package/types/lib/inventory/ciParsers/circleCi.d.ts +27 -0
  488. package/types/lib/inventory/ciParsers/circleCi.d.ts.map +1 -0
  489. package/types/lib/inventory/ciParsers/common.d.ts +11 -0
  490. package/types/lib/inventory/ciParsers/common.d.ts.map +1 -0
  491. package/types/lib/inventory/ciParsers/githubActions.d.ts +38 -0
  492. package/types/lib/inventory/ciParsers/githubActions.d.ts.map +1 -0
  493. package/types/lib/inventory/ciParsers/gitlabCi.d.ts +26 -0
  494. package/types/lib/inventory/ciParsers/gitlabCi.d.ts.map +1 -0
  495. package/types/lib/inventory/ciParsers/jenkins.d.ts +26 -0
  496. package/types/lib/inventory/ciParsers/jenkins.d.ts.map +1 -0
  497. package/types/lib/inventory/citations.d.ts +90 -0
  498. package/types/lib/inventory/citations.d.ts.map +1 -0
  499. package/types/lib/inventory/communityAiConfigParser.d.ts +14 -0
  500. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -0
  501. package/types/lib/inventory/containerRisk.d.ts +20 -0
  502. package/types/lib/inventory/containerRisk.d.ts.map +1 -0
  503. package/types/lib/inventory/cryptoAlgorithmFamily.d.ts +37 -0
  504. package/types/lib/inventory/cryptoAlgorithmFamily.d.ts.map +1 -0
  505. package/types/lib/inventory/deps.d.ts +140 -0
  506. package/types/lib/inventory/deps.d.ts.map +1 -0
  507. package/types/lib/inventory/depsUtils.d.ts +74 -0
  508. package/types/lib/inventory/depsUtils.d.ts.map +1 -0
  509. package/types/lib/inventory/display.d.ts +227 -0
  510. package/types/lib/inventory/display.d.ts.map +1 -0
  511. package/types/lib/inventory/dosai.d.ts +130 -0
  512. package/types/lib/inventory/dosai.d.ts.map +1 -0
  513. package/types/lib/inventory/dosaiParsers.d.ts +59 -0
  514. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -0
  515. package/types/lib/inventory/dynamic.d.ts +25 -0
  516. package/types/lib/inventory/dynamic.d.ts.map +1 -0
  517. package/types/lib/inventory/envcontext.d.ts +377 -0
  518. package/types/lib/inventory/envcontext.d.ts.map +1 -0
  519. package/types/lib/inventory/evidenceUtils.d.ts +74 -0
  520. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -0
  521. package/types/lib/inventory/fetchBatch.d.ts +127 -0
  522. package/types/lib/inventory/fetchBatch.d.ts.map +1 -0
  523. package/types/lib/inventory/fetchRate.d.ts +118 -0
  524. package/types/lib/inventory/fetchRate.d.ts.map +1 -0
  525. package/types/lib/inventory/forgeEnricher.d.ts +31 -0
  526. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -0
  527. package/types/lib/inventory/formulationParsers.d.ts +50 -0
  528. package/types/lib/inventory/formulationParsers.d.ts.map +1 -0
  529. package/types/lib/inventory/gtfobins.d.ts +27 -0
  530. package/types/lib/inventory/gtfobins.d.ts.map +1 -0
  531. package/types/lib/inventory/hbom.d.ts +58 -0
  532. package/types/lib/inventory/hbom.d.ts.map +1 -0
  533. package/types/lib/inventory/hbomAnalysis.d.ts +56 -0
  534. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -0
  535. package/types/lib/inventory/hbomLoader.d.ts +7 -0
  536. package/types/lib/inventory/hbomLoader.d.ts.map +1 -0
  537. package/types/lib/inventory/hostTopology.d.ts +47 -0
  538. package/types/lib/inventory/hostTopology.d.ts.map +1 -0
  539. package/types/lib/inventory/inventoryStats.d.ts +42 -0
  540. package/types/lib/inventory/inventoryStats.d.ts.map +1 -0
  541. package/types/lib/inventory/jsonLike.d.ts +23 -0
  542. package/types/lib/inventory/jsonLike.d.ts.map +1 -0
  543. package/types/lib/inventory/licenseEnhancer.d.ts +70 -0
  544. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -0
  545. package/types/lib/inventory/lolbas.d.ts +16 -0
  546. package/types/lib/inventory/lolbas.d.ts.map +1 -0
  547. package/types/lib/inventory/mcp.d.ts +29 -0
  548. package/types/lib/inventory/mcp.d.ts.map +1 -0
  549. package/types/lib/inventory/mcpConfigParser.d.ts +20 -0
  550. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -0
  551. package/types/lib/inventory/mcpDiscovery.d.ts +33 -0
  552. package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -0
  553. package/types/lib/inventory/mcpPinning.d.ts +11 -0
  554. package/types/lib/inventory/mcpPinning.d.ts.map +1 -0
  555. package/types/lib/inventory/osPackageResolver.d.ts +49 -0
  556. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -0
  557. package/types/lib/inventory/osinfo.d.ts +201 -0
  558. package/types/lib/inventory/osinfo.d.ts.map +1 -0
  559. package/types/lib/inventory/osqueryTransform.d.ts +84 -0
  560. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -0
  561. package/types/lib/inventory/plugins.d.ts +58 -0
  562. package/types/lib/inventory/plugins.d.ts.map +1 -0
  563. package/types/lib/inventory/protobom.d.ts +71 -0
  564. package/types/lib/inventory/protobom.d.ts.map +1 -0
  565. package/types/lib/inventory/protobomLoader.d.ts +17 -0
  566. package/types/lib/inventory/protobomLoader.d.ts.map +1 -0
  567. package/types/lib/inventory/provenanceUtils.d.ts +98 -0
  568. package/types/lib/inventory/provenanceUtils.d.ts.map +1 -0
  569. package/types/lib/inventory/purl.d.ts +313 -0
  570. package/types/lib/inventory/purl.d.ts.map +1 -0
  571. package/types/lib/inventory/rustFormulationParser.d.ts +17 -0
  572. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -0
  573. package/types/lib/inventory/sbomDocument.d.ts +59 -0
  574. package/types/lib/inventory/sbomDocument.d.ts.map +1 -0
  575. package/types/lib/inventory/source.d.ts +163 -0
  576. package/types/lib/inventory/source.d.ts.map +1 -0
  577. package/types/lib/inventory/spdx.d.ts +66 -0
  578. package/types/lib/inventory/spdx.d.ts.map +1 -0
  579. package/types/lib/inventory/spdxExpression.d.ts +58 -0
  580. package/types/lib/inventory/spdxExpression.d.ts.map +1 -0
  581. package/types/lib/inventory/spdxUtils.d.ts +12 -0
  582. package/types/lib/inventory/spdxUtils.d.ts.map +1 -0
  583. package/types/lib/inventory/table.d.ts +22 -0
  584. package/types/lib/inventory/table.d.ts.map +1 -0
  585. package/types/lib/inventory/traceRunner.d.ts +80 -0
  586. package/types/lib/inventory/traceRunner.d.ts.map +1 -0
  587. package/types/lib/inventory/unicodeScan.d.ts +46 -0
  588. package/types/lib/inventory/unicodeScan.d.ts.map +1 -0
  589. package/types/lib/managers/binary.d.ts +147 -0
  590. package/types/lib/managers/binary.d.ts.map +1 -0
  591. package/types/lib/managers/containerutils.d.ts +30 -0
  592. package/types/lib/managers/containerutils.d.ts.map +1 -0
  593. package/types/lib/managers/docker.d.ts +157 -0
  594. package/types/lib/managers/docker.d.ts.map +1 -0
  595. package/types/lib/managers/dockerConnection.d.ts +42 -0
  596. package/types/lib/managers/dockerConnection.d.ts.map +1 -0
  597. package/types/lib/managers/oci.d.ts +21 -0
  598. package/types/lib/managers/oci.d.ts.map +1 -0
  599. package/types/lib/managers/piptree.d.ts +13 -0
  600. package/types/lib/managers/piptree.d.ts.map +1 -0
  601. package/types/lib/parsers/cmakeCache.d.ts +70 -0
  602. package/types/lib/parsers/cmakeCache.d.ts.map +1 -0
  603. package/types/lib/parsers/gguf.d.ts +39 -0
  604. package/types/lib/parsers/gguf.d.ts.map +1 -0
  605. package/types/lib/parsers/gitAiNotes.d.ts +15 -0
  606. package/types/lib/parsers/gitAiNotes.d.ts.map +1 -0
  607. package/types/lib/parsers/gitmodules.d.ts +88 -0
  608. package/types/lib/parsers/gitmodules.d.ts.map +1 -0
  609. package/types/lib/parsers/htmlExtract.d.ts +34 -0
  610. package/types/lib/parsers/htmlExtract.d.ts.map +1 -0
  611. package/types/lib/parsers/huggingfaceManifest.d.ts +153 -0
  612. package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -0
  613. package/types/lib/parsers/huggingfaceUtils.d.ts +155 -0
  614. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -0
  615. package/types/lib/parsers/iri.d.ts +68 -0
  616. package/types/lib/parsers/iri.d.ts.map +1 -0
  617. package/types/lib/parsers/npmrc.d.ts +33 -0
  618. package/types/lib/parsers/npmrc.d.ts.map +1 -0
  619. package/types/lib/parsers/ollama.d.ts +22 -0
  620. package/types/lib/parsers/ollama.d.ts.map +1 -0
  621. package/types/lib/parsers/xml.d.ts +47 -0
  622. package/types/lib/parsers/xml.d.ts.map +1 -0
  623. package/types/lib/server/server.d.ts +51 -0
  624. package/types/lib/server/server.d.ts.map +1 -0
  625. package/types/lib/stages/postgen/annotator.d.ts +36 -0
  626. package/types/lib/stages/postgen/annotator.d.ts.map +1 -0
  627. package/types/lib/stages/postgen/auditBom.d.ts +92 -0
  628. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -0
  629. package/types/lib/stages/postgen/postgen.d.ts +59 -0
  630. package/types/lib/stages/postgen/postgen.d.ts.map +1 -0
  631. package/types/lib/stages/postgen/ruleEngine.d.ts +18 -0
  632. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -0
  633. package/types/lib/stages/postgen/sortBom.d.ts +34 -0
  634. package/types/lib/stages/postgen/sortBom.d.ts.map +1 -0
  635. package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
  636. package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
  637. package/types/lib/stages/postgen/specVersionCompat.d.ts +71 -0
  638. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -0
  639. package/types/lib/stages/pregen/envAudit.d.ts +14 -0
  640. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -0
  641. package/types/lib/stages/pregen/pregen.d.ts +59 -0
  642. package/types/lib/stages/pregen/pregen.d.ts.map +1 -0
  643. package/types/lib/third-party/arborist/lib/arborist/index.d.ts +47 -0
  644. package/types/lib/third-party/arborist/lib/arborist/index.d.ts.map +1 -0
  645. package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts +4 -0
  646. package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts.map +1 -0
  647. package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts +4 -0
  648. package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts.map +1 -0
  649. package/types/lib/third-party/arborist/lib/bin-links.d.ts +18 -0
  650. package/types/lib/third-party/arborist/lib/bin-links.d.ts.map +1 -0
  651. package/types/lib/third-party/arborist/lib/calc-dep-flags.d.ts +3 -0
  652. package/types/lib/third-party/arborist/lib/calc-dep-flags.d.ts.map +1 -0
  653. package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts +9 -0
  654. package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts.map +1 -0
  655. package/types/lib/third-party/arborist/lib/consistent-resolve.d.ts +3 -0
  656. package/types/lib/third-party/arborist/lib/consistent-resolve.d.ts.map +1 -0
  657. package/types/lib/third-party/arborist/lib/debug.d.ts +3 -0
  658. package/types/lib/third-party/arborist/lib/debug.d.ts.map +1 -0
  659. package/types/lib/third-party/arborist/lib/dep-valid.d.ts +3 -0
  660. package/types/lib/third-party/arborist/lib/dep-valid.d.ts.map +1 -0
  661. package/types/lib/third-party/arborist/lib/edge.d.ts +46 -0
  662. package/types/lib/third-party/arborist/lib/edge.d.ts.map +1 -0
  663. package/types/lib/third-party/arborist/lib/from-path.d.ts +3 -0
  664. package/types/lib/third-party/arborist/lib/from-path.d.ts.map +1 -0
  665. package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts +3 -0
  666. package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts.map +1 -0
  667. package/types/lib/third-party/arborist/lib/index.d.ts +3 -0
  668. package/types/lib/third-party/arborist/lib/index.d.ts.map +1 -0
  669. package/types/lib/third-party/arborist/lib/inventory.d.ts +14 -0
  670. package/types/lib/third-party/arborist/lib/inventory.d.ts.map +1 -0
  671. package/types/lib/third-party/arborist/lib/link.d.ts +18 -0
  672. package/types/lib/third-party/arborist/lib/link.d.ts.map +1 -0
  673. package/types/lib/third-party/arborist/lib/node.d.ts +112 -0
  674. package/types/lib/third-party/arborist/lib/node.d.ts.map +1 -0
  675. package/types/lib/third-party/arborist/lib/npm-extension.d.ts +18 -0
  676. package/types/lib/third-party/arborist/lib/npm-extension.d.ts.map +1 -0
  677. package/types/lib/third-party/arborist/lib/override-resolves.d.ts +3 -0
  678. package/types/lib/third-party/arborist/lib/override-resolves.d.ts.map +1 -0
  679. package/types/lib/third-party/arborist/lib/override-set.d.ts +27 -0
  680. package/types/lib/third-party/arborist/lib/override-set.d.ts.map +1 -0
  681. package/types/lib/third-party/arborist/lib/package-extensions.d.ts +36 -0
  682. package/types/lib/third-party/arborist/lib/package-extensions.d.ts.map +1 -0
  683. package/types/lib/third-party/arborist/lib/printable.d.ts +3 -0
  684. package/types/lib/third-party/arborist/lib/printable.d.ts.map +1 -0
  685. package/types/lib/third-party/arborist/lib/proc-log.d.ts +20 -0
  686. package/types/lib/third-party/arborist/lib/proc-log.d.ts.map +1 -0
  687. package/types/lib/third-party/arborist/lib/realpath.d.ts +3 -0
  688. package/types/lib/third-party/arborist/lib/realpath.d.ts.map +1 -0
  689. package/types/lib/third-party/arborist/lib/relpath.d.ts +3 -0
  690. package/types/lib/third-party/arborist/lib/relpath.d.ts.map +1 -0
  691. package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts +54 -0
  692. package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts.map +1 -0
  693. package/types/lib/third-party/arborist/lib/spec-from-lock.d.ts +3 -0
  694. package/types/lib/third-party/arborist/lib/spec-from-lock.d.ts.map +1 -0
  695. package/types/lib/third-party/arborist/lib/tree-check.d.ts +3 -0
  696. package/types/lib/third-party/arborist/lib/tree-check.d.ts.map +1 -0
  697. package/types/lib/third-party/arborist/lib/validate-npm-package-name.d.ts +13 -0
  698. package/types/lib/third-party/arborist/lib/validate-npm-package-name.d.ts.map +1 -0
  699. package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts +6 -0
  700. package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts.map +1 -0
  701. package/types/lib/third-party/arborist/lib/yarn-lock.d.ts +35 -0
  702. package/types/lib/third-party/arborist/lib/yarn-lock.d.ts.map +1 -0
  703. package/types/lib/validator/bomValidator.d.ts +40 -0
  704. package/types/lib/validator/bomValidator.d.ts.map +1 -0
  705. package/types/lib/validator/complianceEngine.d.ts +72 -0
  706. package/types/lib/validator/complianceEngine.d.ts.map +1 -0
  707. package/types/lib/validator/complianceRules.d.ts +61 -0
  708. package/types/lib/validator/complianceRules.d.ts.map +1 -0
  709. package/types/lib/validator/index.d.ts +101 -0
  710. package/types/lib/validator/index.d.ts.map +1 -0
  711. package/types/lib/validator/reporters/annotations.d.ts +37 -0
  712. package/types/lib/validator/reporters/annotations.d.ts.map +1 -0
  713. package/types/lib/validator/reporters/console.d.ts +34 -0
  714. package/types/lib/validator/reporters/console.d.ts.map +1 -0
  715. package/types/lib/validator/reporters/index.d.ts +30 -0
  716. package/types/lib/validator/reporters/index.d.ts.map +1 -0
  717. package/types/lib/validator/reporters/json.d.ts +11 -0
  718. package/types/lib/validator/reporters/json.d.ts.map +1 -0
  719. package/types/lib/validator/reporters/sarif.d.ts +22 -0
  720. package/types/lib/validator/reporters/sarif.d.ts.map +1 -0
  721. package/mod.d.ts +0 -7
  722. package/mod.js +0 -22
@@ -0,0 +1,2128 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import process from "node:process";
4
+
5
+ import {
6
+ getRecordedActivities,
7
+ isDryRun,
8
+ isSecureMode,
9
+ readEnvironmentVariable,
10
+ } from "../core/activity.js";
11
+ import { safeExistsSync } from "../core/fs.js";
12
+ import { toCamel } from "../core/paths.js";
13
+ import { detectAiModelVariants } from "./aiModelVariants.js";
14
+ import { formatOccurrenceEvidence } from "./evidenceUtils.js";
15
+ import {
16
+ formatHbomHardwareClassSummary,
17
+ getHbomSummary,
18
+ isHbomLikeBom,
19
+ } from "./hbomAnalysis.js";
20
+ import { getHostViewSummary, isMergedHostViewBom } from "./hostTopology.js";
21
+ import { getPropertyValue } from "./inventoryStats.js";
22
+ import {
23
+ hasComponentRegistryProvenance,
24
+ REGISTRY_PROVENANCE_ICON,
25
+ } from "./provenanceUtils.js";
26
+ import { createStream, table } from "./table.js";
27
+
28
+ // https://github.com/yangshun/tree-node-cli/blob/master/src/index.js
29
+ const SYMBOLS_ANSI = {
30
+ BRANCH: "├── ",
31
+ EMPTY: "",
32
+ INDENT: " ",
33
+ LAST_BRANCH: "└── ",
34
+ VERTICAL: "│ ",
35
+ };
36
+
37
+ const MAX_TREE_DEPTH = 6;
38
+ const CYCLE_NODE_ICON = "↺";
39
+ const REPEATED_NODE_ICON = "⤴";
40
+ const MULTIVALUE_ACTIVITY_TARGET_KEYS = new Set([
41
+ "LockFiles",
42
+ "ManifestFiles",
43
+ "PkgFiles",
44
+ "SrcFiles",
45
+ ]);
46
+ const PATH_SEPARATOR_REGEX = /[\\/]+/;
47
+ const SUSPICIOUS_SHELL_PATH_LABEL = "⚠ shell-metacharacters";
48
+ const ENV_AUDIT_SEVERITY_RANK = {
49
+ low: 1,
50
+ medium: 2,
51
+ high: 3,
52
+ critical: 4,
53
+ };
54
+ const ENV_AUDIT_TYPE_LABELS = {
55
+ "code-execution": "Code Execution",
56
+ "credential-exposure": "Credential Exposure",
57
+ "debug-exposure": "Debug Exposure",
58
+ "environment-variable": "Environment Variable",
59
+ "network-interception": "Network Interception",
60
+ "permission-misuse": "Permission Misuse",
61
+ privilege: "Privilege",
62
+ };
63
+
64
+ const highlightStr = (s, highlight) => {
65
+ if (highlight && s?.includes(highlight)) {
66
+ s = s.replaceAll(highlight, `\x1b[1;33m${highlight}\x1b[0m`);
67
+ }
68
+ return s;
69
+ };
70
+
71
+ const formatComponentName = (component, highlight) => {
72
+ const displayName = highlightStr(component?.name || "", highlight);
73
+ if (hasComponentRegistryProvenance(component)) {
74
+ return `${REGISTRY_PROVENANCE_ICON} ${displayName}`;
75
+ }
76
+ return displayName;
77
+ };
78
+
79
+ const isAiInventoryComponent = (component) =>
80
+ Boolean(
81
+ component?.type === "machine-learning-model" ||
82
+ component?.modelCard ||
83
+ component?.pedigree ||
84
+ getPropertyValue(component, "cdx:ai:provider") ||
85
+ getPropertyValue(component, "cdx:ai:kind"),
86
+ );
87
+
88
+ const getAiInventoryComponents = (bomJson) =>
89
+ (bomJson?.components || []).filter((component) =>
90
+ isAiInventoryComponent(component),
91
+ );
92
+
93
+ const isAiInventoryService = (service) =>
94
+ Boolean(
95
+ getPropertyValue(service, "cdx:ai:kind") ||
96
+ service?.provider?.name ||
97
+ (service?.tags || []).includes("ai"),
98
+ );
99
+
100
+ const getAiInventoryServices = (bomJson) =>
101
+ (bomJson?.services || []).filter((service) => isAiInventoryService(service));
102
+
103
+ const uniqueStrings = (values) => [...new Set((values || []).filter(Boolean))];
104
+
105
+ const formatLimitedList = (values, max = 3) => {
106
+ const uniqueValues = uniqueStrings(values);
107
+ if (!uniqueValues.length) {
108
+ return "";
109
+ }
110
+ return `${uniqueValues.slice(0, max).join(", ")}${
111
+ uniqueValues.length > max ? ` +${uniqueValues.length - max} more` : ""
112
+ }`;
113
+ };
114
+
115
+ const getAiVariantValues = (component) =>
116
+ detectAiModelVariants({
117
+ description: component?.description,
118
+ metadata: [component?.modelCard?.modelParameters?.task],
119
+ modelName: [component?.group, component?.name].filter(Boolean).join("/"),
120
+ notes: [component?.pedigree?.notes],
121
+ quantization: getPropertyValue(component, "cdx:ai:quantization"),
122
+ tags: component?.tags,
123
+ });
124
+
125
+ const getAiDatasetNames = (component, bomJson) =>
126
+ uniqueStrings(
127
+ component?.modelCard?.modelParameters?.datasets?.map((dataset) => {
128
+ if (dataset?.name) {
129
+ return dataset.name;
130
+ }
131
+ if (!dataset?.ref) {
132
+ return undefined;
133
+ }
134
+ const resolved = (bomJson?.components || []).find(
135
+ (candidate) => candidate?.["bom-ref"] === dataset.ref,
136
+ );
137
+ if (!resolved) {
138
+ return dataset.ref;
139
+ }
140
+ return resolved.group
141
+ ? `${resolved.group}/${resolved.name}`
142
+ : resolved.name;
143
+ }) || [],
144
+ );
145
+
146
+ const formatAiLicense = (component) =>
147
+ (component?.licenses || [])
148
+ .map(
149
+ (entry) =>
150
+ entry?.license?.id ||
151
+ entry?.license?.expression ||
152
+ entry?.license?.name ||
153
+ entry?.expression,
154
+ )
155
+ .filter(Boolean)
156
+ .join(", ");
157
+
158
+ const formatPedigreeNodeLabel = (component) =>
159
+ [
160
+ component?.group ? `${component.group}/${component.name}` : component?.name,
161
+ component?.version ? `@${component.version}` : "",
162
+ ].join("");
163
+
164
+ const formatAiQuantizationExplanation = (quantization) =>
165
+ quantization
166
+ ? `${quantization} — compressed weights for lower memory / faster inference trade-offs`
167
+ : undefined;
168
+
169
+ /**
170
+ * Build operator-facing AI-BOM summary lines.
171
+ *
172
+ * @param {Object} bomJson CycloneDX BOM JSON object
173
+ * @returns {string[]} formatted AI-BOM summary lines
174
+ */
175
+ export function buildAiBomInsightLines(bomJson) {
176
+ const aiComponents = getAiInventoryComponents(bomJson);
177
+ if (!aiComponents.length) {
178
+ return [];
179
+ }
180
+ const aiServices = getAiInventoryServices(bomJson);
181
+ const lines = [
182
+ "",
183
+ `AI-BOM: ${aiComponents.length} AI component(s) with model, pedigree, and runtime context`,
184
+ ];
185
+ if (aiServices.length) {
186
+ lines.push(
187
+ `Usage: ${aiServices.length} AI service(s) expose ${formatLimitedList(
188
+ aiServices.map(
189
+ (service) =>
190
+ service?.provider?.name || service?.group || service?.name,
191
+ ),
192
+ 4,
193
+ )}`,
194
+ );
195
+ }
196
+ for (const component of aiComponents.slice(0, 8)) {
197
+ const task =
198
+ component?.modelCard?.modelParameters?.task ||
199
+ getPropertyValue(component, "cdx:ai:modality");
200
+ const provider = getPropertyValue(component, "cdx:ai:provider");
201
+ const quantization = formatAiQuantizationExplanation(
202
+ getPropertyValue(component, "cdx:ai:quantization"),
203
+ );
204
+ const license = formatAiLicense(component);
205
+ const datasets = getAiDatasetNames(component, bomJson);
206
+ const variants = getAiVariantValues(component);
207
+ lines.push(
208
+ `${formatPedigreeNodeLabel(component)}${component?.description ? ` — ${component.description}` : ""}`,
209
+ );
210
+ if (provider) {
211
+ lines.push(`${SYMBOLS_ANSI.INDENT}Provider: ${provider}`);
212
+ }
213
+ if (task) {
214
+ lines.push(`${SYMBOLS_ANSI.INDENT}Task: ${task}`);
215
+ }
216
+ if (quantization) {
217
+ lines.push(`${SYMBOLS_ANSI.INDENT}Quantization: ${quantization}`);
218
+ }
219
+ if (variants.length) {
220
+ lines.push(`${SYMBOLS_ANSI.INDENT}Variants: ${variants.join(", ")}`);
221
+ }
222
+ if (license) {
223
+ lines.push(`${SYMBOLS_ANSI.INDENT}License: ${license}`);
224
+ }
225
+ const ancestors = component?.pedigree?.ancestors || [];
226
+ if (ancestors.length) {
227
+ lines.push(
228
+ `${SYMBOLS_ANSI.INDENT}Pedigree: derived from ${ancestors.length} upstream model(s)`,
229
+ );
230
+ ancestors.slice(0, 4).forEach((ancestor, index) => {
231
+ const branch =
232
+ index === ancestors.slice(0, 4).length - 1
233
+ ? SYMBOLS_ANSI.LAST_BRANCH
234
+ : SYMBOLS_ANSI.BRANCH;
235
+ lines.push(
236
+ `${SYMBOLS_ANSI.INDENT}${SYMBOLS_ANSI.INDENT}${branch}${formatPedigreeNodeLabel(ancestor)}`,
237
+ );
238
+ });
239
+ }
240
+ if (datasets.length) {
241
+ lines.push(
242
+ `${SYMBOLS_ANSI.INDENT}Datasets: ${datasets.slice(0, 3).join(", ")}${datasets.length > 3 ? ` +${datasets.length - 3} more` : ""}`,
243
+ );
244
+ }
245
+ if (component?.pedigree?.notes) {
246
+ lines.push(
247
+ `${SYMBOLS_ANSI.INDENT}ℹ ${String(component.pedigree.notes).replace(/^Hugging Face relation:\s*/u, "Relation: ")}`,
248
+ );
249
+ }
250
+ }
251
+ if (aiComponents.length > 8) {
252
+ lines.push(`… ${aiComponents.length - 8} more AI component(s) omitted`);
253
+ }
254
+ return lines;
255
+ }
256
+
257
+ /**
258
+ * Print operator-facing AI-BOM summary lines.
259
+ *
260
+ * @param {Object} bomJson CycloneDX BOM JSON object
261
+ */
262
+ export function printAiBomInsights(bomJson) {
263
+ const lines = buildAiBomInsightLines(bomJson);
264
+ if (lines.length) {
265
+ console.log(lines.join("\n"));
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Build AI-BOM pedigree lines for REPL inspection.
271
+ *
272
+ * @param {Object} bomJson CycloneDX BOM JSON object
273
+ * @returns {string[]} formatted pedigree lines
274
+ */
275
+ export function buildAiBomPedigreeLines(bomJson) {
276
+ const pedigreeComponents = getAiInventoryComponents(bomJson).filter(
277
+ (component) =>
278
+ component?.pedigree?.ancestors?.length ||
279
+ component?.pedigree?.variants?.length ||
280
+ component?.pedigree?.notes,
281
+ );
282
+ if (!pedigreeComponents.length) {
283
+ return [];
284
+ }
285
+ const lines = ["", `AI pedigree view: ${pedigreeComponents.length} model(s)`];
286
+ for (const component of pedigreeComponents.slice(0, 10)) {
287
+ lines.push(formatPedigreeNodeLabel(component));
288
+ for (const [label, nodes] of [
289
+ ["Ancestors", component?.pedigree?.ancestors || []],
290
+ ["Variants", component?.pedigree?.variants || []],
291
+ ]) {
292
+ if (!nodes.length) {
293
+ continue;
294
+ }
295
+ lines.push(`${SYMBOLS_ANSI.INDENT}${label}:`);
296
+ nodes.slice(0, 4).forEach((node, index) => {
297
+ lines.push(
298
+ `${SYMBOLS_ANSI.INDENT}${SYMBOLS_ANSI.INDENT}${
299
+ index === nodes.slice(0, 4).length - 1
300
+ ? SYMBOLS_ANSI.LAST_BRANCH
301
+ : SYMBOLS_ANSI.BRANCH
302
+ }${formatPedigreeNodeLabel(node)}`,
303
+ );
304
+ });
305
+ }
306
+ if (component?.pedigree?.notes) {
307
+ lines.push(`${SYMBOLS_ANSI.INDENT}ℹ ${component.pedigree.notes}`);
308
+ }
309
+ }
310
+ if (pedigreeComponents.length > 10) {
311
+ lines.push(
312
+ `… ${pedigreeComponents.length - 10} more pedigree entries omitted`,
313
+ );
314
+ }
315
+ return lines;
316
+ }
317
+
318
+ /**
319
+ * Print AI-BOM pedigree lines.
320
+ *
321
+ * @param {Object} bomJson CycloneDX BOM JSON object
322
+ */
323
+ export function printAiBomPedigree(bomJson) {
324
+ const lines = buildAiBomPedigreeLines(bomJson);
325
+ if (lines.length) {
326
+ console.log(lines.join("\n"));
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Build AI-BOM model variant lines for REPL inspection.
332
+ *
333
+ * @param {Object} bomJson CycloneDX BOM JSON object
334
+ * @returns {string[]} formatted variant lines
335
+ */
336
+ export function buildAiBomVariantLines(bomJson) {
337
+ const variantComponents = getAiInventoryComponents(bomJson).filter(
338
+ (component) => getAiVariantValues(component).length,
339
+ );
340
+ if (!variantComponents.length) {
341
+ return [];
342
+ }
343
+ const lines = ["", `AI variant view: ${variantComponents.length} model(s)`];
344
+ for (const component of variantComponents.slice(0, 10)) {
345
+ const variants = getAiVariantValues(component);
346
+ lines.push(
347
+ `${formatPedigreeNodeLabel(component)} — ${variants.join(", ")}`,
348
+ );
349
+ if (component?.pedigree?.notes) {
350
+ lines.push(`${SYMBOLS_ANSI.INDENT}ℹ ${component.pedigree.notes}`);
351
+ }
352
+ }
353
+ if (variantComponents.length > 10) {
354
+ lines.push(
355
+ `… ${variantComponents.length - 10} more variant entries omitted`,
356
+ );
357
+ }
358
+ return lines;
359
+ }
360
+
361
+ /**
362
+ * Print AI-BOM model variant lines.
363
+ *
364
+ * @param {Object} bomJson CycloneDX BOM JSON object
365
+ */
366
+ export function printAiBomVariants(bomJson) {
367
+ const lines = buildAiBomVariantLines(bomJson);
368
+ if (lines.length) {
369
+ console.log(lines.join("\n"));
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Build AI-BOM dataset usage lines for REPL inspection.
375
+ *
376
+ * @param {Object} bomJson CycloneDX BOM JSON object
377
+ * @returns {string[]} formatted dataset lines
378
+ */
379
+ export function buildAiBomDatasetLines(bomJson) {
380
+ const datasetComponents = getAiInventoryComponents(bomJson).filter(
381
+ (component) => getAiDatasetNames(component).length,
382
+ );
383
+ if (!datasetComponents.length) {
384
+ return [];
385
+ }
386
+ const lines = ["", `AI dataset view: ${datasetComponents.length} model(s)`];
387
+ for (const component of datasetComponents.slice(0, 10)) {
388
+ lines.push(
389
+ `${formatPedigreeNodeLabel(component)} — ${formatLimitedList(
390
+ getAiDatasetNames(component),
391
+ 4,
392
+ )}`,
393
+ );
394
+ }
395
+ if (datasetComponents.length > 10) {
396
+ lines.push(
397
+ `… ${datasetComponents.length - 10} more dataset entries omitted`,
398
+ );
399
+ }
400
+ return lines;
401
+ }
402
+
403
+ /**
404
+ * Print AI-BOM dataset usage lines.
405
+ *
406
+ * @param {Object} bomJson CycloneDX BOM JSON object
407
+ */
408
+ export function printAiBomDatasets(bomJson) {
409
+ const lines = buildAiBomDatasetLines(bomJson);
410
+ if (lines.length) {
411
+ console.log(lines.join("\n"));
412
+ }
413
+ }
414
+
415
+ /**
416
+ * Builds the summary and provenance lines printed after the component table.
417
+ *
418
+ * @param {Object} bomJson CycloneDX BOM JSON object
419
+ * @param {string[]|undefined} filterTypes Optional list of component types to include
420
+ * @param {string|undefined} summaryText Optional summary message to print after the table
421
+ * @param {number} displayedProvenanceCount Number of displayed components with registry provenance
422
+ * @returns {string[]} Summary lines to print
423
+ */
424
+ export const buildTableSummaryLines = (
425
+ bomJson,
426
+ filterTypes,
427
+ summaryText,
428
+ displayedProvenanceCount = 0,
429
+ ) => {
430
+ const summaryLines = [];
431
+ if (summaryText) {
432
+ summaryLines.push(summaryText);
433
+ } else if (!filterTypes && isHbomLikeBom(bomJson)) {
434
+ const hbomSummary = getHbomSummary(bomJson);
435
+ summaryLines.push(
436
+ `HBOM includes ${hbomSummary.componentCount} hardware component(s) across ${hbomSummary.hardwareClassCount} hardware class(es)`,
437
+ );
438
+ if (hbomSummary.hardwareClassCounts.length) {
439
+ summaryLines.push(
440
+ `Top hardware classes: ${formatHbomHardwareClassSummary(hbomSummary.hardwareClassCounts)}`,
441
+ );
442
+ }
443
+ if (hbomSummary.collectorProfile) {
444
+ summaryLines.push(
445
+ `Collector profile: ${hbomSummary.collectorProfile}; command evidence: ${hbomSummary.evidenceCommandCount}; observed files: ${hbomSummary.evidenceFileCount}`,
446
+ );
447
+ }
448
+ if (hbomSummary.commandDiagnosticCount) {
449
+ const diagnosticDetails = [];
450
+ if (hbomSummary.missingCommandCount) {
451
+ diagnosticDetails.push(
452
+ `missing commands: ${hbomSummary.missingCommandCount}`,
453
+ );
454
+ }
455
+ if (hbomSummary.permissionDeniedCount) {
456
+ diagnosticDetails.push(
457
+ `permission denied: ${hbomSummary.permissionDeniedCount}`,
458
+ );
459
+ }
460
+ if (hbomSummary.partialSupportCount) {
461
+ diagnosticDetails.push(
462
+ `partial support: ${hbomSummary.partialSupportCount}`,
463
+ );
464
+ }
465
+ if (hbomSummary.timeoutCount) {
466
+ diagnosticDetails.push(`timeouts: ${hbomSummary.timeoutCount}`);
467
+ }
468
+ if (hbomSummary.commandErrorCount) {
469
+ diagnosticDetails.push(
470
+ `other command errors: ${hbomSummary.commandErrorCount}`,
471
+ );
472
+ }
473
+ summaryLines.push(
474
+ `Collector diagnostics: ${hbomSummary.commandDiagnosticCount} issue(s)${diagnosticDetails.length ? `; ${diagnosticDetails.join(", ")}` : ""}`,
475
+ );
476
+ if (hbomSummary.requiresPrivilegedEnrichment) {
477
+ summaryLines.push(
478
+ "Permission-sensitive enrichments were skipped or blocked. Re-run with --privileged where policy allows.",
479
+ );
480
+ }
481
+ }
482
+ if (isMergedHostViewBom(bomJson)) {
483
+ const hostViewSummary = getHostViewSummary(bomJson);
484
+ summaryLines.push(
485
+ `Host topology view: ${hostViewSummary.runtimeComponentCount} runtime component(s), ${hostViewSummary.topologyLinkCount} strict host/runtime topology link(s), ${hostViewSummary.linkedHardwareComponentCount} linked hardware component(s)`,
486
+ );
487
+ if (hostViewSummary.linkedRuntimeCategories.length) {
488
+ summaryLines.push(
489
+ `Linked runtime categories: ${hostViewSummary.linkedRuntimeCategories.join(", ")}`,
490
+ );
491
+ }
492
+ }
493
+ } else if (!filterTypes) {
494
+ summaryLines.push(
495
+ `BOM includes ${bomJson?.components?.length || 0} components and ${
496
+ bomJson?.dependencies?.length || 0
497
+ } dependencies`,
498
+ );
499
+ } else {
500
+ summaryLines.push(
501
+ `Components filtered based on type: ${filterTypes.join(", ")}`,
502
+ );
503
+ }
504
+ const instrumentedCount = bomJson?.components?.filter((c) =>
505
+ c.evidence?.identity?.some((id) =>
506
+ id.methods?.some((m) => m.technique === "instrumentation"),
507
+ ),
508
+ ).length;
509
+ if (instrumentedCount > 0) {
510
+ summaryLines.push(
511
+ `Instrumented components: ${instrumentedCount} dynamic librar${instrumentedCount === 1 ? "y" : "ies"} tracked via process tracing.`,
512
+ );
513
+ }
514
+ if (displayedProvenanceCount > 0) {
515
+ summaryLines.push(
516
+ `${displayedProvenanceCount} component(s) include registry provenance or trusted publishing metadata.`,
517
+ );
518
+ }
519
+ return summaryLines;
520
+ };
521
+
522
+ const HBOM_COLUMN_PRIORITY = Object.freeze([
523
+ ["cdx:hbom:status", "status"],
524
+ ["cdx:hbom:connected", "connected"],
525
+ ["cdx:hbom:connectionState", "connectionState"],
526
+ ["cdx:hbom:securityMode", "securityMode"],
527
+ ["cdx:hbom:health", "health"],
528
+ ["cdx:hbom:smartStatus", "smartStatus"],
529
+ ["cdx:hbom:powerSource", "powerSource"],
530
+ ["cdx:hbom:maximumCapacity", "maximumCapacity"],
531
+ ["cdx:hbom:chargePercent", "chargePercent"],
532
+ ["cdx:hbom:capacity", "capacity"],
533
+ ["cdx:hbom:size", "size"],
534
+ ["cdx:hbom:sizeBytes", "sizeBytes"],
535
+ ["cdx:hbom:linkRateMbps", "linkRateMbps"],
536
+ ["cdx:hbom:speedMbps", "speedMbps"],
537
+ ["cdx:hbom:resolution", "resolution"],
538
+ ["cdx:hbom:transport", "transport"],
539
+ ["cdx:hbom:connectionType", "connectionType"],
540
+ ["cdx:hbom:firmwareVersion", "firmwareVersion"],
541
+ ["cdx:hbom:driver", "driver"],
542
+ ["cdx:hbom:channel", "channel"],
543
+ ["cdx:hbom:phyMode", "phyMode"],
544
+ ["cdx:hbom:temperatureCelsius", "temperatureCelsius"],
545
+ ["cdx:hostview:runtimeAddressCount", "runtimeAddrs"],
546
+ ["cdx:hostview:kernel_modules:count", "kernelMods"],
547
+ ["cdx:hostview:mount_hardening:count", "runtimeMounts"],
548
+ ["cdx:hostview:runtime-storage:count", "runtimeStorage"],
549
+ ["cdx:hostview:linkedRuntimeCategoryCount", "runtimeLinks"],
550
+ ]);
551
+
552
+ const HBOM_CLASS_PROPERTY_PRIORITY = Object.freeze({
553
+ "audio-device": Object.freeze([
554
+ ["cdx:hbom:transport", "transport"],
555
+ ["cdx:hbom:defaultOutput", "defaultOutput"],
556
+ ["cdx:hbom:defaultInput", "defaultInput"],
557
+ ["cdx:hbom:sampleRate", "sampleRate"],
558
+ ]),
559
+ bus: Object.freeze([
560
+ ["cdx:hbom:speed", "speed"],
561
+ ["cdx:hbom:linkStatus", "linkStatus"],
562
+ ["cdx:hbom:receptacleStatus", "receptacleStatus"],
563
+ ]),
564
+ camera: Object.freeze([
565
+ ["cdx:hbom:isVirtual", "virtual"],
566
+ ["cdx:hbom:cameraModelId", "modelId"],
567
+ ]),
568
+ "bluetooth-controller": Object.freeze([
569
+ ["cdx:hbom:state", "state"],
570
+ ["cdx:hbom:transport", "transport"],
571
+ ["cdx:hbom:firmwareVersion", "firmware"],
572
+ ]),
573
+ "bluetooth-device": Object.freeze([
574
+ ["cdx:hbom:connectionState", "connection"],
575
+ ["cdx:hbom:rssi", "rssi"],
576
+ ["cdx:hbom:firmwareVersion", "firmware"],
577
+ ["cdx:hbom:minorType", "minorType"],
578
+ ]),
579
+ display: Object.freeze([
580
+ ["cdx:hbom:resolution", "resolution"],
581
+ ["cdx:hbom:connectionType", "connection"],
582
+ ["cdx:hbom:vendorId", "vendorId"],
583
+ ["cdx:hbom:productId", "productId"],
584
+ ]),
585
+ memory: Object.freeze([
586
+ ["cdx:hbom:size", "size"],
587
+ ["cdx:hbom:sizeBytes", "sizeBytes"],
588
+ ["cdx:hbom:memoryOnlineSize", "onlineSize"],
589
+ ["cdx:hbom:addressSizes", "addressSizes"],
590
+ ]),
591
+ "network-interface": Object.freeze([
592
+ ["cdx:hbom:status", "status"],
593
+ ["cdx:hbom:speedMbps", "speedMbps"],
594
+ ["cdx:hbom:duplex", "duplex"],
595
+ ["cdx:hbom:driver", "driver"],
596
+ ["cdx:hostview:runtimeAddressCount", "runtimeAddrs"],
597
+ ["cdx:hostview:kernel_modules:count", "kernelMods"],
598
+ ]),
599
+ power: Object.freeze([
600
+ ["cdx:hbom:health", "health"],
601
+ ["cdx:hbom:chargePercent", "charge%"],
602
+ ["cdx:hbom:maximumCapacity", "maxCapacity"],
603
+ ["cdx:hbom:cycleCount", "cycles"],
604
+ ["cdx:hbom:powerSource", "source"],
605
+ ]),
606
+ "power-adapter": Object.freeze([
607
+ ["cdx:hbom:connected", "connected"],
608
+ ["cdx:hbom:watts", "watts"],
609
+ ["cdx:hbom:isCharging", "charging"],
610
+ ]),
611
+ processor: Object.freeze([
612
+ ["cdx:hbom:coreCount", "cores"],
613
+ ["cdx:hbom:logicalCpuCount", "logical"],
614
+ ["cdx:hbom:physicalCpuCount", "physical"],
615
+ ]),
616
+ storage: Object.freeze([
617
+ ["cdx:hbom:capacity", "capacity"],
618
+ ["cdx:hbom:smartStatus", "smart"],
619
+ ["cdx:hbom:wearPercentageUsed", "wearUsed"],
620
+ ["cdx:hbom:transport", "transport"],
621
+ ["cdx:hbom:firmwareVersion", "firmware"],
622
+ ["cdx:hostview:mount_hardening:count", "runtimeMounts"],
623
+ ["cdx:hostview:runtime-storage:count", "runtimeStorage"],
624
+ ]),
625
+ "storage-volume": Object.freeze([
626
+ ["cdx:hbom:size", "size"],
627
+ ["cdx:hbom:capacity", "capacity"],
628
+ ["cdx:hbom:fileVault", "fileVault"],
629
+ ["cdx:hbom:isEncrypted", "encrypted"],
630
+ ["cdx:hbom:isRemovable", "removable"],
631
+ ["cdx:hostview:mount_hardening:count", "runtimeMounts"],
632
+ ["cdx:hostview:runtime-storage:count", "runtimeStorage"],
633
+ ]),
634
+ sensor: Object.freeze([
635
+ ["cdx:hbom:temperatureCelsius", "tempC"],
636
+ ["cdx:hbom:fanCount", "fanCount"],
637
+ ]),
638
+ "thermal-zone": Object.freeze([
639
+ ["cdx:hbom:temperatureCelsius", "tempC"],
640
+ ["cdx:hbom:fanCount", "fanCount"],
641
+ ]),
642
+ "wireless-adapter": Object.freeze([
643
+ ["cdx:hbom:connected", "connected"],
644
+ ["cdx:hbom:securityMode", "security"],
645
+ ["cdx:hbom:linkRateMbps", "linkMbps"],
646
+ ["cdx:hbom:channel", "channel"],
647
+ ["cdx:hbom:phyMode", "phy"],
648
+ ["cdx:hostview:runtimeAddressCount", "runtimeAddrs"],
649
+ ]),
650
+ });
651
+
652
+ function formatHbomKeyProperties(component) {
653
+ const hardwareClass = getPropertyValue(component, "cdx:hbom:hardwareClass");
654
+ const classSpecificPriority =
655
+ HBOM_CLASS_PROPERTY_PRIORITY[hardwareClass] || [];
656
+ const details = [];
657
+ const seenPropertyNames = new Set();
658
+ for (const [propertyName, label] of [
659
+ ...classSpecificPriority,
660
+ ...HBOM_COLUMN_PRIORITY,
661
+ ]) {
662
+ if (seenPropertyNames.has(propertyName)) {
663
+ continue;
664
+ }
665
+ seenPropertyNames.add(propertyName);
666
+ const value = getPropertyValue(component, propertyName);
667
+ if (value === undefined || value === null || value === "") {
668
+ continue;
669
+ }
670
+ details.push(`${label}=${value}`);
671
+ if (details.length >= 3) {
672
+ break;
673
+ }
674
+ }
675
+ return details.join(", ");
676
+ }
677
+
678
+ function printHBOMTable(bomJson, filterTypes, highlight, summaryText) {
679
+ const config = {
680
+ columnDefault: {
681
+ width: 28,
682
+ },
683
+ columnCount: 5,
684
+ columns: [
685
+ { width: 22 },
686
+ { width: 32 },
687
+ { width: 24 },
688
+ { width: 52 },
689
+ { width: 24 },
690
+ ],
691
+ };
692
+ const stream = createStream(config);
693
+ stream.write([
694
+ "Hardware Class",
695
+ "Name",
696
+ "Manufacturer / Version",
697
+ "Key Properties",
698
+ "Tags",
699
+ ]);
700
+ for (const comp of bomJson.components) {
701
+ if (filterTypes && !filterTypes.includes(comp.type)) {
702
+ continue;
703
+ }
704
+ const manufacturerOrVersion = [comp.manufacturer?.name, comp.version]
705
+ .filter(Boolean)
706
+ .join(" / ");
707
+ stream.write([
708
+ highlightStr(
709
+ getPropertyValue(comp, "cdx:hbom:hardwareClass") || comp.type || "",
710
+ highlight,
711
+ ),
712
+ formatComponentName(comp, highlight),
713
+ highlightStr(manufacturerOrVersion, highlight),
714
+ highlightStr(formatHbomKeyProperties(comp), highlight),
715
+ (comp.tags || []).join(", "),
716
+ ]);
717
+ }
718
+ stream.end();
719
+ console.log();
720
+ for (const line of buildTableSummaryLines(
721
+ bomJson,
722
+ filterTypes,
723
+ summaryText,
724
+ 0,
725
+ )) {
726
+ console.log(line);
727
+ }
728
+ if (!filterTypes?.length) {
729
+ printAiBomInsights(bomJson);
730
+ }
731
+ }
732
+
733
+ /**
734
+ * Builds legend lines for dependency tree marker icons.
735
+ *
736
+ * @param {string[]} treeGraphics Dependency tree lines
737
+ * @returns {string[]} Legend lines to print after the tree output
738
+ */
739
+ export const buildDependencyTreeLegendLines = (treeGraphics) => {
740
+ const legendLines = [];
741
+ if (treeGraphics.some((line) => line.includes(`${REPEATED_NODE_ICON} `))) {
742
+ legendLines.push(`${REPEATED_NODE_ICON} = already shown`);
743
+ }
744
+ if (treeGraphics.some((line) => line.includes(`${CYCLE_NODE_ICON} `))) {
745
+ legendLines.push(`${CYCLE_NODE_ICON} = cycle`);
746
+ }
747
+ if (!legendLines.length) {
748
+ return legendLines;
749
+ }
750
+ return [`Legend: ${legendLines.join("; ")}`];
751
+ };
752
+
753
+ /**
754
+ * Aggregate recorded activities into a summary payload with mode and status counts.
755
+ *
756
+ * @param {Object[]} activities Recorded activity objects with a status field
757
+ * @param {boolean} [dryRunMode] Whether the run was a dry run; defaults to the global dry-run flag
758
+ * @returns {Object} Payload containing the activities, mode ("dry-run" or "debug"), and summary counts
759
+ */
760
+ export function buildActivitySummaryPayload(activities, dryRunMode = isDryRun) {
761
+ const completedCount = activities.filter(
762
+ ({ status }) => status === "completed",
763
+ ).length;
764
+ const blockedCount = activities.filter(
765
+ ({ status }) => status === "blocked",
766
+ ).length;
767
+ const failedCount = activities.filter(
768
+ ({ status }) => status === "failed",
769
+ ).length;
770
+ return {
771
+ activities,
772
+ mode: dryRunMode ? "dry-run" : "debug",
773
+ summary: {
774
+ blocked: blockedCount,
775
+ completed: completedCount,
776
+ failed: failedCount,
777
+ total: activities.length,
778
+ },
779
+ };
780
+ }
781
+
782
+ /**
783
+ * Serialize an activity summary into report lines for the given report type.
784
+ *
785
+ * Produces a single pretty-printed JSON document for "json", newline-delimited
786
+ * JSON records (a summary record followed by one record per activity) for
787
+ * "jsonl", and no lines otherwise.
788
+ *
789
+ * @param {Object[]} activities Recorded activity objects
790
+ * @param {string} [reportType] Report format: "json" or "jsonl"
791
+ * @param {boolean} [dryRunMode] Whether the run was a dry run; defaults to the global dry-run flag
792
+ * @returns {string[]} Serialized report lines; empty when the report type is unsupported
793
+ */
794
+ export function serializeActivitySummary(
795
+ activities,
796
+ reportType = "json",
797
+ dryRunMode = isDryRun,
798
+ ) {
799
+ const activitySummaryPayload = buildActivitySummaryPayload(
800
+ activities,
801
+ dryRunMode,
802
+ );
803
+ if (reportType === "json") {
804
+ return [JSON.stringify(activitySummaryPayload, null, 2)];
805
+ }
806
+ if (reportType === "jsonl") {
807
+ return [
808
+ JSON.stringify({
809
+ mode: activitySummaryPayload.mode,
810
+ recordType: "summary",
811
+ ...activitySummaryPayload.summary,
812
+ }),
813
+ ...activities.map((activity) =>
814
+ JSON.stringify({
815
+ recordType: "activity",
816
+ ...activity,
817
+ }),
818
+ ),
819
+ ];
820
+ }
821
+ return [];
822
+ }
823
+
824
+ const splitCommaSeparatedActivityEntries = (value) =>
825
+ value
826
+ .split(",")
827
+ .map((entry) => entry.trim())
828
+ .filter(Boolean);
829
+
830
+ const activityPathDepth = (entry) =>
831
+ entry.split(PATH_SEPARATOR_REGEX).filter(Boolean).length;
832
+
833
+ const sortActivityTargetEntries = (entries) =>
834
+ [...entries].sort((left, right) => {
835
+ const depthDiff = activityPathDepth(left) - activityPathDepth(right);
836
+ if (depthDiff !== 0) {
837
+ return depthDiff;
838
+ }
839
+ const lengthDiff = left.length - right.length;
840
+ if (lengthDiff !== 0) {
841
+ return lengthDiff;
842
+ }
843
+ return left.localeCompare(right);
844
+ });
845
+
846
+ const isLikelyActivityPathList = (entries) =>
847
+ entries.length > 1 &&
848
+ entries.every(
849
+ (entry) => PATH_SEPARATOR_REGEX.test(entry) && !entry.includes("://"),
850
+ );
851
+ /**
852
+ * Prints the BOM components as a streaming table to the console.
853
+ * Delegates to {@link printOSTable} automatically when the BOM metadata indicates
854
+ * an operating-system or platform component type.
855
+ *
856
+ * @param {Object} bomJson CycloneDX BOM JSON object
857
+ * @param {string[]} [filterTypes] Optional list of component types to include; all types shown when omitted
858
+ * @param {string} [highlight] Optional string to highlight in the output
859
+ * @param {string} [summaryText] Optional summary message to print after the table
860
+ * @returns {void}
861
+ */
862
+ export function printTable(
863
+ bomJson,
864
+ filterTypes = undefined,
865
+ highlight = undefined,
866
+ summaryText = undefined,
867
+ ) {
868
+ if (!bomJson?.components) {
869
+ return;
870
+ }
871
+ if (
872
+ bomJson.metadata?.component &&
873
+ ["operating-system", "platform"].includes(bomJson.metadata.component.type)
874
+ ) {
875
+ return printOSTable(bomJson);
876
+ }
877
+ if (isHbomLikeBom(bomJson) && !filterTypes?.includes("cryptographic-asset")) {
878
+ return printHBOMTable(bomJson, filterTypes, highlight, summaryText);
879
+ }
880
+ const config = {
881
+ columnDefault: {
882
+ width: 30,
883
+ },
884
+ columnCount: 5,
885
+ columns: [
886
+ { width: 25 },
887
+ { width: 35 },
888
+ { width: 25, alignment: "right" },
889
+ { width: 15 },
890
+ { width: 25 },
891
+ ],
892
+ };
893
+ const stream = createStream(config);
894
+ let displayedProvenanceCount = 0;
895
+ stream.write([
896
+ filterTypes?.includes("cryptographic-asset")
897
+ ? "Asset Type / Group"
898
+ : "Group",
899
+ "Name",
900
+ filterTypes?.includes("cryptographic-asset") ? "Version / oid" : "Version",
901
+ "Scope",
902
+ "Tags",
903
+ ]);
904
+ for (const comp of bomJson.components) {
905
+ if (filterTypes && !filterTypes.includes(comp.type)) {
906
+ continue;
907
+ }
908
+ if (comp.type === "cryptographic-asset") {
909
+ stream.write([
910
+ comp.cryptoProperties?.assetType || comp.group || "",
911
+ comp.name,
912
+ `\x1b[1;35m${comp.cryptoProperties?.oid || ""}\x1b[0m`,
913
+ comp.scope || "",
914
+ (comp.tags || []).join(", "),
915
+ ]);
916
+ } else {
917
+ if (hasComponentRegistryProvenance(comp)) {
918
+ displayedProvenanceCount += 1;
919
+ }
920
+ stream.write([
921
+ highlightStr(comp.group || "", highlight),
922
+ formatComponentName(comp, highlight),
923
+ `\x1b[1;35m${comp.version || ""}\x1b[0m`,
924
+ comp.scope || "",
925
+ (comp.tags || []).join(", "),
926
+ ]);
927
+ }
928
+ }
929
+ stream.end();
930
+ console.log();
931
+ for (const line of buildTableSummaryLines(
932
+ bomJson,
933
+ filterTypes,
934
+ summaryText,
935
+ displayedProvenanceCount,
936
+ )) {
937
+ console.log(line);
938
+ }
939
+ }
940
+ const formatProps = (props) => {
941
+ const retList = [];
942
+ for (const p of props) {
943
+ retList.push(`\x1b[0;32m${p.name}\x1b[0m ${p.value}`);
944
+ }
945
+ return retList.join("\n");
946
+ };
947
+ /**
948
+ * Prints OS package components from the BOM as a formatted streaming table.
949
+ *
950
+ * @param {Object} bomJson CycloneDX BOM JSON object
951
+ * @returns {void}
952
+ */
953
+ export function printOSTable(bomJson) {
954
+ const config = {
955
+ columnDefault: {
956
+ width: 50,
957
+ },
958
+ columnCount: 4,
959
+ columns: [{ width: 20 }, { width: 40 }, { width: 50 }, { width: 25 }],
960
+ };
961
+ const stream = createStream(config);
962
+ stream.write(["Type", "Title", "Properties", "Tags"]);
963
+ for (const comp of bomJson.components) {
964
+ stream.write([
965
+ comp.type,
966
+ `\x1b[1;35m${comp.name.replace(/\+/g, " ").replace(/--/g, "::")}\x1b[0m`,
967
+ formatProps(comp.properties || []),
968
+ (comp.tags || []).join(", "),
969
+ ]);
970
+ }
971
+ stream.end();
972
+ console.log();
973
+ }
974
+ /**
975
+ * Prints the services listed in the BOM as a formatted table.
976
+ * Includes endpoint URLs, authentication flag, and cross-trust-boundary flag.
977
+ *
978
+ * @param {Object} bomJson CycloneDX BOM JSON object
979
+ * @returns {void}
980
+ */
981
+ export function printServices(bomJson) {
982
+ const data = [["Name", "Endpoints", "Authenticated", "X Trust Boundary"]];
983
+ if (!bomJson?.services) {
984
+ return;
985
+ }
986
+ for (const aservice of bomJson.services) {
987
+ data.push([
988
+ aservice.name || "",
989
+ aservice.endpoints ? aservice.endpoints.join("\n") : "",
990
+ aservice.authenticated ? "\x1b[1;35mYes\x1b[0m" : "",
991
+ aservice.xTrustBoundary ? "\x1b[1;35mYes\x1b[0m" : "",
992
+ ]);
993
+ }
994
+ const config = {
995
+ header: {
996
+ alignment: "center",
997
+ content: "List of Services\nGenerated with \u2665 by cdxgen",
998
+ },
999
+ };
1000
+ if (data.length > 1) {
1001
+ console.log(table(data, config));
1002
+ }
1003
+ }
1004
+
1005
+ /**
1006
+ * Prints the formulation components from the BOM as a formatted table.
1007
+ *
1008
+ * @param {Object} bomJson CycloneDX BOM JSON object
1009
+ * @returns {void}
1010
+ */
1011
+ export function printFormulation(bomJson) {
1012
+ const data = [["Type", "Name", "Version"]];
1013
+ if (!bomJson?.formulation) {
1014
+ return;
1015
+ }
1016
+ for (const aform of bomJson.formulation) {
1017
+ if (aform.components) {
1018
+ for (const acomp of aform.components) {
1019
+ data.push([acomp.type || "", acomp.name || "", acomp.version || ""]);
1020
+ }
1021
+ }
1022
+ }
1023
+ const config = {
1024
+ header: {
1025
+ alignment: "center",
1026
+ content: "Formulation\nGenerated with \u2665 by cdxgen",
1027
+ },
1028
+ };
1029
+ if (data.length > 1) {
1030
+ console.log(table(data, config));
1031
+ }
1032
+ }
1033
+
1034
+ const locationComparator = (a, b) => {
1035
+ if (a && b && a.includes("#") && b.includes("#")) {
1036
+ const tmpA = a.split("#");
1037
+ const tmpB = b.split("#");
1038
+ if (tmpA.length === 2 && tmpB.length === 2) {
1039
+ if (tmpA[0] === tmpB[0]) {
1040
+ return tmpA[1] - tmpB[1];
1041
+ }
1042
+ }
1043
+ }
1044
+ if (a && b) {
1045
+ const tmpA = a.match(/^(.*):(\d+)(?::(\d+))?$/);
1046
+ const tmpB = b.match(/^(.*):(\d+)(?::(\d+))?$/);
1047
+ if (tmpA && tmpB && tmpA[1] === tmpB[1]) {
1048
+ const lineComparison = Number(tmpA[2]) - Number(tmpB[2]);
1049
+ if (lineComparison !== 0) {
1050
+ return lineComparison;
1051
+ }
1052
+ return Number(tmpA[3] || 0) - Number(tmpB[3] || 0);
1053
+ }
1054
+ }
1055
+ return a.localeCompare(b);
1056
+ };
1057
+
1058
+ /**
1059
+ * Prints component evidence occurrences (file locations) as a streaming table.
1060
+ * Only components that have `evidence.occurrences` are included.
1061
+ *
1062
+ * @param {Object} bomJson CycloneDX BOM JSON object
1063
+ * @returns {void}
1064
+ */
1065
+ export function printOccurrences(bomJson) {
1066
+ if (!bomJson?.components) {
1067
+ return;
1068
+ }
1069
+ const data = ["Group", "Name", "Version", "Occurrences"];
1070
+ const config = {
1071
+ columnDefault: {
1072
+ width: 30,
1073
+ },
1074
+ columnCount: 4,
1075
+ columns: [
1076
+ { width: 30 },
1077
+ { width: 30 },
1078
+ { width: 25, alignment: "right" },
1079
+ { width: 80 },
1080
+ ],
1081
+ };
1082
+ const stream = createStream(config); // Create stream with the config
1083
+ const header = "Component Evidence\nGenerated with \u2665 by cdxgen";
1084
+ console.log(header);
1085
+ stream.write(data);
1086
+ // Stream the components
1087
+ for (const comp of bomJson.components) {
1088
+ if (comp.evidence?.occurrences) {
1089
+ const row = [
1090
+ comp.group || "",
1091
+ comp.name,
1092
+ comp.version || "",
1093
+ comp.evidence.occurrences
1094
+ .map((occurrence) => formatOccurrenceEvidence(occurrence))
1095
+ .sort(locationComparator)
1096
+ .join("\n"),
1097
+ ];
1098
+ stream.write(row);
1099
+ }
1100
+ }
1101
+ stream.end();
1102
+ console.log();
1103
+ }
1104
+
1105
+ /**
1106
+ * Prints the call stack evidence for each component in the BOM as a formatted table.
1107
+ * Only components that have `evidence.callstack.frames` are included.
1108
+ *
1109
+ * @param {Object} bomJson CycloneDX BOM JSON object
1110
+ * @returns {void}
1111
+ */
1112
+ export function printCallStack(bomJson) {
1113
+ const data = [["Group", "Name", "Version", "Call Stack"]];
1114
+ if (!bomJson?.components) {
1115
+ return;
1116
+ }
1117
+ for (const comp of bomJson.components) {
1118
+ if (!comp.evidence?.callstack?.frames) {
1119
+ continue;
1120
+ }
1121
+ const frames = [];
1122
+ const seenFrames = new Set();
1123
+ for (const frame of comp.evidence.callstack.frames) {
1124
+ const location = `${frame.fullFilename}${frame.line ? `#${frame.line}` : ""}`;
1125
+ if (!frame.fullFilename || seenFrames.has(location)) {
1126
+ continue;
1127
+ }
1128
+ seenFrames.add(location);
1129
+ frames.push(location);
1130
+ }
1131
+ const frameDisplay = [frames[0]];
1132
+ if (frames.length > 1) {
1133
+ for (let i = 1; i < frames.length - 1; i++) {
1134
+ frameDisplay.push(`${SYMBOLS_ANSI.BRANCH} ${frames[i]}`);
1135
+ }
1136
+ frameDisplay.push(
1137
+ `${SYMBOLS_ANSI.LAST_BRANCH} ${frames[frames.length - 1]}`,
1138
+ );
1139
+ }
1140
+ data.push([
1141
+ comp.group || "",
1142
+ comp.name,
1143
+ comp.version || "",
1144
+ frameDisplay.join("\n"),
1145
+ ]);
1146
+ }
1147
+ const config = {
1148
+ header: {
1149
+ alignment: "center",
1150
+ content:
1151
+ "Component Call Stack Evidence\nGenerated with \u2665 by cdxgen",
1152
+ },
1153
+ };
1154
+ if (data.length > 1) {
1155
+ console.log(table(data, config));
1156
+ }
1157
+ }
1158
+ /**
1159
+ * Prints the dependency tree from the BOM as an ASCII tree diagram.
1160
+ * Uses the `table` library for small trees and plain console output for larger ones.
1161
+ *
1162
+ * @param {Object} bomJson CycloneDX BOM JSON object containing a `dependencies` array
1163
+ * @param {string} [mode="dependsOn"] Dependency relation to traverse (`"dependsOn"` or `"provides"`)
1164
+ * @param {string} [highlight] Optional string to highlight in the tree output
1165
+ * @returns {void}
1166
+ */
1167
+ export function printDependencyTree(
1168
+ bomJson,
1169
+ mode = "dependsOn",
1170
+ highlight = undefined,
1171
+ ) {
1172
+ const dependencies = bomJson.dependencies || [];
1173
+ if (!dependencies.length) {
1174
+ return;
1175
+ }
1176
+ const treeGraphics = buildDependencyTreeLines(dependencies, mode);
1177
+ const legendLines = buildDependencyTreeLegendLines(treeGraphics);
1178
+ // table library is too slow for display large lists.
1179
+ // Fixes #491
1180
+ if (treeGraphics.length && treeGraphics.length < 100) {
1181
+ const treeType =
1182
+ mode && mode === "provides" ? "Crypto Implementation" : "Dependency";
1183
+ const config = {
1184
+ header: {
1185
+ alignment: "center",
1186
+ content: `${treeType} Tree\nGenerated with \u2665 by cdxgen`,
1187
+ },
1188
+ };
1189
+ console.log(
1190
+ table([[highlightStr(treeGraphics.join("\n"), highlight)]], config),
1191
+ );
1192
+ } else if (treeGraphics.length < 500) {
1193
+ // https://github.com/nodejs/node/issues/35973
1194
+ console.log(highlightStr(treeGraphics.join("\n"), highlight));
1195
+ } else {
1196
+ console.log(highlightStr(treeGraphics.slice(0, 500).join("\n"), highlight));
1197
+ }
1198
+ if (legendLines.length) {
1199
+ console.log(legendLines.join("\n"));
1200
+ }
1201
+ }
1202
+
1203
+ const dependencyTreePrefix = (ancestorContinuations, isLast) => {
1204
+ let prefix = "";
1205
+ for (const hasNextSibling of ancestorContinuations) {
1206
+ prefix = `${prefix}${hasNextSibling ? "│ " : " "}`;
1207
+ }
1208
+ return `${prefix}${isLast ? SYMBOLS_ANSI.LAST_BRANCH : SYMBOLS_ANSI.BRANCH}`;
1209
+ };
1210
+
1211
+ const dependencyTreeRefKey = (ref) => ref.toLowerCase();
1212
+
1213
+ const compareDependencyTreeNodes = (a, b) => {
1214
+ if (a.order !== b.order) {
1215
+ return a.order - b.order;
1216
+ }
1217
+ return a.ref.localeCompare(b.ref);
1218
+ };
1219
+
1220
+ const createDependencyTreeGraph = (dependencies, mode) => {
1221
+ const nodes = new Map();
1222
+ let nextOrder = 0;
1223
+
1224
+ const ensureNode = (ref) => {
1225
+ if (!ref) {
1226
+ return undefined;
1227
+ }
1228
+ const refKey = dependencyTreeRefKey(ref);
1229
+ if (!nodes.has(refKey)) {
1230
+ nodes.set(refKey, {
1231
+ childKeys: new Set(),
1232
+ children: [],
1233
+ order: nextOrder,
1234
+ parents: new Set(),
1235
+ ref,
1236
+ });
1237
+ nextOrder += 1;
1238
+ }
1239
+ return nodes.get(refKey);
1240
+ };
1241
+
1242
+ for (const dependency of dependencies) {
1243
+ const rawChildren = Array.isArray(dependency?.[mode])
1244
+ ? dependency[mode].filter(Boolean)
1245
+ : [];
1246
+ const childRefs = Array.from(new Set(rawChildren)).sort((a, b) =>
1247
+ a.localeCompare(b),
1248
+ );
1249
+ let parentNode;
1250
+ if (mode !== "provides" || childRefs.length) {
1251
+ parentNode = ensureNode(dependency.ref);
1252
+ }
1253
+ if (!childRefs.length) {
1254
+ continue;
1255
+ }
1256
+ parentNode = parentNode || ensureNode(dependency.ref);
1257
+ for (const childRef of childRefs) {
1258
+ const childNode = ensureNode(childRef);
1259
+ if (!parentNode || !childNode) {
1260
+ continue;
1261
+ }
1262
+ parentNode.childKeys.add(dependencyTreeRefKey(childRef));
1263
+ childNode.parents.add(dependencyTreeRefKey(parentNode.ref));
1264
+ }
1265
+ }
1266
+
1267
+ for (const node of nodes.values()) {
1268
+ node.children = Array.from(node.childKeys).sort((a, b) =>
1269
+ compareDependencyTreeNodes(nodes.get(a), nodes.get(b)),
1270
+ );
1271
+ }
1272
+
1273
+ return nodes;
1274
+ };
1275
+
1276
+ const renderDependencyTreeNode = (
1277
+ nodes,
1278
+ nodeKey,
1279
+ depth,
1280
+ ancestorContinuations,
1281
+ isLast,
1282
+ renderedNodes,
1283
+ treeGraphics,
1284
+ visitingNodes = new Set(),
1285
+ ) => {
1286
+ const node = nodes.get(nodeKey);
1287
+ if (!node || renderedNodes.has(nodeKey)) {
1288
+ return;
1289
+ }
1290
+ const prefix =
1291
+ depth === 0
1292
+ ? SYMBOLS_ANSI.EMPTY
1293
+ : dependencyTreePrefix(ancestorContinuations, isLast);
1294
+ treeGraphics.push(`${prefix}${node.ref}`);
1295
+ renderedNodes.add(nodeKey);
1296
+ if (depth >= MAX_TREE_DEPTH) {
1297
+ return;
1298
+ }
1299
+ const nextVisitingNodes = new Set(visitingNodes);
1300
+ nextVisitingNodes.add(nodeKey);
1301
+ const nextAncestorContinuations =
1302
+ depth === 0 ? ancestorContinuations : [...ancestorContinuations, !isLast];
1303
+ const childEntries = [];
1304
+ for (const childKey of node.children) {
1305
+ if (nextVisitingNodes.has(childKey)) {
1306
+ childEntries.push({ childKey, isCycle: true });
1307
+ continue;
1308
+ }
1309
+ if (renderedNodes.has(childKey)) {
1310
+ childEntries.push({ childKey, isRepeated: true });
1311
+ continue;
1312
+ }
1313
+ childEntries.push({ childKey, isCycle: false });
1314
+ }
1315
+ for (let i = 0; i < childEntries.length; i++) {
1316
+ const childEntry = childEntries[i];
1317
+ const childNode = nodes.get(childEntry.childKey);
1318
+ const childIsLast = i === childEntries.length - 1;
1319
+ if (!childNode) {
1320
+ continue;
1321
+ }
1322
+ if (childEntry.isCycle) {
1323
+ treeGraphics.push(
1324
+ `${dependencyTreePrefix(nextAncestorContinuations, childIsLast)}${CYCLE_NODE_ICON} ${childNode.ref}`,
1325
+ );
1326
+ continue;
1327
+ }
1328
+ if (childEntry.isRepeated) {
1329
+ treeGraphics.push(
1330
+ `${dependencyTreePrefix(nextAncestorContinuations, childIsLast)}${REPEATED_NODE_ICON} ${childNode.ref}`,
1331
+ );
1332
+ continue;
1333
+ }
1334
+ renderDependencyTreeNode(
1335
+ nodes,
1336
+ childEntry.childKey,
1337
+ depth + 1,
1338
+ nextAncestorContinuations,
1339
+ childIsLast,
1340
+ renderedNodes,
1341
+ treeGraphics,
1342
+ nextVisitingNodes,
1343
+ );
1344
+ }
1345
+ };
1346
+
1347
+ /**
1348
+ * Builds printable dependency tree lines from a BOM dependency graph.
1349
+ * Produces a spanning forest so shared children are rendered once, while
1350
+ * disconnected or cyclic subgraphs are still emitted as dangling trees.
1351
+ *
1352
+ * @param {Object[]} dependencies CycloneDX dependency objects
1353
+ * @param {string} [mode="dependsOn"] Dependency relation to traverse
1354
+ * @returns {string[]} Dependency tree lines ready for console rendering
1355
+ */
1356
+ export const buildDependencyTreeLines = (dependencies, mode = "dependsOn") => {
1357
+ const nodes = createDependencyTreeGraph(dependencies, mode);
1358
+ if (!nodes.size) {
1359
+ return [];
1360
+ }
1361
+ const nodeEntries = Array.from(nodes.entries()).sort(([, a], [, b]) =>
1362
+ compareDependencyTreeNodes(a, b),
1363
+ );
1364
+ const rootKeys = nodeEntries
1365
+ .filter(([, node]) => !node.parents.size)
1366
+ .map(([nodeKey]) => nodeKey);
1367
+ const renderedNodes = new Set();
1368
+ const treeGraphics = [];
1369
+ for (let i = 0; i < rootKeys.length; i++) {
1370
+ renderDependencyTreeNode(
1371
+ nodes,
1372
+ rootKeys[i],
1373
+ 0,
1374
+ [],
1375
+ i === rootKeys.length - 1,
1376
+ renderedNodes,
1377
+ treeGraphics,
1378
+ );
1379
+ }
1380
+ const danglingNodeKeys = nodeEntries
1381
+ .map(([nodeKey]) => nodeKey)
1382
+ .filter((nodeKey) => !renderedNodes.has(nodeKey));
1383
+ for (let i = 0; i < danglingNodeKeys.length; i++) {
1384
+ renderDependencyTreeNode(
1385
+ nodes,
1386
+ danglingNodeKeys[i],
1387
+ 0,
1388
+ [],
1389
+ i === danglingNodeKeys.length - 1,
1390
+ renderedNodes,
1391
+ treeGraphics,
1392
+ );
1393
+ }
1394
+ return treeGraphics;
1395
+ };
1396
+
1397
+ /**
1398
+ * Prints a table of reachable components derived from a reachability slices file.
1399
+ * Aggregates per-purl reachable-flow counts and sorts them descending.
1400
+ *
1401
+ * @param {Object} sliceArtefacts Slice artefact paths, must include `reachablesSlicesFile`
1402
+ * @returns {void}
1403
+ */
1404
+ export function printReachables(sliceArtefacts) {
1405
+ const reachablesSlicesFile = sliceArtefacts.reachablesSlicesFile;
1406
+ if (!reachablesSlicesFile || !safeExistsSync(reachablesSlicesFile)) {
1407
+ return;
1408
+ }
1409
+ const purlCounts = {};
1410
+ const reachablesSlices = JSON.parse(
1411
+ readFileSync(reachablesSlicesFile, "utf-8"),
1412
+ );
1413
+ const rflows = Array.isArray(reachablesSlices)
1414
+ ? reachablesSlices
1415
+ : reachablesSlices.reachables || [];
1416
+ for (const areachable of rflows) {
1417
+ const purls = areachable.purls || [];
1418
+ for (const apurl of purls) {
1419
+ purlCounts[apurl] = (purlCounts[apurl] || 0) + 1;
1420
+ }
1421
+ }
1422
+ const sortedPurls = Object.fromEntries(
1423
+ Object.entries(purlCounts).sort(([, a], [, b]) => b - a),
1424
+ );
1425
+ const data = [["Package URL", "Reachable Flows"]];
1426
+ for (const apurl of Object.keys(sortedPurls)) {
1427
+ data.push([apurl, `${sortedPurls[apurl]}`]);
1428
+ }
1429
+ const config = {
1430
+ header: {
1431
+ alignment: "center",
1432
+ content: "Reachable Components\nGenerated with \u2665 by cdxgen",
1433
+ },
1434
+ };
1435
+ if (data.length > 1) {
1436
+ console.log(table(data, config));
1437
+ }
1438
+ }
1439
+
1440
+ /**
1441
+ * Prints a formatted table of CycloneDX vulnerability objects.
1442
+ *
1443
+ * @param {Object[]} vulnerabilities Array of CycloneDX vulnerability objects
1444
+ * @returns {void}
1445
+ */
1446
+ export function printVulnerabilities(vulnerabilities) {
1447
+ if (!vulnerabilities) {
1448
+ return;
1449
+ }
1450
+ const data = [["Ref", "Ratings", "State", "Justification"]];
1451
+ for (const avuln of vulnerabilities) {
1452
+ const arow = [
1453
+ avuln["bom-ref"],
1454
+ `${avuln?.ratings
1455
+ .map((r) => r?.severity?.toUpperCase())
1456
+ .join("\n")}\n${avuln?.ratings.map((r) => r?.score).join("\n")}`,
1457
+ avuln?.analysis?.state || "",
1458
+ avuln?.analysis?.justification || "",
1459
+ ];
1460
+ data.push(arow);
1461
+ }
1462
+ const config = {
1463
+ header: {
1464
+ alignment: "center",
1465
+ content: "Vulnerabilities\nGenerated with \u2665 by cdxgen",
1466
+ },
1467
+ };
1468
+ if (data.length > 1) {
1469
+ console.log(table(data, config));
1470
+ }
1471
+ console.log(`${vulnerabilities.length} vulnerabilities found.`);
1472
+ }
1473
+
1474
+ /**
1475
+ * Prints an OWASP donation banner when running in a CI environment.
1476
+ * The banner is suppressed when `options.noBanner` is set or the repository
1477
+ * belongs to the cdxgen project itself.
1478
+ *
1479
+ * @param {Object} options CLI options
1480
+ * @returns {void}
1481
+ */
1482
+ export function printSponsorBanner(options) {
1483
+ if (
1484
+ process?.env?.CI &&
1485
+ !options.noBanner &&
1486
+ !readEnvironmentVariable("GITHUB_REPOSITORY")
1487
+ ?.toLowerCase()
1488
+ .startsWith("cdxgen")
1489
+ ) {
1490
+ const config = {
1491
+ header: {
1492
+ alignment: "center",
1493
+ content: "\u00A4 Donate to the OWASP Foundation",
1494
+ },
1495
+ };
1496
+ let message =
1497
+ "OWASP foundation relies on donations to fund our projects.\nDonation link: https://owasp.org/donate/?reponame=www-project-cdxgen&title=OWASP+cdxgen";
1498
+ if (options.serverUrl && options.apiKey) {
1499
+ message = `${message}\nDependency Track: https://owasp.org/donate/?reponame=www-project-dependency-track&title=OWASP+Dependency-Track`;
1500
+ }
1501
+ const data = [[message]];
1502
+ console.log(table(data, config));
1503
+ }
1504
+ }
1505
+
1506
+ /**
1507
+ * Prints a BOM summary table including generator tool names, component package types,
1508
+ * and component namespaces extracted from BOM metadata properties.
1509
+ *
1510
+ * @param {Object} bomJson CycloneDX BOM JSON object
1511
+ * @returns {void}
1512
+ */
1513
+ export function printSummary(bomJson) {
1514
+ const config = {
1515
+ header: {
1516
+ alignment: "center",
1517
+ content: "BOM summary",
1518
+ },
1519
+ columns: [{ wrapWord: true, width: 100 }],
1520
+ };
1521
+ const metadataProperties = bomJson?.metadata?.properties;
1522
+ if (!metadataProperties) {
1523
+ return;
1524
+ }
1525
+ let message = "";
1526
+ let bomPkgTypes = [];
1527
+ let bomPkgNamespaces = [];
1528
+ // Print any annotations found
1529
+ const annotations = bomJson?.annotations || [];
1530
+ if (annotations.length) {
1531
+ for (const annot of annotations) {
1532
+ message = `${message}\n${annot.text}`;
1533
+ }
1534
+ }
1535
+ const tools = bomJson?.metadata?.tools?.components;
1536
+ if (tools) {
1537
+ message = `${message}\n\n** Generator Tools **`;
1538
+ for (const atool of tools) {
1539
+ if (atool.name && atool.version) {
1540
+ message = `${message}\n${atool.name} (${atool.version})`;
1541
+ }
1542
+ }
1543
+ }
1544
+ for (const aprop of metadataProperties) {
1545
+ if (aprop.name === "cdx:bom:componentTypes") {
1546
+ bomPkgTypes = aprop?.value.split("\\n");
1547
+ }
1548
+ if (aprop.name === "cdx:bom:componentNamespaces") {
1549
+ bomPkgNamespaces = aprop?.value.split("\\n");
1550
+ }
1551
+ }
1552
+ if (!bomPkgTypes.length && !bomPkgNamespaces.length) {
1553
+ return;
1554
+ }
1555
+ message = `${message}\n\n** Package Types (${bomPkgTypes.length}) **\n${bomPkgTypes.join("\n")}`;
1556
+ if (bomPkgNamespaces.length) {
1557
+ message = `${message}\n\n** Namespaces (${bomPkgNamespaces.length}) **\n${bomPkgNamespaces.join("\n")}`;
1558
+ }
1559
+ const aiInsightLines = buildAiBomInsightLines(bomJson);
1560
+ if (aiInsightLines.length) {
1561
+ message = `${message}\n\n** AI-BOM **\n${aiInsightLines.join("\n")}`;
1562
+ }
1563
+ const data = [[message]];
1564
+ console.log(table(data, config));
1565
+ }
1566
+
1567
+ /**
1568
+ * Print the recorded activity summary to the console.
1569
+ *
1570
+ * For "json"/"jsonl" report types the serialized lines are printed verbatim;
1571
+ * otherwise a human-readable console summary with status counts is shown.
1572
+ *
1573
+ * @param {string} [reportType] Report format: "json", "jsonl", or undefined for console output
1574
+ * @returns {void}
1575
+ */
1576
+ export function printActivitySummary(reportType = undefined) {
1577
+ const activities = getRecordedActivities();
1578
+ if (!activities.length) {
1579
+ return;
1580
+ }
1581
+ const activitySummaryPayload = buildActivitySummaryPayload(activities);
1582
+ const completedCount = activitySummaryPayload.summary.completed;
1583
+ const blockedCount = activitySummaryPayload.summary.blocked;
1584
+ const failedCount = activitySummaryPayload.summary.failed;
1585
+ const formatStatus = (status) => {
1586
+ if (status === "completed") {
1587
+ return "completed";
1588
+ }
1589
+ if (status === "blocked") {
1590
+ return "blocked";
1591
+ }
1592
+ if (status === "failed") {
1593
+ return "failed";
1594
+ }
1595
+ return status || "";
1596
+ };
1597
+ if (reportType === "json") {
1598
+ for (const line of serializeActivitySummary(activities, reportType)) {
1599
+ console.log(line);
1600
+ }
1601
+ return;
1602
+ }
1603
+ if (reportType === "jsonl") {
1604
+ for (const line of serializeActivitySummary(activities, reportType)) {
1605
+ console.log(line);
1606
+ }
1607
+ return;
1608
+ }
1609
+ const formatActivityTarget = (activity) => {
1610
+ const target = activity?.target;
1611
+ const suspiciousPrefix =
1612
+ activity?.risk === "shell-metacharacters"
1613
+ ? `${SUSPICIOUS_SHELL_PATH_LABEL}\n`
1614
+ : "";
1615
+ if (typeof target !== "string" || !target.includes(",")) {
1616
+ return `${suspiciousPrefix}${target || ""}`;
1617
+ }
1618
+ const targetEntries = splitCommaSeparatedActivityEntries(target);
1619
+ if (isLikelyActivityPathList(targetEntries)) {
1620
+ return `${suspiciousPrefix}${sortActivityTargetEntries(targetEntries).join("\n")}`;
1621
+ }
1622
+ if (!(target.includes(":") || target.includes("="))) {
1623
+ return `${suspiciousPrefix}${target || ""}`;
1624
+ }
1625
+ const targetSegments = target.split(/,\s*(?=[A-Za-z][\w-]*\s*[:=])/);
1626
+ let didFormat = false;
1627
+ const renderedSegments = targetSegments.map((segment) => {
1628
+ const segmentMatch = segment.match(/^([A-Za-z][\w-]*)\s*([:=])\s*(.*)$/);
1629
+ if (!segmentMatch) {
1630
+ return segment;
1631
+ }
1632
+ const [, key, separator, value] = segmentMatch;
1633
+ if (!MULTIVALUE_ACTIVITY_TARGET_KEYS.has(key) || !value.includes(",")) {
1634
+ return segment;
1635
+ }
1636
+ didFormat = true;
1637
+ return `${key}${separator}\n${sortActivityTargetEntries(
1638
+ splitCommaSeparatedActivityEntries(value),
1639
+ )
1640
+ .map((entry) => `- ${entry}`)
1641
+ .join("\n")}`;
1642
+ });
1643
+ return `${suspiciousPrefix}${didFormat ? renderedSegments.join("\n") : target}`;
1644
+ };
1645
+ const formatActivityType = (type) => {
1646
+ if (typeof type !== "string" || !type.includes(",")) {
1647
+ return type || "";
1648
+ }
1649
+ return splitCommaSeparatedActivityEntries(type)
1650
+ .sort((left, right) => left.localeCompare(right))
1651
+ .join("\n");
1652
+ };
1653
+ const data = [
1654
+ [
1655
+ "Identifier",
1656
+ "Type",
1657
+ "Package Type",
1658
+ "Activity",
1659
+ "Target",
1660
+ "Outcome / Why",
1661
+ ],
1662
+ ];
1663
+ for (const activity of activities) {
1664
+ data.push([
1665
+ activity.identifier,
1666
+ formatActivityType(activity.projectType),
1667
+ activity.packageType || "",
1668
+ activity.kind || "",
1669
+ formatActivityTarget(activity),
1670
+ activity.reason
1671
+ ? `${formatStatus(activity.status)}\n${activity.reason}`.trim()
1672
+ : formatStatus(activity.status),
1673
+ ]);
1674
+ }
1675
+ const config = {
1676
+ header: {
1677
+ alignment: "center",
1678
+ content: `${
1679
+ isDryRun
1680
+ ? "cdxgen dry-run activity summary"
1681
+ : "cdxgen debug activity summary"
1682
+ }\n${completedCount} completed ${blockedCount} blocked ${failedCount} failed`,
1683
+ },
1684
+ columns: [
1685
+ { width: 14 },
1686
+ { width: 14 },
1687
+ { width: 14 },
1688
+ { width: 12 },
1689
+ { width: 48, wrapWord: true },
1690
+ { width: 28, wrapWord: true },
1691
+ ],
1692
+ };
1693
+ console.log(table(data, config));
1694
+ }
1695
+
1696
+ /**
1697
+ * @typedef {{type: string, variable: string, severity: string, message: string, mitigation: string}} EnvAuditFinding
1698
+ */
1699
+
1700
+ const summarizeEnvAuditSeverities = (envAuditFindings) => {
1701
+ const counts = { critical: 0, high: 0, medium: 0, low: 0 };
1702
+ for (const finding of envAuditFindings) {
1703
+ if (counts[finding.severity] !== undefined) {
1704
+ counts[finding.severity] += 1;
1705
+ }
1706
+ }
1707
+ return ["critical", "high", "medium", "low"]
1708
+ .filter((severity) => counts[severity] > 0)
1709
+ .map((severity) => `${counts[severity]} ${severity}`)
1710
+ .join(" ");
1711
+ };
1712
+
1713
+ const buildEnvironmentAuditGroups = (envAuditFindings) => {
1714
+ const groups = new Map();
1715
+ for (const finding of envAuditFindings) {
1716
+ const isCredentialExposure = finding.type === "credential-exposure";
1717
+ const groupKey = isCredentialExposure
1718
+ ? "credential-exposure"
1719
+ : JSON.stringify([
1720
+ finding.type,
1721
+ finding.severity,
1722
+ finding.message,
1723
+ finding.mitigation,
1724
+ ]);
1725
+ if (!groups.has(groupKey)) {
1726
+ groups.set(groupKey, {
1727
+ details: isCredentialExposure
1728
+ ? "Credential-like environment variables are set. Build tools or install scripts invoked during SBOM generation may read inherited environment variables."
1729
+ : finding.message,
1730
+ mitigation: isCredentialExposure
1731
+ ? "Unset unneeded secrets when scanning untrusted repositories. Prefer ephemeral, scoped CI credentials injected only for the step that needs them."
1732
+ : finding.mitigation,
1733
+ severity: finding.severity,
1734
+ title:
1735
+ ENV_AUDIT_TYPE_LABELS[finding.type] ||
1736
+ toCamel(finding.type || "Finding"),
1737
+ variables: new Set(),
1738
+ });
1739
+ }
1740
+ groups.get(groupKey).variables.add(finding.variable);
1741
+ }
1742
+ return [...groups.values()]
1743
+ .map((group) => ({
1744
+ ...group,
1745
+ variables: [...group.variables].filter(Boolean).sort(),
1746
+ }))
1747
+ .sort((left, right) => {
1748
+ const severityDiff =
1749
+ (ENV_AUDIT_SEVERITY_RANK[right.severity] || 0) -
1750
+ (ENV_AUDIT_SEVERITY_RANK[left.severity] || 0);
1751
+ if (severityDiff !== 0) {
1752
+ return severityDiff;
1753
+ }
1754
+ return left.title.localeCompare(right.title);
1755
+ });
1756
+ };
1757
+
1758
+ /**
1759
+ * Prints a grouped secure-mode environment audit call-out panel.
1760
+ *
1761
+ * @param {EnvAuditFinding[]} envAuditFindings Audit findings to display
1762
+ * @returns {void}
1763
+ */
1764
+ export function printEnvironmentAuditFindings(envAuditFindings = []) {
1765
+ if (!envAuditFindings.length) {
1766
+ return;
1767
+ }
1768
+ const groupedFindings = buildEnvironmentAuditGroups(envAuditFindings);
1769
+ const severitySummary = summarizeEnvAuditSeverities(envAuditFindings);
1770
+ const data = [["Category", "Severity", "Variable(s)", "Details"]];
1771
+ for (const finding of groupedFindings) {
1772
+ data.push([
1773
+ finding.title,
1774
+ finding.severity.toUpperCase(),
1775
+ finding.variables.join("\n"),
1776
+ `${finding.details}\nMitigation: ${finding.mitigation}`,
1777
+ ]);
1778
+ }
1779
+ const config = {
1780
+ header: {
1781
+ alignment: "center",
1782
+ content: `SECURE MODE: Environment audit\n${severitySummary || `${envAuditFindings.length} finding(s)`}`,
1783
+ },
1784
+ columns: [
1785
+ { width: 22 },
1786
+ { width: 10 },
1787
+ { width: 24, wrapWord: true },
1788
+ { width: 50, wrapWord: true },
1789
+ ],
1790
+ columnDefault: { wrapWord: true },
1791
+ };
1792
+ console.log(table(data, config));
1793
+ }
1794
+
1795
+ /**
1796
+ * Runs the pre-generation environment audit and renders the results as formatted
1797
+ * tables to the console. Called when the --env-audit CLI flag is set.
1798
+ *
1799
+ * @param {string} filePath Project path being scanned
1800
+ * @param {Object} config Loaded .cdxgenrc / config-file values
1801
+ * @param {Object} options Effective CLI options
1802
+ * @param {EnvAuditFinding[]} envAuditFindings Audit findings to display
1803
+ */
1804
+ export function displaySelfThreatModel(
1805
+ filePath,
1806
+ config,
1807
+ options,
1808
+ envAuditFindings,
1809
+ ) {
1810
+ const TLP = options.tlpClassification;
1811
+ const risks = [];
1812
+ let riskScore = 0;
1813
+
1814
+ const addRisk = (level, reason, category = "configuration") => {
1815
+ const scores = { low: 1, medium: 3, high: 5, critical: 8 };
1816
+ riskScore = Math.min(10, riskScore + scores[level]);
1817
+ risks.push({ level, reason, category });
1818
+ };
1819
+
1820
+ // Config file risks
1821
+ if (Object.keys(config).length > 0) {
1822
+ addRisk(
1823
+ "medium",
1824
+ "A .cdxgenrc config file was loaded from the working directory. It may override security-relevant settings without being visible on the command line.",
1825
+ "configuration",
1826
+ );
1827
+ const sensitive = ["server-url", "api-key", "include-formulation"];
1828
+ for (const key of sensitive) {
1829
+ if (config[key] || config[toCamel(key)]) {
1830
+ addRisk(
1831
+ key === "api-key" ? "high" : "medium",
1832
+ `Config file sets '${key}', which affects SBOM content or remote submission behavior.`,
1833
+ "configuration",
1834
+ );
1835
+ }
1836
+ }
1837
+ }
1838
+
1839
+ // Remote submission risks
1840
+ if (options.serverUrl) {
1841
+ const isHttps = options.serverUrl.startsWith("https://");
1842
+ addRisk(
1843
+ isHttps ? "medium" : "critical",
1844
+ `SBOM will be submitted to ${options.serverUrl}${!isHttps ? " over plain HTTP — contents may be intercepted or tampered in transit." : "."}`,
1845
+ "network",
1846
+ );
1847
+ if (options.skipDtTlsCheck) {
1848
+ addRisk(
1849
+ "high",
1850
+ "TLS certificate validation is disabled for Dependency-Track uploads. SBOM contents may be intercepted or tampered in transit.",
1851
+ "network",
1852
+ );
1853
+ }
1854
+ }
1855
+
1856
+ // Data exposure risks
1857
+ if (options.includeFormulation) {
1858
+ addRisk(
1859
+ "medium",
1860
+ "Formulation mode is active. The SBOM will include build metadata such as git history, committer identities, and CI environment variables.",
1861
+ "data-exposure",
1862
+ );
1863
+ }
1864
+ if (options.evidence || options.deep) {
1865
+ addRisk(
1866
+ "medium",
1867
+ "Evidence / deep mode will invoke build tools and parse source files to collect call graph and reachability evidence. Malicious build scripts may execute.",
1868
+ "data-exposure",
1869
+ );
1870
+ }
1871
+ if (options.installDeps) {
1872
+ addRisk(
1873
+ "high",
1874
+ "Dependency auto-install is enabled. Lifecycle hooks (install scripts) from third-party packages will execute in the current environment.",
1875
+ "data-exposure",
1876
+ );
1877
+ }
1878
+
1879
+ // Output path outside the project directory
1880
+ if (options.output) {
1881
+ const resolvedOutput = path.resolve(options.output);
1882
+ const resolvedProject = path.resolve(filePath);
1883
+ if (
1884
+ !resolvedOutput.startsWith(resolvedProject + path.sep) &&
1885
+ resolvedOutput !== resolvedProject
1886
+ ) {
1887
+ addRisk(
1888
+ "medium",
1889
+ `Output path '${options.output}' resolves to '${resolvedOutput}', which is outside the project directory '${resolvedProject}'. Ensure this is intentional.`,
1890
+ "configuration",
1891
+ );
1892
+ }
1893
+ }
1894
+
1895
+ // Environment variable risks (config-layer only; env-audit covers the rest)
1896
+ if (readEnvironmentVariable("CDXGEN_SERVER_URL")) {
1897
+ addRisk(
1898
+ "low",
1899
+ "CDXGEN_SERVER_URL is set in the environment and will override any --server-url value.",
1900
+ "environment",
1901
+ );
1902
+ }
1903
+
1904
+ // Integrate environment audit findings
1905
+ if (envAuditFindings?.length) {
1906
+ for (const f of envAuditFindings) {
1907
+ const categoryMap = {
1908
+ "code-execution": "runtime",
1909
+ "debug-exposure": "runtime",
1910
+ "environment-variable": "environment",
1911
+ "network-interception": "network",
1912
+ "credential-exposure": "environment",
1913
+ "permission-misuse": "runtime",
1914
+ privilege: "runtime",
1915
+ };
1916
+ addRisk(
1917
+ f.severity,
1918
+ `${f.variable}: ${f.message}`,
1919
+ categoryMap[f.type] || "configuration",
1920
+ );
1921
+ }
1922
+ }
1923
+
1924
+ const nodeOptions = readEnvironmentVariable("NODE_OPTIONS") || "";
1925
+ const riskLevel =
1926
+ riskScore >= 8
1927
+ ? "CRITICAL"
1928
+ : riskScore >= 5
1929
+ ? "HIGH"
1930
+ : riskScore >= 3
1931
+ ? "MEDIUM"
1932
+ : "LOW";
1933
+
1934
+ const riskColor = {
1935
+ CRITICAL: "\x1b[1;31m",
1936
+ HIGH: "\x1b[1;33m",
1937
+ MEDIUM: "\x1b[1;36m",
1938
+ LOW: "\x1b[1;32m",
1939
+ };
1940
+ const reset = "\x1b[0m";
1941
+ const tlpGuidance = {
1942
+ CLEAR: "May be shared publicly. No restrictions.",
1943
+ GREEN: "Limited to community/peers. Not for public posting.",
1944
+ AMBER:
1945
+ "Limited to organisation and trusted partners. Handle-in-confidence.",
1946
+ AMBER_AND_STRICT: "Organisation only. No external sharing.",
1947
+ RED: "Named recipients only. Do not forward or store beyond session.",
1948
+ };
1949
+ const tlpValue = TLP
1950
+ ? `${TLP} — ${tlpGuidance[TLP]}`
1951
+ : "Not set — no distribution constraints recorded.";
1952
+ const headerData = [
1953
+ ["TLP Classification", tlpValue],
1954
+ ["Risk Score", `${riskScore}/10`],
1955
+ ["Risk Level", `${riskColor[riskLevel]}${riskLevel}${reset}`],
1956
+ ];
1957
+ const headerConfig = {
1958
+ header: {
1959
+ alignment: "center",
1960
+ content:
1961
+ "SBOM Generation Environment Assessment\nPre-generation security audit by cdxgen",
1962
+ },
1963
+ columns: [{ width: 30, alignment: "right" }, { width: 70 }],
1964
+ columnDefault: { wrapWord: true },
1965
+ };
1966
+
1967
+ console.log(table(headerData, headerConfig));
1968
+ if (risks.length > 0) {
1969
+ const findingsData = [["#", "Severity", "Category", "Finding"]];
1970
+ risks.forEach(({ level, reason, category }, i) => {
1971
+ const severityColor =
1972
+ level === "critical"
1973
+ ? "\x1b[1;31m"
1974
+ : level === "high"
1975
+ ? "\x1b[1;33m"
1976
+ : level === "medium"
1977
+ ? "\x1b[1;36m"
1978
+ : "\x1b[1;32m";
1979
+ findingsData.push([
1980
+ `${i + 1}`,
1981
+ `${severityColor}${level.toUpperCase()}${reset}`,
1982
+ category,
1983
+ reason,
1984
+ ]);
1985
+ });
1986
+ const findingsConfig = {
1987
+ header: {
1988
+ alignment: "center",
1989
+ content: `Findings (${risks.length})`,
1990
+ },
1991
+ columns: [
1992
+ { width: 5, alignment: "right" },
1993
+ { width: 12 },
1994
+ { width: 17 },
1995
+ { width: 66 },
1996
+ ],
1997
+ columnDefault: { wrapWord: true },
1998
+ };
1999
+ console.log(table(findingsData, findingsConfig));
2000
+ } else {
2001
+ const noFindingsData = [
2002
+ [
2003
+ `${riskColor[riskLevel]}✅ No risks detected in the current configuration.${reset}`,
2004
+ ],
2005
+ ];
2006
+ const noFindingsConfig = {
2007
+ header: { alignment: "center", content: "📋 Findings" },
2008
+ columns: [{ width: 100, alignment: "center" }],
2009
+ };
2010
+ console.log(table(noFindingsData, noFindingsConfig));
2011
+ }
2012
+
2013
+ const configData = [
2014
+ ["Setting", "Value"],
2015
+ ["Project", options.projectName || filePath],
2016
+ ["Type(s)", options.projectType?.join(", ") || "auto-detect"],
2017
+ ["Profile", options.profile || "generic"],
2018
+ ["Path", filePath],
2019
+ ["Output", options.output || "(stdout)"],
2020
+ ["Recursive", options.recursive ? "yes" : "no"],
2021
+ ["Remote Submission", options.serverUrl || "none"],
2022
+ ["Formulation", options.includeFormulation ? "yes" : "no"],
2023
+ ["Evidence / Deep Mode", options.evidence || options.deep ? "yes" : "no"],
2024
+ ["Auto-install Dependencies", options.installDeps ? "yes" : "no"],
2025
+ ["NODE_OPTIONS", nodeOptions || "(not set)"],
2026
+ ];
2027
+ const effConfigTableConfig = {
2028
+ header: { alignment: "center", content: "Effective Configuration" },
2029
+ columns: [{ width: 28 }, { width: 72 }],
2030
+ columnDefault: { wrapWord: true },
2031
+ };
2032
+ console.log(table(configData, effConfigTableConfig));
2033
+
2034
+ const recommendations = [];
2035
+ if (["AMBER", "AMBER_AND_STRICT", "RED"].includes(TLP)) {
2036
+ recommendations.push([
2037
+ "High",
2038
+ "Omit --include-formulation to avoid embedding committer identities and CI secrets in the SBOM.",
2039
+ ]);
2040
+ if (TLP === "RED") {
2041
+ recommendations.push([
2042
+ "Critical",
2043
+ "Run cdxgen inside an isolated container or VM with no access to production credentials.",
2044
+ ]);
2045
+ recommendations.push([
2046
+ "Critical",
2047
+ "Do not set --server-url; review and handle the output SBOM manually before sharing.",
2048
+ ]);
2049
+ }
2050
+ }
2051
+ if (riskScore >= 5) {
2052
+ recommendations.push([
2053
+ "High",
2054
+ "Address the findings above before scanning untrusted repositories.",
2055
+ ]);
2056
+ recommendations.push([
2057
+ "Medium",
2058
+ "Pass --no-install-deps to prevent package manager hooks from executing.",
2059
+ ]);
2060
+ }
2061
+ if (envAuditFindings.some((f) => f.type === "code-execution")) {
2062
+ recommendations.push([
2063
+ "High",
2064
+ "Remove code-execution flags (--require, --eval, --loader, --import) from NODE_OPTIONS and JAVA_TOOL_OPTIONS.",
2065
+ ]);
2066
+ }
2067
+ if (envAuditFindings.some((f) => f.variable === "NODE_PATH")) {
2068
+ recommendations.push([
2069
+ "High",
2070
+ "Unset NODE_PATH to prevent module-resolution poisoning by malicious packages.",
2071
+ ]);
2072
+ }
2073
+ if (envAuditFindings.some((f) => f.type === "privilege")) {
2074
+ recommendations.push([
2075
+ "High",
2076
+ "Do not run cdxgen as root. Create a dedicated low-privilege user or use a rootless container.",
2077
+ ]);
2078
+ }
2079
+ if (/--permission\b/i.test(nodeOptions)) {
2080
+ recommendations.push([
2081
+ "Medium",
2082
+ "Audit every --allow-* scope; use absolute paths rather than wildcards to minimise the permission surface.",
2083
+ ]);
2084
+ }
2085
+ recommendations.push([
2086
+ "Info",
2087
+ "Minimal safe invocation: cdxgen --no-install-deps --output ./sbom.cdx.json <path>",
2088
+ ]);
2089
+ const recommendationsData = [["Priority", "Action"]];
2090
+ recommendations.forEach(([priority, action]) => {
2091
+ const priorityColor =
2092
+ priority === "Critical"
2093
+ ? "\x1b[1;31m"
2094
+ : priority === "High"
2095
+ ? "\x1b[1;33m"
2096
+ : priority === "Medium"
2097
+ ? "\x1b[1;36m"
2098
+ : "\x1b[1;32m";
2099
+ recommendationsData.push([`${priorityColor}${priority}${reset}`, action]);
2100
+ });
2101
+ const recommendationsConfig = {
2102
+ header: {
2103
+ alignment: "center",
2104
+ content: `Recommendations for TLP:${TLP}`,
2105
+ },
2106
+ columns: [{ width: 12 }, { width: 88 }],
2107
+ columnDefault: { wrapWord: true },
2108
+ };
2109
+
2110
+ console.log(table(recommendationsData, recommendationsConfig));
2111
+ // Only abort in secure mode when at least one finding is high or critical severity.
2112
+ // Accumulated low/medium findings may push riskScore above 5 but should not abort.
2113
+ if (
2114
+ isSecureMode &&
2115
+ envAuditFindings?.some((f) => ["high", "critical"].includes(f.severity))
2116
+ ) {
2117
+ const abortData = [
2118
+ [
2119
+ `${riskColor[riskLevel]}🚫 SECURE MODE: High-risk configuration detected. Aborting SBOM generation.${reset}`,
2120
+ ],
2121
+ ];
2122
+ const abortConfig = {
2123
+ columns: [{ width: 100, alignment: "center" }],
2124
+ };
2125
+ console.log(table(abortData, abortConfig));
2126
+ process.exit(1);
2127
+ }
2128
+ }