@cdxgen/cdxgen 0.0.1 → 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (722) hide show
  1. package/README.md +921 -87
  2. package/bin/audit.js +239 -0
  3. package/bin/cdxgen.js +2324 -0
  4. package/bin/convert.js +213 -0
  5. package/bin/evinse.js +353 -0
  6. package/bin/hbom.js +495 -0
  7. package/bin/repl.js +2224 -0
  8. package/bin/sign.js +195 -0
  9. package/bin/tracebom.js +295 -0
  10. package/bin/validate.js +268 -0
  11. package/bin/verify.js +189 -0
  12. package/data/README.md +307 -0
  13. package/data/bom-1.4.schema.json +1605 -0
  14. package/data/bom-1.5.schema.json +3660 -0
  15. package/data/bom-1.6.schema.json +5511 -0
  16. package/data/bom-1.7.schema.json +6700 -0
  17. package/data/cbomosdb-queries.json +68 -0
  18. package/data/component-tags.json +339 -0
  19. package/data/container-knowledge-index.json +125 -0
  20. package/data/cosdb-queries.json +17 -0
  21. package/data/crypto-oid.json +1998 -0
  22. package/data/cryptography-defs.json +3793 -0
  23. package/data/cryptography-defs.schema.json +576 -0
  24. package/data/cyclonedx-2.0-bundled.schema.json +7182 -0
  25. package/data/frameworks-list.json +219 -0
  26. package/data/glibc-stdlib.json +117 -0
  27. package/data/gtfobins-index.json +6296 -0
  28. package/data/helpers/init.gradle +71 -0
  29. package/data/jsf-0.82.schema.json +211 -0
  30. package/data/known-licenses.json +82 -0
  31. package/data/lic-mapping.json +338 -0
  32. package/data/license-aliases.json +6624 -0
  33. package/data/license-db.json +39059 -0
  34. package/data/license-deprecations.json +302 -0
  35. package/data/lolbas-index.json +150 -0
  36. package/data/predictive-audit-allowlist.json +11 -0
  37. package/data/pypi-pkg-aliases.json +1199 -0
  38. package/data/python-stdlib.json +308 -0
  39. package/data/queries-darwin.json +193 -0
  40. package/data/queries-win.json +257 -0
  41. package/data/queries.json +268 -0
  42. package/data/ruby-known-modules.json +281 -0
  43. package/data/rules/ai-agent-governance.yaml +323 -0
  44. package/data/rules/ai-bom.yaml +268 -0
  45. package/data/rules/ai-oversight.yaml +186 -0
  46. package/data/rules/ai-provenance.yaml +92 -0
  47. package/data/rules/asar-archives.yaml +150 -0
  48. package/data/rules/cbom-security.yaml +165 -0
  49. package/data/rules/chrome-extensions.yaml +248 -0
  50. package/data/rules/ci-permissions.yaml +802 -0
  51. package/data/rules/container-risk.yaml +353 -0
  52. package/data/rules/dependency-sources.yaml +236 -0
  53. package/data/rules/golem-go.yaml +229 -0
  54. package/data/rules/hbom-compliance.yaml +325 -0
  55. package/data/rules/hbom-performance.yaml +307 -0
  56. package/data/rules/hbom-security.yaml +248 -0
  57. package/data/rules/host-topology.yaml +165 -0
  58. package/data/rules/mcp-servers.yaml +388 -0
  59. package/data/rules/obom-runtime.yaml +1776 -0
  60. package/data/rules/package-integrity.yaml +364 -0
  61. package/data/rules/rootfs-hardening.yaml +179 -0
  62. package/data/rules/vscode-extensions.yaml +237 -0
  63. package/data/spdx-export.schema.json +6794 -0
  64. package/data/spdx-license-list.json +7449 -0
  65. package/data/spdx-licenses.json +759 -0
  66. package/data/spdx.schema.json +819 -0
  67. package/data/templates/README.md +3 -0
  68. package/data/templates/asvs-4.0.3.cdx.json +2943 -0
  69. package/data/templates/asvs-5.0.cdx.json +3046 -0
  70. package/data/templates/bsimm-v13.cdx.json +1008 -0
  71. package/data/templates/masvs-2.0.0.cdx.json +225 -0
  72. package/data/templates/nist-ssdf-1.1.cdx.json +828 -0
  73. package/data/templates/pcissc-secure-slc-1.1.cdx.json +1027 -0
  74. package/data/templates/scvs-1.0.0.cdx.json +813 -0
  75. package/data/templates/ssaf-DRAFT-2023-11.cdx.json +127 -0
  76. package/data/vendor-alias.json +41967 -0
  77. package/data/wrapdb-releases.json +4227 -0
  78. package/index.cjs +15 -0
  79. package/lib/audit/index.js +2800 -0
  80. package/lib/audit/progress.js +114 -0
  81. package/lib/audit/reporters.js +937 -0
  82. package/lib/audit/scoring.js +560 -0
  83. package/lib/audit/targets.js +798 -0
  84. package/lib/cli/bomAssembly.js +1833 -0
  85. package/lib/cli/cliOptions.js +513 -0
  86. package/lib/cli/index.js +2759 -0
  87. package/lib/cli/jsBom.js +2087 -0
  88. package/lib/cli/jvmBom.js +1858 -0
  89. package/lib/cli/managedBom.js +2083 -0
  90. package/lib/cli/nativeBom.js +2125 -0
  91. package/lib/core/activity.js +1087 -0
  92. package/lib/core/env.js +719 -0
  93. package/lib/core/fs.js +1058 -0
  94. package/lib/core/httpClient.js +730 -0
  95. package/lib/core/logger.js +142 -0
  96. package/lib/core/parallel.js +323 -0
  97. package/lib/core/paths.js +70 -0
  98. package/lib/core/projectConfig.js +122 -0
  99. package/lib/core/propertySanitizer.js +174 -0
  100. package/lib/core/spdx.js +5 -0
  101. package/lib/core/state.js +59 -0
  102. package/lib/core/text.js +207 -0
  103. package/lib/core/ui.js +876 -0
  104. package/lib/core/urls.js +84 -0
  105. package/lib/ecosystems/asarutils.js +1636 -0
  106. package/lib/ecosystems/bunutils.js +402 -0
  107. package/lib/ecosystems/caxa.js +77 -0
  108. package/lib/ecosystems/chromextutils.js +1174 -0
  109. package/lib/ecosystems/cmakeResolver.js +587 -0
  110. package/lib/ecosystems/cppEvidence.js +305 -0
  111. package/lib/ecosystems/denoutils.js +1094 -0
  112. package/lib/ecosystems/dotnetutils.js +132 -0
  113. package/lib/ecosystems/ecosystems.js +2772 -0
  114. package/lib/ecosystems/golem.js +1807 -0
  115. package/lib/ecosystems/gradleutils.js +1048 -0
  116. package/lib/ecosystems/jsEvidence.js +317 -0
  117. package/lib/ecosystems/npmutils.js +586 -0
  118. package/lib/ecosystems/parsers-bazel.js +875 -0
  119. package/lib/ecosystems/parsers-dotnet.js +1430 -0
  120. package/lib/ecosystems/parsers-gleam.js +302 -0
  121. package/lib/ecosystems/parsers-go.js +818 -0
  122. package/lib/ecosystems/parsers-js.js +4222 -0
  123. package/lib/ecosystems/parsers-jvm.js +948 -0
  124. package/lib/ecosystems/parsers-misc.js +3191 -0
  125. package/lib/ecosystems/parsers-python.js +2070 -0
  126. package/lib/ecosystems/parsers-rust.js +1294 -0
  127. package/lib/ecosystems/parsers-zig.js +861 -0
  128. package/lib/ecosystems/pep770.js +341 -0
  129. package/lib/ecosystems/pylockutils.js +427 -0
  130. package/lib/ecosystems/pythonutils.js +1281 -0
  131. package/lib/ecosystems/registryProvenance.js +1003 -0
  132. package/lib/ecosystems/remote/huggingface.js +1343 -0
  133. package/lib/ecosystems/rubyutils.js +1629 -0
  134. package/lib/ecosystems/rusi.js +820 -0
  135. package/lib/ecosystems/sbtutils.js +972 -0
  136. package/lib/ecosystems/tea.js +607 -0
  137. package/lib/ecosystems/utils.js +575 -0
  138. package/lib/ecosystems/zigResolver.js +410 -0
  139. package/lib/evinser/db.js +145 -0
  140. package/lib/evinser/evinser.js +2362 -0
  141. package/lib/evinser/scalasem.js +65 -0
  142. package/lib/evinser/swiftsem.js +767 -0
  143. package/lib/helpers/annotationFormatter.js +84 -0
  144. package/lib/helpers/bomSigner.js +312 -0
  145. package/lib/helpers/exportUtils.js +160 -0
  146. package/lib/helpers/remote/dependency-track.js +117 -0
  147. package/lib/helpers/versutils.js +202 -0
  148. package/lib/helpers/vsixutils.js +1069 -0
  149. package/lib/inventory/agentFormulationParser.js +322 -0
  150. package/lib/inventory/aiCollector.js +2140 -0
  151. package/lib/inventory/aiInventory.js +414 -0
  152. package/lib/inventory/aiModelVariants.js +121 -0
  153. package/lib/inventory/aiOversightCollector.js +661 -0
  154. package/lib/inventory/aiProvenanceCollector.js +822 -0
  155. package/lib/inventory/analyzer.js +6089 -0
  156. package/lib/inventory/analyzerScope.js +807 -0
  157. package/lib/inventory/astWorker.js +20 -0
  158. package/lib/inventory/atomUtils.js +1092 -0
  159. package/lib/inventory/auditCategories.js +192 -0
  160. package/lib/inventory/bomUtils.js +369 -0
  161. package/lib/inventory/cacheCommands.js +182 -0
  162. package/lib/inventory/cacheDir.js +104 -0
  163. package/lib/inventory/cbomutils.js +590 -0
  164. package/lib/inventory/cdxrs.js +434 -0
  165. package/lib/inventory/ciParsers/azurePipelines.js +295 -0
  166. package/lib/inventory/ciParsers/circleCi.js +286 -0
  167. package/lib/inventory/ciParsers/common.js +24 -0
  168. package/lib/inventory/ciParsers/githubActions.js +2434 -0
  169. package/lib/inventory/ciParsers/gitlabCi.js +213 -0
  170. package/lib/inventory/ciParsers/jenkins.js +180 -0
  171. package/lib/inventory/citations.js +234 -0
  172. package/lib/inventory/communityAiConfigParser.js +756 -0
  173. package/lib/inventory/containerRisk.js +204 -0
  174. package/lib/inventory/cryptoAlgorithmFamily.js +258 -0
  175. package/lib/inventory/deps.js +807 -0
  176. package/lib/inventory/depsUtils.js +588 -0
  177. package/lib/inventory/display.js +2128 -0
  178. package/lib/inventory/dosai.js +601 -0
  179. package/lib/inventory/dosaiParsers.js +154 -0
  180. package/lib/inventory/dynamic.js +145 -0
  181. package/lib/inventory/envcontext.js +1789 -0
  182. package/lib/inventory/evidenceUtils.js +774 -0
  183. package/lib/inventory/fetchBatch.js +779 -0
  184. package/lib/inventory/fetchRate.js +255 -0
  185. package/lib/inventory/forgeEnricher.js +300 -0
  186. package/lib/inventory/formulationParsers.js +422 -0
  187. package/lib/inventory/gtfobins.js +339 -0
  188. package/lib/inventory/hbom.js +719 -0
  189. package/lib/inventory/hbomAnalysis.js +310 -0
  190. package/lib/inventory/hbomLoader.js +35 -0
  191. package/lib/inventory/hostTopology.js +835 -0
  192. package/lib/inventory/inventoryStats.js +100 -0
  193. package/lib/inventory/jsonLike.js +121 -0
  194. package/lib/inventory/licenseEnhancer.js +918 -0
  195. package/lib/inventory/lolbas.js +285 -0
  196. package/lib/inventory/mcp.js +253 -0
  197. package/lib/inventory/mcpConfigParser.js +683 -0
  198. package/lib/inventory/mcpDiscovery.js +112 -0
  199. package/lib/inventory/mcpPinning.js +209 -0
  200. package/lib/inventory/osPackageResolver.js +493 -0
  201. package/lib/inventory/osinfo.js +277 -0
  202. package/lib/inventory/osqueryTransform.js +274 -0
  203. package/lib/inventory/plugins.js +466 -0
  204. package/lib/inventory/protobom.js +231 -0
  205. package/lib/inventory/protobomLoader.js +43 -0
  206. package/lib/inventory/provenanceUtils.js +224 -0
  207. package/lib/inventory/purl.js +749 -0
  208. package/lib/inventory/rustFormulationParser.js +339 -0
  209. package/lib/inventory/sbomDocument.js +340 -0
  210. package/lib/inventory/source.js +1488 -0
  211. package/lib/inventory/spdx.js +361 -0
  212. package/lib/inventory/spdxExpression.js +512 -0
  213. package/lib/inventory/spdxUtils.js +107 -0
  214. package/lib/inventory/table.js +263 -0
  215. package/lib/inventory/traceRunner.js +439 -0
  216. package/lib/inventory/unicodeScan.js +147 -0
  217. package/lib/managers/binary.js +3307 -0
  218. package/lib/managers/containerutils.js +125 -0
  219. package/lib/managers/docker.js +2145 -0
  220. package/lib/managers/dockerConnection.js +162 -0
  221. package/lib/managers/oci.js +704 -0
  222. package/lib/managers/piptree.js +267 -0
  223. package/lib/parsers/cmakeCache.js +199 -0
  224. package/lib/parsers/gguf.js +492 -0
  225. package/lib/parsers/gitAiNotes.js +233 -0
  226. package/lib/parsers/gitmodules.js +239 -0
  227. package/lib/parsers/htmlExtract.js +610 -0
  228. package/lib/parsers/huggingfaceManifest.js +551 -0
  229. package/lib/parsers/huggingfaceUtils.js +506 -0
  230. package/lib/parsers/iri.js +618 -0
  231. package/lib/parsers/npmrc.js +99 -0
  232. package/lib/parsers/ollama.js +101 -0
  233. package/lib/parsers/xml.js +841 -0
  234. package/lib/server/openapi.yaml +433 -0
  235. package/lib/server/server.js +616 -0
  236. package/lib/stages/postgen/annotator.js +977 -0
  237. package/lib/stages/postgen/auditBom.js +468 -0
  238. package/lib/stages/postgen/postgen.js +1239 -0
  239. package/lib/stages/postgen/ruleEngine.js +731 -0
  240. package/lib/stages/postgen/sortBom.js +195 -0
  241. package/lib/stages/postgen/spdxConverter.js +796 -0
  242. package/lib/stages/postgen/specVersionCompat.js +703 -0
  243. package/lib/stages/pregen/envAudit.js +388 -0
  244. package/lib/stages/pregen/pregen.js +637 -0
  245. package/lib/third-party/README.md +9 -0
  246. package/lib/third-party/arborist/CHANGELOG.md +1109 -0
  247. package/lib/third-party/arborist/LICENSE.md +20 -0
  248. package/lib/third-party/arborist/README.md +315 -0
  249. package/lib/third-party/arborist/lib/arborist/index.js +258 -0
  250. package/lib/third-party/arborist/lib/arborist/load-actual.js +605 -0
  251. package/lib/third-party/arborist/lib/arborist/load-virtual.js +300 -0
  252. package/lib/third-party/arborist/lib/bin-links.js +120 -0
  253. package/lib/third-party/arborist/lib/calc-dep-flags.js +130 -0
  254. package/lib/third-party/arborist/lib/case-insensitive-map.js +51 -0
  255. package/lib/third-party/arborist/lib/consistent-resolve.js +42 -0
  256. package/lib/third-party/arborist/lib/debug.js +50 -0
  257. package/lib/third-party/arborist/lib/dep-valid.js +190 -0
  258. package/lib/third-party/arborist/lib/edge.js +417 -0
  259. package/lib/third-party/arborist/lib/from-path.js +36 -0
  260. package/lib/third-party/arborist/lib/gather-dep-set.js +43 -0
  261. package/lib/third-party/arborist/lib/index.js +22 -0
  262. package/lib/third-party/arborist/lib/inventory.js +138 -0
  263. package/lib/third-party/arborist/lib/link.js +169 -0
  264. package/lib/third-party/arborist/lib/node.js +1673 -0
  265. package/lib/third-party/arborist/lib/npm-extension.js +45 -0
  266. package/lib/third-party/arborist/lib/override-resolves.js +11 -0
  267. package/lib/third-party/arborist/lib/override-set.js +319 -0
  268. package/lib/third-party/arborist/lib/package-extensions.js +289 -0
  269. package/lib/third-party/arborist/lib/printable.js +210 -0
  270. package/lib/third-party/arborist/lib/proc-log.js +30 -0
  271. package/lib/third-party/arborist/lib/realpath.js +100 -0
  272. package/lib/third-party/arborist/lib/relpath.js +4 -0
  273. package/lib/third-party/arborist/lib/shrinkwrap.js +1291 -0
  274. package/lib/third-party/arborist/lib/spec-from-lock.js +34 -0
  275. package/lib/third-party/arborist/lib/tree-check.js +165 -0
  276. package/lib/third-party/arborist/lib/validate-npm-package-name.js +107 -0
  277. package/lib/third-party/arborist/lib/version-from-tgz.js +51 -0
  278. package/lib/third-party/arborist/lib/yarn-lock.js +397 -0
  279. package/lib/validator/bomValidator.js +985 -0
  280. package/lib/validator/complianceEngine.js +241 -0
  281. package/lib/validator/complianceRules.js +1680 -0
  282. package/lib/validator/generated/runtime.mjs +17 -0
  283. package/lib/validator/generated/validate-1.6.mjs +21258 -0
  284. package/lib/validator/generated/validate-1.7.mjs +24803 -0
  285. package/lib/validator/generated/validate-2.0.mjs +22931 -0
  286. package/lib/validator/index.js +246 -0
  287. package/lib/validator/reporters/annotations.js +142 -0
  288. package/lib/validator/reporters/console.js +160 -0
  289. package/lib/validator/reporters/index.js +41 -0
  290. package/lib/validator/reporters/json.js +37 -0
  291. package/lib/validator/reporters/sarif.js +184 -0
  292. package/package.json +209 -26
  293. package/types/bin/audit.d.ts +3 -0
  294. package/types/bin/audit.d.ts.map +1 -0
  295. package/types/bin/cdxgen.d.ts +3 -0
  296. package/types/bin/cdxgen.d.ts.map +1 -0
  297. package/types/bin/convert.d.ts +3 -0
  298. package/types/bin/convert.d.ts.map +1 -0
  299. package/types/bin/evinse.d.ts +3 -0
  300. package/types/bin/evinse.d.ts.map +1 -0
  301. package/types/bin/hbom.d.ts +3 -0
  302. package/types/bin/hbom.d.ts.map +1 -0
  303. package/types/bin/repl.d.ts +3 -0
  304. package/types/bin/repl.d.ts.map +1 -0
  305. package/types/bin/sign.d.ts +3 -0
  306. package/types/bin/sign.d.ts.map +1 -0
  307. package/types/bin/tracebom.d.ts +3 -0
  308. package/types/bin/tracebom.d.ts.map +1 -0
  309. package/types/bin/validate.d.ts +14 -0
  310. package/types/bin/validate.d.ts.map +1 -0
  311. package/types/bin/verify.d.ts +3 -0
  312. package/types/bin/verify.d.ts.map +1 -0
  313. package/types/lib/audit/index.d.ts +153 -0
  314. package/types/lib/audit/index.d.ts.map +1 -0
  315. package/types/lib/audit/progress.d.ts +31 -0
  316. package/types/lib/audit/progress.d.ts.map +1 -0
  317. package/types/lib/audit/reporters.d.ts +62 -0
  318. package/types/lib/audit/reporters.d.ts.map +1 -0
  319. package/types/lib/audit/scoring.d.ts +36 -0
  320. package/types/lib/audit/scoring.d.ts.map +1 -0
  321. package/types/lib/audit/targets.d.ts +77 -0
  322. package/types/lib/audit/targets.d.ts.map +1 -0
  323. package/types/lib/cli/bomAssembly.d.ts +315 -0
  324. package/types/lib/cli/bomAssembly.d.ts.map +1 -0
  325. package/types/lib/cli/cliOptions.d.ts +156 -0
  326. package/types/lib/cli/cliOptions.d.ts.map +1 -0
  327. package/types/lib/cli/index.d.ts +107 -0
  328. package/types/lib/cli/index.d.ts.map +1 -0
  329. package/types/lib/cli/jsBom.d.ts +94 -0
  330. package/types/lib/cli/jsBom.d.ts.map +1 -0
  331. package/types/lib/cli/jvmBom.d.ts +54 -0
  332. package/types/lib/cli/jvmBom.d.ts.map +1 -0
  333. package/types/lib/cli/managedBom.d.ts +105 -0
  334. package/types/lib/cli/managedBom.d.ts.map +1 -0
  335. package/types/lib/cli/nativeBom.d.ts +143 -0
  336. package/types/lib/cli/nativeBom.d.ts.map +1 -0
  337. package/types/lib/core/activity.d.ts +178 -0
  338. package/types/lib/core/activity.d.ts.map +1 -0
  339. package/types/lib/core/env.d.ts +192 -0
  340. package/types/lib/core/env.d.ts.map +1 -0
  341. package/types/lib/core/fs.d.ts +158 -0
  342. package/types/lib/core/fs.d.ts.map +1 -0
  343. package/types/lib/core/httpClient.d.ts +149 -0
  344. package/types/lib/core/httpClient.d.ts.map +1 -0
  345. package/types/lib/core/logger.d.ts +28 -0
  346. package/types/lib/core/logger.d.ts.map +1 -0
  347. package/types/lib/core/parallel.d.ts +95 -0
  348. package/types/lib/core/parallel.d.ts.map +1 -0
  349. package/types/lib/core/paths.d.ts +36 -0
  350. package/types/lib/core/paths.d.ts.map +1 -0
  351. package/types/lib/core/projectConfig.d.ts +23 -0
  352. package/types/lib/core/projectConfig.d.ts.map +1 -0
  353. package/types/lib/core/propertySanitizer.d.ts +23 -0
  354. package/types/lib/core/propertySanitizer.d.ts.map +1 -0
  355. package/types/lib/core/spdx.d.ts +5 -0
  356. package/types/lib/core/spdx.d.ts.map +1 -0
  357. package/types/lib/core/state.d.ts +20 -0
  358. package/types/lib/core/state.d.ts.map +1 -0
  359. package/types/lib/core/text.d.ts +78 -0
  360. package/types/lib/core/text.d.ts.map +1 -0
  361. package/types/lib/core/ui.d.ts +87 -0
  362. package/types/lib/core/ui.d.ts.map +1 -0
  363. package/types/lib/core/urls.d.ts +43 -0
  364. package/types/lib/core/urls.d.ts.map +1 -0
  365. package/types/lib/ecosystems/asarutils.d.ts +71 -0
  366. package/types/lib/ecosystems/asarutils.d.ts.map +1 -0
  367. package/types/lib/ecosystems/bunutils.d.ts +38 -0
  368. package/types/lib/ecosystems/bunutils.d.ts.map +1 -0
  369. package/types/lib/ecosystems/caxa.d.ts +8 -0
  370. package/types/lib/ecosystems/caxa.d.ts.map +1 -0
  371. package/types/lib/ecosystems/chromextutils.d.ts +97 -0
  372. package/types/lib/ecosystems/chromextutils.d.ts.map +1 -0
  373. package/types/lib/ecosystems/cmakeResolver.d.ts +176 -0
  374. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -0
  375. package/types/lib/ecosystems/cppEvidence.d.ts +35 -0
  376. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -0
  377. package/types/lib/ecosystems/denoutils.d.ts +76 -0
  378. package/types/lib/ecosystems/denoutils.d.ts.map +1 -0
  379. package/types/lib/ecosystems/dotnetutils.d.ts +45 -0
  380. package/types/lib/ecosystems/dotnetutils.d.ts.map +1 -0
  381. package/types/lib/ecosystems/ecosystems.d.ts +279 -0
  382. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -0
  383. package/types/lib/ecosystems/golem.d.ts +60 -0
  384. package/types/lib/ecosystems/golem.d.ts.map +1 -0
  385. package/types/lib/ecosystems/gradleutils.d.ts +139 -0
  386. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -0
  387. package/types/lib/ecosystems/jsEvidence.d.ts +17 -0
  388. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -0
  389. package/types/lib/ecosystems/npmutils.d.ts +183 -0
  390. package/types/lib/ecosystems/npmutils.d.ts.map +1 -0
  391. package/types/lib/ecosystems/parsers-bazel.d.ts +36 -0
  392. package/types/lib/ecosystems/parsers-bazel.d.ts.map +1 -0
  393. package/types/lib/ecosystems/parsers-dotnet.d.ts +116 -0
  394. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -0
  395. package/types/lib/ecosystems/parsers-gleam.d.ts +33 -0
  396. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -0
  397. package/types/lib/ecosystems/parsers-go.d.ts +92 -0
  398. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -0
  399. package/types/lib/ecosystems/parsers-js.d.ts +242 -0
  400. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -0
  401. package/types/lib/ecosystems/parsers-jvm.d.ts +100 -0
  402. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -0
  403. package/types/lib/ecosystems/parsers-misc.d.ts +450 -0
  404. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -0
  405. package/types/lib/ecosystems/parsers-python.d.ts +145 -0
  406. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -0
  407. package/types/lib/ecosystems/parsers-rust.d.ts +56 -0
  408. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -0
  409. package/types/lib/ecosystems/parsers-zig.d.ts +83 -0
  410. package/types/lib/ecosystems/parsers-zig.d.ts.map +1 -0
  411. package/types/lib/ecosystems/pep770.d.ts +38 -0
  412. package/types/lib/ecosystems/pep770.d.ts.map +1 -0
  413. package/types/lib/ecosystems/pylockutils.d.ts +68 -0
  414. package/types/lib/ecosystems/pylockutils.d.ts.map +1 -0
  415. package/types/lib/ecosystems/pythonutils.d.ts +70 -0
  416. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -0
  417. package/types/lib/ecosystems/registryProvenance.d.ts +26 -0
  418. package/types/lib/ecosystems/registryProvenance.d.ts.map +1 -0
  419. package/types/lib/ecosystems/remote/huggingface.d.ts +31 -0
  420. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -0
  421. package/types/lib/ecosystems/rubyutils.d.ts +182 -0
  422. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -0
  423. package/types/lib/ecosystems/rusi.d.ts +40 -0
  424. package/types/lib/ecosystems/rusi.d.ts.map +1 -0
  425. package/types/lib/ecosystems/sbtutils.d.ts +179 -0
  426. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -0
  427. package/types/lib/ecosystems/tea.d.ts +180 -0
  428. package/types/lib/ecosystems/tea.d.ts.map +1 -0
  429. package/types/lib/ecosystems/utils.d.ts +41 -0
  430. package/types/lib/ecosystems/utils.d.ts.map +1 -0
  431. package/types/lib/ecosystems/zigResolver.d.ts +70 -0
  432. package/types/lib/ecosystems/zigResolver.d.ts.map +1 -0
  433. package/types/lib/evinser/db.d.ts +17 -0
  434. package/types/lib/evinser/db.d.ts.map +1 -0
  435. package/types/lib/evinser/evinser.d.ts +354 -0
  436. package/types/lib/evinser/evinser.d.ts.map +1 -0
  437. package/types/lib/evinser/scalasem.d.ts +15 -0
  438. package/types/lib/evinser/scalasem.d.ts.map +1 -0
  439. package/types/lib/evinser/swiftsem.d.ts +103 -0
  440. package/types/lib/evinser/swiftsem.d.ts.map +1 -0
  441. package/types/lib/helpers/annotationFormatter.d.ts +23 -0
  442. package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
  443. package/types/lib/helpers/bomSigner.d.ts +28 -0
  444. package/types/lib/helpers/bomSigner.d.ts.map +1 -0
  445. package/types/lib/helpers/exportUtils.d.ts +52 -0
  446. package/types/lib/helpers/exportUtils.d.ts.map +1 -0
  447. package/types/lib/helpers/remote/dependency-track.d.ts +23 -0
  448. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
  449. package/types/lib/helpers/versutils.d.ts +8 -0
  450. package/types/lib/helpers/versutils.d.ts.map +1 -0
  451. package/types/lib/helpers/vsixutils.d.ts +130 -0
  452. package/types/lib/helpers/vsixutils.d.ts.map +1 -0
  453. package/types/lib/inventory/agentFormulationParser.d.ts +21 -0
  454. package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -0
  455. package/types/lib/inventory/aiCollector.d.ts +61 -0
  456. package/types/lib/inventory/aiCollector.d.ts.map +1 -0
  457. package/types/lib/inventory/aiInventory.d.ts +103 -0
  458. package/types/lib/inventory/aiInventory.d.ts.map +1 -0
  459. package/types/lib/inventory/aiModelVariants.d.ts +31 -0
  460. package/types/lib/inventory/aiModelVariants.d.ts.map +1 -0
  461. package/types/lib/inventory/aiOversightCollector.d.ts +28 -0
  462. package/types/lib/inventory/aiOversightCollector.d.ts.map +1 -0
  463. package/types/lib/inventory/aiProvenanceCollector.d.ts +13 -0
  464. package/types/lib/inventory/aiProvenanceCollector.d.ts.map +1 -0
  465. package/types/lib/inventory/analyzer.d.ts +157 -0
  466. package/types/lib/inventory/analyzer.d.ts.map +1 -0
  467. package/types/lib/inventory/analyzerScope.d.ts +106 -0
  468. package/types/lib/inventory/analyzerScope.d.ts.map +1 -0
  469. package/types/lib/inventory/astWorker.d.ts +9 -0
  470. package/types/lib/inventory/astWorker.d.ts.map +1 -0
  471. package/types/lib/inventory/atomUtils.d.ts +159 -0
  472. package/types/lib/inventory/atomUtils.d.ts.map +1 -0
  473. package/types/lib/inventory/auditCategories.d.ts +80 -0
  474. package/types/lib/inventory/auditCategories.d.ts.map +1 -0
  475. package/types/lib/inventory/bomUtils.d.ts +145 -0
  476. package/types/lib/inventory/bomUtils.d.ts.map +1 -0
  477. package/types/lib/inventory/cacheCommands.d.ts +28 -0
  478. package/types/lib/inventory/cacheCommands.d.ts.map +1 -0
  479. package/types/lib/inventory/cacheDir.d.ts +55 -0
  480. package/types/lib/inventory/cacheDir.d.ts.map +1 -0
  481. package/types/lib/inventory/cbomutils.d.ts +37 -0
  482. package/types/lib/inventory/cbomutils.d.ts.map +1 -0
  483. package/types/lib/inventory/cdxrs.d.ts +90 -0
  484. package/types/lib/inventory/cdxrs.d.ts.map +1 -0
  485. package/types/lib/inventory/ciParsers/azurePipelines.d.ts +27 -0
  486. package/types/lib/inventory/ciParsers/azurePipelines.d.ts.map +1 -0
  487. package/types/lib/inventory/ciParsers/circleCi.d.ts +27 -0
  488. package/types/lib/inventory/ciParsers/circleCi.d.ts.map +1 -0
  489. package/types/lib/inventory/ciParsers/common.d.ts +11 -0
  490. package/types/lib/inventory/ciParsers/common.d.ts.map +1 -0
  491. package/types/lib/inventory/ciParsers/githubActions.d.ts +38 -0
  492. package/types/lib/inventory/ciParsers/githubActions.d.ts.map +1 -0
  493. package/types/lib/inventory/ciParsers/gitlabCi.d.ts +26 -0
  494. package/types/lib/inventory/ciParsers/gitlabCi.d.ts.map +1 -0
  495. package/types/lib/inventory/ciParsers/jenkins.d.ts +26 -0
  496. package/types/lib/inventory/ciParsers/jenkins.d.ts.map +1 -0
  497. package/types/lib/inventory/citations.d.ts +90 -0
  498. package/types/lib/inventory/citations.d.ts.map +1 -0
  499. package/types/lib/inventory/communityAiConfigParser.d.ts +14 -0
  500. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -0
  501. package/types/lib/inventory/containerRisk.d.ts +20 -0
  502. package/types/lib/inventory/containerRisk.d.ts.map +1 -0
  503. package/types/lib/inventory/cryptoAlgorithmFamily.d.ts +37 -0
  504. package/types/lib/inventory/cryptoAlgorithmFamily.d.ts.map +1 -0
  505. package/types/lib/inventory/deps.d.ts +140 -0
  506. package/types/lib/inventory/deps.d.ts.map +1 -0
  507. package/types/lib/inventory/depsUtils.d.ts +74 -0
  508. package/types/lib/inventory/depsUtils.d.ts.map +1 -0
  509. package/types/lib/inventory/display.d.ts +227 -0
  510. package/types/lib/inventory/display.d.ts.map +1 -0
  511. package/types/lib/inventory/dosai.d.ts +130 -0
  512. package/types/lib/inventory/dosai.d.ts.map +1 -0
  513. package/types/lib/inventory/dosaiParsers.d.ts +59 -0
  514. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -0
  515. package/types/lib/inventory/dynamic.d.ts +25 -0
  516. package/types/lib/inventory/dynamic.d.ts.map +1 -0
  517. package/types/lib/inventory/envcontext.d.ts +377 -0
  518. package/types/lib/inventory/envcontext.d.ts.map +1 -0
  519. package/types/lib/inventory/evidenceUtils.d.ts +74 -0
  520. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -0
  521. package/types/lib/inventory/fetchBatch.d.ts +127 -0
  522. package/types/lib/inventory/fetchBatch.d.ts.map +1 -0
  523. package/types/lib/inventory/fetchRate.d.ts +118 -0
  524. package/types/lib/inventory/fetchRate.d.ts.map +1 -0
  525. package/types/lib/inventory/forgeEnricher.d.ts +31 -0
  526. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -0
  527. package/types/lib/inventory/formulationParsers.d.ts +50 -0
  528. package/types/lib/inventory/formulationParsers.d.ts.map +1 -0
  529. package/types/lib/inventory/gtfobins.d.ts +27 -0
  530. package/types/lib/inventory/gtfobins.d.ts.map +1 -0
  531. package/types/lib/inventory/hbom.d.ts +58 -0
  532. package/types/lib/inventory/hbom.d.ts.map +1 -0
  533. package/types/lib/inventory/hbomAnalysis.d.ts +56 -0
  534. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -0
  535. package/types/lib/inventory/hbomLoader.d.ts +7 -0
  536. package/types/lib/inventory/hbomLoader.d.ts.map +1 -0
  537. package/types/lib/inventory/hostTopology.d.ts +47 -0
  538. package/types/lib/inventory/hostTopology.d.ts.map +1 -0
  539. package/types/lib/inventory/inventoryStats.d.ts +42 -0
  540. package/types/lib/inventory/inventoryStats.d.ts.map +1 -0
  541. package/types/lib/inventory/jsonLike.d.ts +23 -0
  542. package/types/lib/inventory/jsonLike.d.ts.map +1 -0
  543. package/types/lib/inventory/licenseEnhancer.d.ts +70 -0
  544. package/types/lib/inventory/licenseEnhancer.d.ts.map +1 -0
  545. package/types/lib/inventory/lolbas.d.ts +16 -0
  546. package/types/lib/inventory/lolbas.d.ts.map +1 -0
  547. package/types/lib/inventory/mcp.d.ts +29 -0
  548. package/types/lib/inventory/mcp.d.ts.map +1 -0
  549. package/types/lib/inventory/mcpConfigParser.d.ts +20 -0
  550. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -0
  551. package/types/lib/inventory/mcpDiscovery.d.ts +33 -0
  552. package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -0
  553. package/types/lib/inventory/mcpPinning.d.ts +11 -0
  554. package/types/lib/inventory/mcpPinning.d.ts.map +1 -0
  555. package/types/lib/inventory/osPackageResolver.d.ts +49 -0
  556. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -0
  557. package/types/lib/inventory/osinfo.d.ts +120 -0
  558. package/types/lib/inventory/osinfo.d.ts.map +1 -0
  559. package/types/lib/inventory/osqueryTransform.d.ts +84 -0
  560. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -0
  561. package/types/lib/inventory/plugins.d.ts +58 -0
  562. package/types/lib/inventory/plugins.d.ts.map +1 -0
  563. package/types/lib/inventory/protobom.d.ts +71 -0
  564. package/types/lib/inventory/protobom.d.ts.map +1 -0
  565. package/types/lib/inventory/protobomLoader.d.ts +17 -0
  566. package/types/lib/inventory/protobomLoader.d.ts.map +1 -0
  567. package/types/lib/inventory/provenanceUtils.d.ts +98 -0
  568. package/types/lib/inventory/provenanceUtils.d.ts.map +1 -0
  569. package/types/lib/inventory/purl.d.ts +313 -0
  570. package/types/lib/inventory/purl.d.ts.map +1 -0
  571. package/types/lib/inventory/rustFormulationParser.d.ts +17 -0
  572. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -0
  573. package/types/lib/inventory/sbomDocument.d.ts +59 -0
  574. package/types/lib/inventory/sbomDocument.d.ts.map +1 -0
  575. package/types/lib/inventory/source.d.ts +163 -0
  576. package/types/lib/inventory/source.d.ts.map +1 -0
  577. package/types/lib/inventory/spdx.d.ts +66 -0
  578. package/types/lib/inventory/spdx.d.ts.map +1 -0
  579. package/types/lib/inventory/spdxExpression.d.ts +58 -0
  580. package/types/lib/inventory/spdxExpression.d.ts.map +1 -0
  581. package/types/lib/inventory/spdxUtils.d.ts +12 -0
  582. package/types/lib/inventory/spdxUtils.d.ts.map +1 -0
  583. package/types/lib/inventory/table.d.ts +19 -0
  584. package/types/lib/inventory/table.d.ts.map +1 -0
  585. package/types/lib/inventory/traceRunner.d.ts +80 -0
  586. package/types/lib/inventory/traceRunner.d.ts.map +1 -0
  587. package/types/lib/inventory/unicodeScan.d.ts +46 -0
  588. package/types/lib/inventory/unicodeScan.d.ts.map +1 -0
  589. package/types/lib/managers/binary.d.ts +76 -0
  590. package/types/lib/managers/binary.d.ts.map +1 -0
  591. package/types/lib/managers/containerutils.d.ts +30 -0
  592. package/types/lib/managers/containerutils.d.ts.map +1 -0
  593. package/types/lib/managers/docker.d.ts +157 -0
  594. package/types/lib/managers/docker.d.ts.map +1 -0
  595. package/types/lib/managers/dockerConnection.d.ts +42 -0
  596. package/types/lib/managers/dockerConnection.d.ts.map +1 -0
  597. package/types/lib/managers/oci.d.ts +21 -0
  598. package/types/lib/managers/oci.d.ts.map +1 -0
  599. package/types/lib/managers/piptree.d.ts +13 -0
  600. package/types/lib/managers/piptree.d.ts.map +1 -0
  601. package/types/lib/parsers/cmakeCache.d.ts +70 -0
  602. package/types/lib/parsers/cmakeCache.d.ts.map +1 -0
  603. package/types/lib/parsers/gguf.d.ts +39 -0
  604. package/types/lib/parsers/gguf.d.ts.map +1 -0
  605. package/types/lib/parsers/gitAiNotes.d.ts +15 -0
  606. package/types/lib/parsers/gitAiNotes.d.ts.map +1 -0
  607. package/types/lib/parsers/gitmodules.d.ts +88 -0
  608. package/types/lib/parsers/gitmodules.d.ts.map +1 -0
  609. package/types/lib/parsers/htmlExtract.d.ts +34 -0
  610. package/types/lib/parsers/htmlExtract.d.ts.map +1 -0
  611. package/types/lib/parsers/huggingfaceManifest.d.ts +153 -0
  612. package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -0
  613. package/types/lib/parsers/huggingfaceUtils.d.ts +155 -0
  614. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -0
  615. package/types/lib/parsers/iri.d.ts +68 -0
  616. package/types/lib/parsers/iri.d.ts.map +1 -0
  617. package/types/lib/parsers/npmrc.d.ts +33 -0
  618. package/types/lib/parsers/npmrc.d.ts.map +1 -0
  619. package/types/lib/parsers/ollama.d.ts +22 -0
  620. package/types/lib/parsers/ollama.d.ts.map +1 -0
  621. package/types/lib/parsers/xml.d.ts +47 -0
  622. package/types/lib/parsers/xml.d.ts.map +1 -0
  623. package/types/lib/server/server.d.ts +51 -0
  624. package/types/lib/server/server.d.ts.map +1 -0
  625. package/types/lib/stages/postgen/annotator.d.ts +36 -0
  626. package/types/lib/stages/postgen/annotator.d.ts.map +1 -0
  627. package/types/lib/stages/postgen/auditBom.d.ts +92 -0
  628. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -0
  629. package/types/lib/stages/postgen/postgen.d.ts +59 -0
  630. package/types/lib/stages/postgen/postgen.d.ts.map +1 -0
  631. package/types/lib/stages/postgen/ruleEngine.d.ts +18 -0
  632. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -0
  633. package/types/lib/stages/postgen/sortBom.d.ts +34 -0
  634. package/types/lib/stages/postgen/sortBom.d.ts.map +1 -0
  635. package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
  636. package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
  637. package/types/lib/stages/postgen/specVersionCompat.d.ts +71 -0
  638. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -0
  639. package/types/lib/stages/pregen/envAudit.d.ts +14 -0
  640. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -0
  641. package/types/lib/stages/pregen/pregen.d.ts +59 -0
  642. package/types/lib/stages/pregen/pregen.d.ts.map +1 -0
  643. package/types/lib/third-party/arborist/lib/arborist/index.d.ts +47 -0
  644. package/types/lib/third-party/arborist/lib/arborist/index.d.ts.map +1 -0
  645. package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts +4 -0
  646. package/types/lib/third-party/arborist/lib/arborist/load-actual.d.ts.map +1 -0
  647. package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts +4 -0
  648. package/types/lib/third-party/arborist/lib/arborist/load-virtual.d.ts.map +1 -0
  649. package/types/lib/third-party/arborist/lib/bin-links.d.ts +18 -0
  650. package/types/lib/third-party/arborist/lib/bin-links.d.ts.map +1 -0
  651. package/types/lib/third-party/arborist/lib/calc-dep-flags.d.ts +3 -0
  652. package/types/lib/third-party/arborist/lib/calc-dep-flags.d.ts.map +1 -0
  653. package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts +9 -0
  654. package/types/lib/third-party/arborist/lib/case-insensitive-map.d.ts.map +1 -0
  655. package/types/lib/third-party/arborist/lib/consistent-resolve.d.ts +3 -0
  656. package/types/lib/third-party/arborist/lib/consistent-resolve.d.ts.map +1 -0
  657. package/types/lib/third-party/arborist/lib/debug.d.ts +3 -0
  658. package/types/lib/third-party/arborist/lib/debug.d.ts.map +1 -0
  659. package/types/lib/third-party/arborist/lib/dep-valid.d.ts +3 -0
  660. package/types/lib/third-party/arborist/lib/dep-valid.d.ts.map +1 -0
  661. package/types/lib/third-party/arborist/lib/edge.d.ts +46 -0
  662. package/types/lib/third-party/arborist/lib/edge.d.ts.map +1 -0
  663. package/types/lib/third-party/arborist/lib/from-path.d.ts +3 -0
  664. package/types/lib/third-party/arborist/lib/from-path.d.ts.map +1 -0
  665. package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts +3 -0
  666. package/types/lib/third-party/arborist/lib/gather-dep-set.d.ts.map +1 -0
  667. package/types/lib/third-party/arborist/lib/index.d.ts +3 -0
  668. package/types/lib/third-party/arborist/lib/index.d.ts.map +1 -0
  669. package/types/lib/third-party/arborist/lib/inventory.d.ts +14 -0
  670. package/types/lib/third-party/arborist/lib/inventory.d.ts.map +1 -0
  671. package/types/lib/third-party/arborist/lib/link.d.ts +18 -0
  672. package/types/lib/third-party/arborist/lib/link.d.ts.map +1 -0
  673. package/types/lib/third-party/arborist/lib/node.d.ts +112 -0
  674. package/types/lib/third-party/arborist/lib/node.d.ts.map +1 -0
  675. package/types/lib/third-party/arborist/lib/npm-extension.d.ts +18 -0
  676. package/types/lib/third-party/arborist/lib/npm-extension.d.ts.map +1 -0
  677. package/types/lib/third-party/arborist/lib/override-resolves.d.ts +3 -0
  678. package/types/lib/third-party/arborist/lib/override-resolves.d.ts.map +1 -0
  679. package/types/lib/third-party/arborist/lib/override-set.d.ts +27 -0
  680. package/types/lib/third-party/arborist/lib/override-set.d.ts.map +1 -0
  681. package/types/lib/third-party/arborist/lib/package-extensions.d.ts +36 -0
  682. package/types/lib/third-party/arborist/lib/package-extensions.d.ts.map +1 -0
  683. package/types/lib/third-party/arborist/lib/printable.d.ts +3 -0
  684. package/types/lib/third-party/arborist/lib/printable.d.ts.map +1 -0
  685. package/types/lib/third-party/arborist/lib/proc-log.d.ts +20 -0
  686. package/types/lib/third-party/arborist/lib/proc-log.d.ts.map +1 -0
  687. package/types/lib/third-party/arborist/lib/realpath.d.ts +3 -0
  688. package/types/lib/third-party/arborist/lib/realpath.d.ts.map +1 -0
  689. package/types/lib/third-party/arborist/lib/relpath.d.ts +3 -0
  690. package/types/lib/third-party/arborist/lib/relpath.d.ts.map +1 -0
  691. package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts +54 -0
  692. package/types/lib/third-party/arborist/lib/shrinkwrap.d.ts.map +1 -0
  693. package/types/lib/third-party/arborist/lib/spec-from-lock.d.ts +3 -0
  694. package/types/lib/third-party/arborist/lib/spec-from-lock.d.ts.map +1 -0
  695. package/types/lib/third-party/arborist/lib/tree-check.d.ts +3 -0
  696. package/types/lib/third-party/arborist/lib/tree-check.d.ts.map +1 -0
  697. package/types/lib/third-party/arborist/lib/validate-npm-package-name.d.ts +13 -0
  698. package/types/lib/third-party/arborist/lib/validate-npm-package-name.d.ts.map +1 -0
  699. package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts +6 -0
  700. package/types/lib/third-party/arborist/lib/version-from-tgz.d.ts.map +1 -0
  701. package/types/lib/third-party/arborist/lib/yarn-lock.d.ts +35 -0
  702. package/types/lib/third-party/arborist/lib/yarn-lock.d.ts.map +1 -0
  703. package/types/lib/validator/bomValidator.d.ts +40 -0
  704. package/types/lib/validator/bomValidator.d.ts.map +1 -0
  705. package/types/lib/validator/complianceEngine.d.ts +72 -0
  706. package/types/lib/validator/complianceEngine.d.ts.map +1 -0
  707. package/types/lib/validator/complianceRules.d.ts +61 -0
  708. package/types/lib/validator/complianceRules.d.ts.map +1 -0
  709. package/types/lib/validator/index.d.ts +101 -0
  710. package/types/lib/validator/index.d.ts.map +1 -0
  711. package/types/lib/validator/reporters/annotations.d.ts +37 -0
  712. package/types/lib/validator/reporters/annotations.d.ts.map +1 -0
  713. package/types/lib/validator/reporters/console.d.ts +34 -0
  714. package/types/lib/validator/reporters/console.d.ts.map +1 -0
  715. package/types/lib/validator/reporters/index.d.ts +30 -0
  716. package/types/lib/validator/reporters/index.d.ts.map +1 -0
  717. package/types/lib/validator/reporters/json.d.ts +11 -0
  718. package/types/lib/validator/reporters/json.d.ts.map +1 -0
  719. package/types/lib/validator/reporters/sarif.d.ts +22 -0
  720. package/types/lib/validator/reporters/sarif.d.ts.map +1 -0
  721. package/mod.d.ts +0 -7
  722. package/mod.js +0 -22
@@ -0,0 +1,2362 @@
1
+ import fs from "node:fs";
2
+ import path, { basename, join, resolve } from "node:path";
3
+ import process from "node:process";
4
+
5
+ import { Purl } from "@cdxgen/cdx-purl";
6
+
7
+ import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
8
+ import { PROJECT_TYPE_ALIASES } from "../core/env.js";
9
+ import {
10
+ getAllFiles,
11
+ getTimestamp,
12
+ getTmpDir,
13
+ safeExistsSync,
14
+ safeMkdtempSync,
15
+ safeRmSync,
16
+ safeWriteSync,
17
+ } from "../core/fs.js";
18
+ import {
19
+ analyzeGolemProject,
20
+ collectGolemEvidence,
21
+ isGolemGoLanguage,
22
+ } from "../ecosystems/golem.js";
23
+ import {
24
+ collectGradleDependencies,
25
+ getGradleCommand,
26
+ getMavenCommand,
27
+ } from "../ecosystems/gradleutils.js";
28
+ import {
29
+ analyzeRusiProject,
30
+ collectRusiEvidence,
31
+ isRusiRustLanguage,
32
+ } from "../ecosystems/rusi.js";
33
+ import {
34
+ buildAtomCommandEnv,
35
+ executeAtom,
36
+ filterAtomSlicesByExcludePatterns,
37
+ } from "../inventory/atomUtils.js";
38
+ import {
39
+ collectDosaiCryptoComponents,
40
+ findCryptoAlgos,
41
+ } from "../inventory/cbomutils.js";
42
+ import { collectMvnDependencies } from "../inventory/deps.js";
43
+ import { mergeServices } from "../inventory/depsUtils.js";
44
+ import {
45
+ collectDosaiDataFlowFrames,
46
+ collectDosaiPurlEvidence,
47
+ collectDosaiServicesFromMethods,
48
+ createDosaiDataFlowSlice,
49
+ createDosaiMethodsSlice,
50
+ isDosaiDotnetLanguage,
51
+ persistDosaiSemanticsReport,
52
+ } from "../inventory/dosai.js";
53
+ import { parseOccurrenceEvidenceLocation } from "../inventory/evidenceUtils.js";
54
+ import { executeOsQuery } from "../managers/binary.js";
55
+ import { postProcess } from "../stages/postgen/postgen.js";
56
+ import { createOrLoad } from "./db.js";
57
+ import { findPurlLocations } from "./scalasem.js";
58
+ import { createSemanticsSlices } from "./swiftsem.js";
59
+
60
+ const typePurlsCache = {};
61
+
62
+ function appendUniqueProperty(properties, name, value) {
63
+ if (value === undefined || value === null || value === "") {
64
+ return;
65
+ }
66
+ const propertyValue = String(value);
67
+ if (
68
+ !properties.some(
69
+ (property) => property.name === name && property.value === propertyValue,
70
+ )
71
+ ) {
72
+ properties.push({ name, value: propertyValue });
73
+ }
74
+ }
75
+
76
+ function appendComponentProperties(component, properties = []) {
77
+ if (!component || !properties.length) {
78
+ return;
79
+ }
80
+ component.properties ??= [];
81
+ for (const property of properties) {
82
+ appendUniqueProperty(component.properties, property.name, property.value);
83
+ }
84
+ }
85
+
86
+ function filterAtomSliceData(sliceData, options = {}) {
87
+ return filterAtomSlicesByExcludePatterns(sliceData, options.exclude);
88
+ }
89
+
90
+ /**
91
+ * Function to create the db for the libraries referred in the sbom.
92
+ *
93
+ * @param {Object} options Command line options
94
+ */
95
+ export async function prepareDB(options) {
96
+ const dirPath = options._[0] || ".";
97
+ const bomJsonFile = options.input;
98
+ if (!safeExistsSync(bomJsonFile)) {
99
+ console.log(
100
+ "Bom file doesn't exist. Check if cdxgen was invoked with the correct type argument.",
101
+ );
102
+ if (!readEnvironmentVariable("CDXGEN_DEBUG_MODE")) {
103
+ console.log(
104
+ "Set the environment variable CDXGEN_DEBUG_MODE to debug to troubleshoot the issue further.",
105
+ );
106
+ }
107
+ return;
108
+ }
109
+ const bomJson = JSON.parse(fs.readFileSync(bomJsonFile, "utf8"));
110
+ if (bomJson.specVersion < 1.5) {
111
+ console.log(
112
+ "Evinse requires the input SBOM in CycloneDX 1.5 format or above. You can generate one by invoking cdxgen without any --spec-version argument.",
113
+ );
114
+ process.exit(0);
115
+ }
116
+ const components = bomJson.components || [];
117
+ const { sequelize, Namespaces, Usages, DataFlows } = await createOrLoad();
118
+ let hasMavenPkgs = false;
119
+ // We need to slice only non-maven packages
120
+ const purlsToSlice = {};
121
+ const purlsJars = {};
122
+ let usagesSlice;
123
+ for (const comp of components) {
124
+ if (!comp.purl) {
125
+ continue;
126
+ }
127
+ usagesSlice = await Usages.findByPk(comp.purl);
128
+ const namespaceSlice = await Namespaces.findByPk(comp.purl);
129
+ if ((!usagesSlice && !namespaceSlice) || options.force) {
130
+ if (comp.purl.startsWith("pkg:maven")) {
131
+ hasMavenPkgs = true;
132
+ }
133
+ }
134
+ }
135
+ // If there are maven packages we collect and store the namespaces
136
+ if (!options.skipMavenCollector && hasMavenPkgs) {
137
+ const pomXmlFiles = getAllFiles(dirPath, "**/" + "pom.xml");
138
+ const gradleFiles = getAllFiles(dirPath, "**/" + "build.gradle*");
139
+ if (pomXmlFiles?.length) {
140
+ await catalogMavenDeps(dirPath, purlsJars, Namespaces, options);
141
+ }
142
+ if (gradleFiles?.length) {
143
+ await catalogGradleDeps(dirPath, purlsJars, Namespaces);
144
+ }
145
+ }
146
+ for (const purl of Object.keys(purlsToSlice)) {
147
+ await createAndStoreSlice(purl, purlsJars, Usages, options);
148
+ }
149
+ return { sequelize, Namespaces, Usages, DataFlows };
150
+ }
151
+
152
+ /**
153
+ * Collect Maven jar namespace mappings for the purls found in the SBOM.
154
+ *
155
+ * If a previously generated `bom.json.map` is present in `dirPath` it is reused;
156
+ * otherwise the maven command is invoked to collect the jar dependencies. Each
157
+ * resolved purl is recorded in the `purlsJars` map and persisted in the
158
+ * `Namespaces` model.
159
+ *
160
+ * @param {string} dirPath Project directory scanned by evinse
161
+ * @param {Object<string, string>} purlsJars Map populated with purl -> jar file path
162
+ * @param {Object} Namespaces Sequelize-like Namespaces model used to persist pom and namespaces
163
+ * @param {object} options CLI options
164
+ * @returns {Promise<void>}
165
+ */
166
+ export async function catalogMavenDeps(
167
+ dirPath,
168
+ purlsJars,
169
+ Namespaces,
170
+ options = {},
171
+ ) {
172
+ let jarNSMapping;
173
+ if (safeExistsSync(join(dirPath, "bom.json.map"))) {
174
+ try {
175
+ const mapData = JSON.parse(
176
+ fs.readFileSync(join(dirPath, "bom.json.map"), "utf-8"),
177
+ );
178
+ if (mapData && Object.keys(mapData).length) {
179
+ jarNSMapping = mapData;
180
+ }
181
+ } catch (_err) {
182
+ // ignore
183
+ }
184
+ }
185
+ if (!jarNSMapping) {
186
+ console.log("About to collect jar dependencies for the path", dirPath);
187
+ const mavenCmd = getMavenCommand(dirPath, dirPath);
188
+ // collect all jars including from the cache if data-flow mode is enabled
189
+ jarNSMapping = await collectMvnDependencies(
190
+ mavenCmd,
191
+ dirPath,
192
+ false,
193
+ options.withDeepJarCollector,
194
+ );
195
+ }
196
+ if (jarNSMapping) {
197
+ for (const purl of Object.keys(jarNSMapping)) {
198
+ purlsJars[purl] = jarNSMapping[purl].jarFile;
199
+ await Namespaces.findOrCreate({
200
+ where: { purl },
201
+ defaults: {
202
+ purl,
203
+ data: JSON.stringify(
204
+ {
205
+ pom: jarNSMapping[purl].pom,
206
+ namespaces: jarNSMapping[purl].namespaces,
207
+ },
208
+ null,
209
+ null,
210
+ ),
211
+ },
212
+ });
213
+ }
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Collect Gradle cache jar namespace mappings for the purls found in the SBOM.
219
+ *
220
+ * Invokes the gradle command to collect all jars (including from the cache) and
221
+ * records each resolved purl in the `purlsJars` map and the `Namespaces` model.
222
+ *
223
+ * @param {string} dirPath Project directory scanned by evinse
224
+ * @param {Object<string, string>} purlsJars Map populated with purl -> jar file path
225
+ * @param {Object} Namespaces Sequelize-like Namespaces model used to persist pom and namespaces
226
+ * @returns {Promise<void>}
227
+ */
228
+ export async function catalogGradleDeps(dirPath, purlsJars, Namespaces) {
229
+ console.log(
230
+ "About to collect jar dependencies from the gradle cache. This would take a while ...",
231
+ );
232
+ const gradleCmd = getGradleCommand(dirPath, dirPath);
233
+ // collect all jars including from the cache if data-flow mode is enabled
234
+ const jarNSMapping = await collectGradleDependencies(
235
+ gradleCmd,
236
+ dirPath,
237
+ false,
238
+ true,
239
+ );
240
+ if (jarNSMapping) {
241
+ for (const purl of Object.keys(jarNSMapping)) {
242
+ purlsJars[purl] = jarNSMapping[purl].jarFile;
243
+ await Namespaces.findOrCreate({
244
+ where: { purl },
245
+ defaults: {
246
+ purl,
247
+ data: JSON.stringify(
248
+ {
249
+ pom: jarNSMapping[purl].pom,
250
+ namespaces: jarNSMapping[purl].namespaces,
251
+ },
252
+ null,
253
+ null,
254
+ ),
255
+ },
256
+ });
257
+ }
258
+ }
259
+ console.log(
260
+ "To speed up successive re-runs, pass the argument --skip-maven-collector to evinse command.",
261
+ );
262
+ }
263
+
264
+ /**
265
+ * Generate a usage slice for the given purl and persist it in the Usages model.
266
+ *
267
+ * Delegates to {@link createSlice} to produce the slice file, then stores the
268
+ * file contents in the `Usages` model keyed by purl. Any temporary directory
269
+ * created under the cdxgen temp root is cleaned up afterwards.
270
+ *
271
+ * @param {string} purl Package URL to slice
272
+ * @param {Object<string, string>} purlsJars Map of purl -> jar/file path
273
+ * @param {Object} Usages Sequelize-like Usages model used to persist slice data
274
+ * @param {object} options CLI options
275
+ * @returns {Promise<Object|undefined>} The created or found Usages record
276
+ */
277
+ export async function createAndStoreSlice(
278
+ purl,
279
+ purlsJars,
280
+ Usages,
281
+ options = {},
282
+ ) {
283
+ const retMap = createSlice(purl, purlsJars[purl], "usages", options);
284
+ let sliceData;
285
+ if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
286
+ sliceData = await Usages.findOrCreate({
287
+ where: { purl },
288
+ defaults: {
289
+ purl,
290
+ data: fs.readFileSync(retMap.slicesFile, "utf-8"),
291
+ },
292
+ });
293
+ }
294
+ if (retMap?.tempDir?.startsWith(getTmpDir())) {
295
+ safeRmSync(retMap.tempDir, { recursive: true, force: true });
296
+ }
297
+ return sliceData;
298
+ }
299
+
300
+ /**
301
+ * Name the option a slice type reads its output path from.
302
+ *
303
+ * The CLI flags are hyphenated (`--data-flow-slices-file`) and yargs hands them
304
+ * over camel-cased, so a slice type containing a hyphen cannot be turned into
305
+ * its option name by concatenation alone.
306
+ *
307
+ * @param {string} sliceType Slice type such as `usages` or `data-flow`
308
+ * @returns {string} Matching option name
309
+ */
310
+ export function sliceFileOption(sliceType) {
311
+ const camelCased = sliceType.replace(/-([a-z])/g, (_match, letter) =>
312
+ letter.toUpperCase(),
313
+ );
314
+ return `${camelCased}SlicesFile`;
315
+ }
316
+
317
+ /**
318
+ * Run atom/sourcekitten/dosai to produce a usage or data-flow slice file for a purl or language.
319
+ *
320
+ * Accepts either a package url (resolved to a language via {@link purlToLanguage})
321
+ * or an explicit language string. The chosen language is normalised to the
322
+ * canonical atom language name and the requested slice type (`usages`,
323
+ * `data-flow`, `reachables`, or `semantics`) is generated into a temporary
324
+ * directory.
325
+ *
326
+ * @param {string|string[]} purlOrLanguages Package URL or language name (or array whose first entry is used)
327
+ * @param {string} filePath Path to the source file, jar, or project directory to slice
328
+ * @param {string} [sliceType="usages"] Slice type: `usages`, `data-flow`, `reachables`, or `semantics`
329
+ * @param {object} [options={}] CLI options controlling slice generation
330
+ * @returns {Promise<Object>} Result map with `slicesFile`, `atomFile`, `openapiSpecFile`, `semanticsSlicesFile`, `tempDir`, and `tempDirOwned`
331
+ */
332
+ export async function createSlice(
333
+ purlOrLanguages,
334
+ filePath,
335
+ sliceType = "usages",
336
+ options = {},
337
+ ) {
338
+ if (!filePath) {
339
+ return {};
340
+ }
341
+ const firstLanguage = Array.isArray(purlOrLanguages)
342
+ ? purlOrLanguages[0]
343
+ : purlOrLanguages;
344
+ let language = firstLanguage.startsWith("pkg:")
345
+ ? purlToLanguage(firstLanguage, filePath)
346
+ : firstLanguage;
347
+ if (!language) {
348
+ return {};
349
+ }
350
+ // Handle language with version types
351
+ if (language.startsWith("ruby")) {
352
+ language = "ruby";
353
+ } else if (language.startsWith("java") && language !== "javascript") {
354
+ language = "java";
355
+ } else if (language.startsWith("node")) {
356
+ language = "js";
357
+ } else if (language.startsWith("python")) {
358
+ language = "python";
359
+ } else if (PROJECT_TYPE_ALIASES.scala.includes(language)) {
360
+ language = "scala";
361
+ } else if (isDosaiDotnetLanguage(language)) {
362
+ language = "csharp";
363
+ }
364
+ if (
365
+ PROJECT_TYPE_ALIASES.swift.includes(language) &&
366
+ sliceType !== "semantics"
367
+ ) {
368
+ return {};
369
+ }
370
+
371
+ let sliceOutputDir = safeMkdtempSync(join(getTmpDir(), `atom-${sliceType}-`));
372
+ let tempDirOwned = true;
373
+ if (options?.output) {
374
+ const resolvedOutputPath = resolve(options.output);
375
+ sliceOutputDir =
376
+ safeExistsSync(resolvedOutputPath) &&
377
+ fs.lstatSync(resolvedOutputPath).isDirectory()
378
+ ? resolvedOutputPath
379
+ : path.dirname(resolvedOutputPath);
380
+ tempDirOwned = false;
381
+ }
382
+ const slicesFile =
383
+ options[sliceFileOption(sliceType)] ||
384
+ join(sliceOutputDir, `${language}-${sliceType}.slices.json`);
385
+ const openapiSpecFile = basename(
386
+ options.openapiSpecFile ||
387
+ readEnvironmentVariable("ATOM_TOOLS_OPENAPI_FILENAME") ||
388
+ join(sliceOutputDir, `${language}-openapi.json`),
389
+ );
390
+ // For some languages such as scala, semantics slices file would get created during usages slicing.
391
+ let semanticsSlicesFile;
392
+ if (sliceType === "semantics") {
393
+ const slicesData = createSemanticsSlices(resolve(filePath), options);
394
+ // Write the semantics slices data
395
+ if (slicesData) {
396
+ safeWriteSync(
397
+ slicesFile,
398
+ JSON.stringify(slicesData, null, options.jsonPretty ? 2 : null),
399
+ );
400
+ }
401
+ return { tempDir: sliceOutputDir, tempDirOwned, slicesFile };
402
+ }
403
+ if (isDosaiDotnetLanguage(language)) {
404
+ console.log(
405
+ `Creating ${sliceType} slice for ${resolve(filePath)} using dosai. Please wait ...`,
406
+ );
407
+ const sliceResult =
408
+ sliceType === "data-flow"
409
+ ? createDosaiDataFlowSlice(
410
+ resolve(filePath),
411
+ resolve(slicesFile),
412
+ options,
413
+ )
414
+ : createDosaiMethodsSlice(
415
+ resolve(filePath),
416
+ resolve(slicesFile),
417
+ options,
418
+ );
419
+ if (!sliceResult) {
420
+ console.warn(
421
+ `Unable to generate ${sliceType} slice using dosai. Check if this is a supported .NET project.`,
422
+ );
423
+ }
424
+ return {
425
+ tempDir: sliceOutputDir,
426
+ tempDirOwned,
427
+ slicesFile,
428
+ };
429
+ }
430
+ console.log(
431
+ `Creating ${sliceType} slice for ${resolve(filePath)}. Please wait ...`,
432
+ );
433
+ const atomFile = join(sliceOutputDir, `${language}-app.atom`);
434
+ let args = [sliceType];
435
+ // Support for crypto slices aka CBOM
436
+ if (sliceType === "reachables" && options.includeCrypto) {
437
+ args.push("--include-crypto");
438
+ }
439
+ if (sliceType === "usages") {
440
+ // Generate OpenAPI specification for endpoints. Needs atom-tools pypi package to be installed.
441
+ args.push("--extract-endpoints");
442
+ if (
443
+ readEnvironmentVariable("CDXGEN_IN_CONTAINER") !== "true" &&
444
+ !readEnvironmentVariable("DEVENV_NIX") &&
445
+ !readEnvironmentVariable("NIX_STORE")
446
+ ) {
447
+ console.log(
448
+ "Use an official cdxgen container image to improve the precision of endpoints detection (for SaaSBOM).",
449
+ );
450
+ }
451
+ if (["ruby", "scala"].includes(language)) {
452
+ args.push("--remove-atom");
453
+ }
454
+ if (["scala"].includes(language)) {
455
+ semanticsSlicesFile = join(
456
+ sliceOutputDir,
457
+ basename(options.semanticsSlicesFile || "semantics.slices.json"),
458
+ );
459
+ }
460
+ }
461
+ args = args.concat([
462
+ "-l",
463
+ language,
464
+ "-o",
465
+ resolve(atomFile),
466
+ "--slice-outfile",
467
+ resolve(slicesFile),
468
+ ]);
469
+ // For projects with several layers, slice depth needs to be increased from the default 7 to 15 or 20
470
+ // This would increase the time but would yield more deeper paths
471
+ if (
472
+ sliceType === "data-flow" &&
473
+ readEnvironmentVariable("ATOM_SLICE_DEPTH")
474
+ ) {
475
+ args.push("--slice-depth");
476
+ args.push(readEnvironmentVariable("ATOM_SLICE_DEPTH"));
477
+ }
478
+ args.push(resolve(filePath));
479
+ const atomExcludeEnv = buildAtomCommandEnv(options, language);
480
+ // Execute atom
481
+ const result = executeAtom(filePath, args, {
482
+ ...atomExcludeEnv,
483
+ ATOM_TOOLS_OPENAPI_FILENAME: openapiSpecFile, // The file would get over-written
484
+ ATOM_TOOLS_OPENAPI_FORMAT:
485
+ readEnvironmentVariable("ATOM_TOOLS_OPENAPI_FORMAT") || "openapi3.1.0", // editor.swagger.io doesn't support 3.1.0 yet
486
+ ATOM_TOOLS_WORK_DIR:
487
+ readEnvironmentVariable("ATOM_TOOLS_WORK_DIR") || resolve(filePath), // This must be the directory containing semantics.slices.json
488
+ OPENAPI_SERVER_URL: readEnvironmentVariable("OPENAPI_SERVER_URL"),
489
+ });
490
+ // A non-zero atom exit no longer discards a slices file that was written:
491
+ // atom can fail a late phase (endpoint extraction, cleanup) after the slices
492
+ // are on disk, and throwing that work away would be a regression against
493
+ // atom 2.x, where the exit status was not observable at all.
494
+ if (!safeExistsSync(slicesFile)) {
495
+ if (!result) {
496
+ console.warn(
497
+ `atom reported a failure and produced no ${sliceType} slice for ${language}.`,
498
+ );
499
+ }
500
+ console.warn(
501
+ `Unable to generate ${sliceType} slice using atom. Check if this is a supported language.`,
502
+ );
503
+ if (!readEnvironmentVariable("CDXGEN_DEBUG_MODE")) {
504
+ console.log(
505
+ "Set the environment variable CDXGEN_DEBUG_MODE=debug to troubleshoot.",
506
+ );
507
+ } else {
508
+ if (readEnvironmentVariable("CDXGEN_IN_CONTAINER") === "true") {
509
+ console.log(
510
+ "TIP: Try creating the slices using the official atom container image `ghcr.io/appthreat/atom:main` directly. Refer to the documentation: https://atom-docs.appthreat.dev/",
511
+ );
512
+ console.log(
513
+ `evinse will automatically reuse any slices matching the name ${slicesFile}`,
514
+ );
515
+ } else {
516
+ console.log(
517
+ `TIP: Try using a cdxgen container image optimized for ${language}. Refer to the documentation or ask cdxgenGPT for the image details.`,
518
+ );
519
+ }
520
+ }
521
+ } else if (
522
+ DEBUG_MODE &&
523
+ sliceType === "usages" &&
524
+ !safeExistsSync(join(filePath, openapiSpecFile))
525
+ ) {
526
+ console.log(
527
+ `openapi spec file "${join(filePath, openapiSpecFile)}" was not generated successfully. Check if atom-tools pypi package is installed and available in PATH.`,
528
+ );
529
+ }
530
+ if (
531
+ ["scala"].includes(language) &&
532
+ sliceType === "usages" &&
533
+ !safeExistsSync(semanticsSlicesFile)
534
+ ) {
535
+ console.log(
536
+ `Semantics slices file "${semanticsSlicesFile}" was not generated successfully. Try running atom cli in Java mode.`,
537
+ );
538
+ }
539
+ return {
540
+ tempDir: sliceOutputDir,
541
+ tempDirOwned,
542
+ slicesFile,
543
+ atomFile,
544
+ openapiSpecFile: resolve(join(filePath, openapiSpecFile)),
545
+ semanticsSlicesFile,
546
+ };
547
+ }
548
+
549
+ /**
550
+ * Map a package URL type to an analysis language (java, python, js, …).
551
+ *
552
+ * @param {string} purl Package URL to inspect
553
+ * @param {string} [filePath] Optional file path used to distinguish jar vs java for maven purls
554
+ * @returns {string|undefined} Resolved language name or `undefined` when the purl type is unsupported
555
+ */
556
+ export function purlToLanguage(purl, filePath) {
557
+ let language;
558
+ const purlObj = Purl.parse(purl);
559
+ switch (purlObj.type) {
560
+ case "maven":
561
+ language = filePath?.endsWith(".jar") ? "jar" : "java";
562
+ break;
563
+ case "npm":
564
+ language = "javascript";
565
+ break;
566
+ case "pypi":
567
+ language = "python";
568
+ break;
569
+ case "composer":
570
+ language = "php";
571
+ break;
572
+ case "gem":
573
+ language = "ruby";
574
+ break;
575
+ case "nuget":
576
+ language = "csharp";
577
+ break;
578
+ case "generic":
579
+ language = "c";
580
+ }
581
+ return language;
582
+ }
583
+
584
+ /**
585
+ * Seed purl-location and import maps from SBOM component evidence.
586
+ *
587
+ * Walks the supplied components reading `internal:ImportedModules` (or
588
+ * `internal:Namespaces` for php/ruby) and `evidence.occurrences` properties
589
+ * to construct lookup maps used during slice analysis.
590
+ *
591
+ * @param {Object[]} components CycloneDX components from the input SBOM
592
+ * @param {string} language Application language used to select the import property name
593
+ * @returns {{ purlLocationMap: Object<string, Set>, purlImportsMap: Object<string, string[]> }} Seeded purl location and import maps
594
+ */
595
+ export function initFromSbom(components, language) {
596
+ const purlLocationMap = {};
597
+ const purlImportsMap = {};
598
+ for (const comp of components) {
599
+ if (!comp?.evidence) {
600
+ continue;
601
+ }
602
+ if (["php", "ruby"].includes(language)) {
603
+ (comp.properties || [])
604
+ .filter((v) => v.name === "internal:Namespaces")
605
+ .forEach((v) => {
606
+ purlImportsMap[comp.purl] = (v.value || "").split(", ");
607
+ });
608
+ } else {
609
+ (comp.properties || [])
610
+ .filter((v) => v.name === "internal:ImportedModules")
611
+ .forEach((v) => {
612
+ purlImportsMap[comp.purl] = (v.value || "").split(",");
613
+ });
614
+ }
615
+ if (comp.evidence.occurrences) {
616
+ purlLocationMap[comp.purl] = new Set(
617
+ comp.evidence.occurrences.map((v) => v.location),
618
+ );
619
+ }
620
+ }
621
+ return {
622
+ purlLocationMap,
623
+ purlImportsMap,
624
+ };
625
+ }
626
+
627
+ function usableSlicesFile(slicesFile) {
628
+ if (!slicesFile || !safeExistsSync(slicesFile)) {
629
+ return false;
630
+ }
631
+ const stats = fs.statSync(slicesFile);
632
+ if (!stats.isFile()) {
633
+ return false;
634
+ }
635
+ const fileSizeInBytes = stats.size;
636
+ return fileSizeInBytes > 1024;
637
+ }
638
+
639
+ /**
640
+ * Function to analyze the project
641
+ *
642
+ * @param {Object} dbObjMap DB and model instances
643
+ * @param {Object} options Command line options
644
+ */
645
+ export async function analyzeProject(dbObjMap, options) {
646
+ const dirPath = options._[0] || ".";
647
+ const languages = options.language;
648
+ const language = Array.isArray(languages) ? languages[0] : languages;
649
+ let usageSlice;
650
+ let dataFlowSlice;
651
+ let reachablesSlice;
652
+ let semanticsSlice;
653
+ let usagesSlicesFile;
654
+ let dataFlowSlicesFile;
655
+ let reachablesSlicesFile;
656
+ let semanticsSlicesFile;
657
+ let dataFlowFrames = {};
658
+ let servicesMap = {};
659
+ let retMap = {};
660
+ let userDefinedTypesMap = {};
661
+ const bomFile = options.input;
662
+ const bomJson = JSON.parse(fs.readFileSync(bomFile, "utf8"));
663
+ const components = bomJson.components || [];
664
+ let cryptoComponents = [];
665
+ let cryptoGeneratePurls = {};
666
+ let openapiSpecFile;
667
+ // Load any existing purl-location information from the sbom.
668
+ // For eg: cdxgen populates this information for javascript projects
669
+ let { purlLocationMap, purlImportsMap } = initFromSbom(components, language);
670
+
671
+ if (isGolemGoLanguage(language)) {
672
+ const golemReport = analyzeGolemProject(dirPath, options);
673
+ if (golemReport && Object.keys(golemReport).length) {
674
+ const golemEvidence = collectGolemEvidence(golemReport, [
675
+ bomJson.metadata?.component,
676
+ ...components,
677
+ ]);
678
+ return {
679
+ purlLocationMap: golemEvidence.purlLocationMap,
680
+ dataFlowFrames: golemEvidence.dataFlowFrames,
681
+ componentPropertiesMap: golemEvidence.componentPropertiesMap,
682
+ metadataProperties: golemEvidence.metadataProperties,
683
+ cryptoComponents: golemEvidence.cryptoComponents,
684
+ cryptoGeneratePurls: golemEvidence.cryptoGeneratePurls,
685
+ servicesMap,
686
+ userDefinedTypesMap,
687
+ };
688
+ }
689
+ return {
690
+ purlLocationMap,
691
+ servicesMap,
692
+ dataFlowFrames,
693
+ userDefinedTypesMap,
694
+ };
695
+ }
696
+
697
+ if (isRusiRustLanguage(language)) {
698
+ const rusiReport = analyzeRusiProject(dirPath, options);
699
+ if (rusiReport && Object.keys(rusiReport).length) {
700
+ const rusiEvidence = collectRusiEvidence(rusiReport, [
701
+ bomJson.metadata?.component,
702
+ ...components,
703
+ ]);
704
+ return {
705
+ purlLocationMap: rusiEvidence.purlLocationMap,
706
+ dataFlowFrames: rusiEvidence.dataFlowFrames,
707
+ componentPropertiesMap: rusiEvidence.componentPropertiesMap,
708
+ metadataProperties: rusiEvidence.metadataProperties,
709
+ cryptoComponents: rusiEvidence.cryptoComponents,
710
+ cryptoGeneratePurls: rusiEvidence.cryptoGeneratePurls,
711
+ servicesMap,
712
+ userDefinedTypesMap,
713
+ };
714
+ }
715
+ return {
716
+ purlLocationMap,
717
+ servicesMap,
718
+ dataFlowFrames,
719
+ userDefinedTypesMap,
720
+ };
721
+ }
722
+
723
+ if (isDosaiDotnetLanguage(language)) {
724
+ if (options.profile === "research") {
725
+ options.withDataFlow = true;
726
+ options.includeCrypto = true;
727
+ }
728
+ if (
729
+ options.usagesSlicesFile &&
730
+ usableSlicesFile(options.usagesSlicesFile)
731
+ ) {
732
+ usageSlice = JSON.parse(
733
+ fs.readFileSync(options.usagesSlicesFile, "utf-8"),
734
+ );
735
+ usagesSlicesFile = options.usagesSlicesFile;
736
+ } else {
737
+ retMap = await createSlice(language, dirPath, "usages", options);
738
+ if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
739
+ usageSlice = JSON.parse(fs.readFileSync(retMap.slicesFile, "utf-8"));
740
+ usagesSlicesFile = retMap.slicesFile;
741
+ }
742
+ }
743
+ if (usageSlice && Object.keys(usageSlice).length) {
744
+ const dosaiEvidence = collectDosaiPurlEvidence(usageSlice, components);
745
+ for (const [purl, locations] of Object.entries(
746
+ dosaiEvidence.purlLocationMap,
747
+ )) {
748
+ purlLocationMap[purl] ??= new Set();
749
+ for (const location of locations) {
750
+ purlLocationMap[purl].add(location);
751
+ }
752
+ }
753
+ servicesMap = collectDosaiServicesFromMethods(usageSlice, servicesMap);
754
+ userDefinedTypesMap = {};
755
+ }
756
+ if (options.withDataFlow) {
757
+ if (
758
+ options.dataFlowSlicesFile &&
759
+ safeExistsSync(options.dataFlowSlicesFile)
760
+ ) {
761
+ dataFlowSlicesFile = options.dataFlowSlicesFile;
762
+ dataFlowSlice = JSON.parse(
763
+ fs.readFileSync(options.dataFlowSlicesFile, "utf-8"),
764
+ );
765
+ } else {
766
+ retMap = await createSlice(language, dirPath, "data-flow", options);
767
+ if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
768
+ dataFlowSlicesFile = retMap.slicesFile;
769
+ dataFlowSlice = JSON.parse(
770
+ fs.readFileSync(retMap.slicesFile, "utf-8"),
771
+ );
772
+ }
773
+ }
774
+ if (dataFlowSlice && Object.keys(dataFlowSlice).length) {
775
+ dataFlowFrames = collectDosaiDataFlowFrames(dataFlowSlice, components);
776
+ }
777
+ }
778
+ if (options.includeCrypto) {
779
+ cryptoComponents = await collectDosaiCryptoComponents(dirPath, options);
780
+ }
781
+ // Persist the combined native dosai report (methods + dataflows) to the
782
+ // semantics-slices path when one was provided (--semantics-slices-file).
783
+ // Mirrors analyzeRusiProject/analyzeGolemProject on feat/rusi-persist-report:
784
+ // dotnet does not otherwise use the semantics slice (atom is never run for
785
+ // dotnet), so the path is free to carry the full native dosai report that
786
+ // downstream tools (depscan) consume as the source of truth. Only the full
787
+ // native report is persisted; the existing SBOM evidence projection below
788
+ // is unchanged.
789
+ semanticsSlicesFile = persistDosaiSemanticsReport(
790
+ options,
791
+ usageSlice,
792
+ dataFlowSlice,
793
+ );
794
+ return {
795
+ usagesSlicesFile,
796
+ dataFlowSlicesFile,
797
+ semanticsSlicesFile,
798
+ purlLocationMap,
799
+ servicesMap,
800
+ dataFlowFrames,
801
+ tempDir: retMap?.tempDir,
802
+ tempDirOwned: retMap?.tempDirOwned,
803
+ userDefinedTypesMap,
804
+ cryptoComponents,
805
+ cryptoGeneratePurls,
806
+ };
807
+ }
808
+ // Do reachables first so that usages slicing can reuse the atom file
809
+ // We need reachables slicing even when trying to infer crypto packages
810
+ if (options.withReachables || options.includeCrypto) {
811
+ if (
812
+ options.reachablesSlicesFile &&
813
+ usableSlicesFile(options.reachablesSlicesFile)
814
+ ) {
815
+ reachablesSlicesFile = options.reachablesSlicesFile;
816
+ reachablesSlice = filterAtomSliceData(
817
+ JSON.parse(fs.readFileSync(options.reachablesSlicesFile, "utf-8")),
818
+ options,
819
+ );
820
+ } else {
821
+ retMap = await createSlice(language, dirPath, "reachables", options);
822
+ if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
823
+ reachablesSlicesFile = retMap.slicesFile;
824
+ reachablesSlice = filterAtomSliceData(
825
+ JSON.parse(fs.readFileSync(retMap.slicesFile, "utf-8")),
826
+ options,
827
+ );
828
+ }
829
+ }
830
+ }
831
+ if (reachablesSlice && Object.keys(reachablesSlice).length) {
832
+ const retMap = collectReachableFrames(language, reachablesSlice);
833
+ dataFlowFrames = retMap.dataFlowFrames;
834
+ cryptoComponents = retMap.cryptoComponents;
835
+ cryptoGeneratePurls = retMap.cryptoGeneratePurls;
836
+ }
837
+ // Reuse existing usages slices
838
+ if (options.usagesSlicesFile && usableSlicesFile(options.usagesSlicesFile)) {
839
+ usageSlice = filterAtomSliceData(
840
+ JSON.parse(fs.readFileSync(options.usagesSlicesFile, "utf-8")),
841
+ options,
842
+ );
843
+ usagesSlicesFile = options.usagesSlicesFile;
844
+ } else {
845
+ // Generate our own slices
846
+ retMap = await createSlice(language, dirPath, "usages", options);
847
+ if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
848
+ usageSlice = filterAtomSliceData(
849
+ JSON.parse(fs.readFileSync(retMap.slicesFile, "utf-8")),
850
+ options,
851
+ );
852
+ usagesSlicesFile = retMap.slicesFile;
853
+ }
854
+ if (retMap?.semanticsSlicesFile) {
855
+ options.semanticsSlicesFile = retMap.semanticsSlicesFile;
856
+ semanticsSlicesFile = retMap.semanticsSlicesFile;
857
+ if (DEBUG_MODE) {
858
+ console.log(
859
+ `Reusing the generated semantics slices file "${semanticsSlicesFile}".`,
860
+ );
861
+ }
862
+ }
863
+ if (retMap.openapiSpecFile) {
864
+ // Retain any generated openapi spec file
865
+ openapiSpecFile = retMap.openapiSpecFile;
866
+ }
867
+ }
868
+ // Support for semantics slicing
869
+ if (
870
+ (PROJECT_TYPE_ALIASES.swift.includes(language) ||
871
+ PROJECT_TYPE_ALIASES.scala.includes(language)) &&
872
+ components.length
873
+ ) {
874
+ // Reuse existing semantics slices for swift and scala
875
+ if (
876
+ options.semanticsSlicesFile &&
877
+ safeExistsSync(options.semanticsSlicesFile)
878
+ ) {
879
+ semanticsSlice = JSON.parse(
880
+ fs.readFileSync(options.semanticsSlicesFile, "utf-8"),
881
+ );
882
+ semanticsSlicesFile = options.semanticsSlicesFile;
883
+ } else if (PROJECT_TYPE_ALIASES.swift.includes(language)) {
884
+ // Generate our own slices for swift
885
+ retMap = await createSlice(language, dirPath, "semantics", options);
886
+ if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
887
+ semanticsSlice = JSON.parse(
888
+ fs.readFileSync(retMap.slicesFile, "utf-8"),
889
+ );
890
+ semanticsSlicesFile = retMap.slicesFile;
891
+ }
892
+ }
893
+ }
894
+ // Parse usage slices
895
+ if (usageSlice && Object.keys(usageSlice).length) {
896
+ const retMap = await parseObjectSlices(
897
+ language,
898
+ usageSlice,
899
+ dbObjMap,
900
+ servicesMap,
901
+ purlLocationMap,
902
+ purlImportsMap,
903
+ openapiSpecFile,
904
+ );
905
+ purlLocationMap = retMap.purlLocationMap;
906
+ servicesMap = retMap.servicesMap;
907
+ userDefinedTypesMap = retMap.userDefinedTypesMap;
908
+ }
909
+ // Parse the semantics slices for swift and scala
910
+ if (
911
+ semanticsSlice &&
912
+ Object.keys(semanticsSlice).length &&
913
+ components.length
914
+ ) {
915
+ // Identify the purl locations
916
+ const retMap = parseSemanticSlices(language, components, semanticsSlice);
917
+ purlLocationMap = retMap.purlLocationMap;
918
+ }
919
+ if (options.withDataFlow) {
920
+ if (
921
+ options.dataFlowSlicesFile &&
922
+ safeExistsSync(options.dataFlowSlicesFile)
923
+ ) {
924
+ dataFlowSlicesFile = options.dataFlowSlicesFile;
925
+ dataFlowSlice = filterAtomSliceData(
926
+ JSON.parse(fs.readFileSync(options.dataFlowSlicesFile, "utf-8")),
927
+ options,
928
+ );
929
+ } else if (!PROJECT_TYPE_ALIASES.scala.includes(language)) {
930
+ retMap = await createSlice(language, dirPath, "data-flow", options);
931
+ if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
932
+ dataFlowSlicesFile = retMap.slicesFile;
933
+ dataFlowSlice = filterAtomSliceData(
934
+ JSON.parse(fs.readFileSync(retMap.slicesFile, "utf-8")),
935
+ options,
936
+ );
937
+ }
938
+ }
939
+ }
940
+ if (dataFlowSlice && Object.keys(dataFlowSlice).length) {
941
+ dataFlowFrames = await collectDataFlowFrames(
942
+ language,
943
+ userDefinedTypesMap,
944
+ dataFlowSlice,
945
+ dbObjMap,
946
+ purlLocationMap,
947
+ purlImportsMap,
948
+ );
949
+ }
950
+ return {
951
+ atomFile: retMap?.atomFile,
952
+ usagesSlicesFile,
953
+ dataFlowSlicesFile,
954
+ reachablesSlicesFile,
955
+ semanticsSlicesFile,
956
+ purlLocationMap,
957
+ servicesMap,
958
+ dataFlowFrames,
959
+ tempDir: retMap?.tempDir,
960
+ tempDirOwned: retMap?.tempDirOwned,
961
+ userDefinedTypesMap,
962
+ cryptoComponents,
963
+ cryptoGeneratePurls,
964
+ openapiSpecFile,
965
+ };
966
+ }
967
+
968
+ /**
969
+ * Parse atom object slices into usages, user-defined types, services, and purl-location evidence.
970
+ *
971
+ * Iterates over the `objectSlices` and `userDefinedTypes` of an atom usage slice,
972
+ * delegating to {@link parseSliceUsages} to record purl locations and imports.
973
+ * Services are detected first from an OpenAPI spec (when present) and then from
974
+ * usage slices or user-defined types as fallbacks.
975
+ *
976
+ * @param {string} language Application language
977
+ * @param {Object} usageSlice Parsed atom usage slice object
978
+ * @param {Object} dbObjMap DB models handle containing Namespaces and Usages models
979
+ * @param {Object} [servicesMap={}] Map populated with detected service definitions
980
+ * @param {Object<string, Set>} [purlLocationMap={}] Map populated with purl -> source locations
981
+ * @param {Object<string, string[]>} [purlImportsMap={}] Map populated with purl -> imported modules
982
+ * @param {string} [openapiSpecFile=undefined] Optional path to an OpenAPI spec used for service detection
983
+ * @returns {Promise<Object>} Map with `purlLocationMap`, `servicesMap`, and `userDefinedTypesMap`
984
+ */
985
+ export async function parseObjectSlices(
986
+ language,
987
+ usageSlice,
988
+ dbObjMap,
989
+ servicesMap = {},
990
+ purlLocationMap = {},
991
+ purlImportsMap = {},
992
+ openapiSpecFile = undefined,
993
+ ) {
994
+ let openapiServicesMode = false;
995
+ if (!usageSlice || !Object.keys(usageSlice).length) {
996
+ return purlLocationMap;
997
+ }
998
+ const userDefinedTypesMap = {};
999
+ (usageSlice.userDefinedTypes || []).forEach((ut) => {
1000
+ userDefinedTypesMap[ut.name] = true;
1001
+ });
1002
+ for (const slice of [
1003
+ ...(usageSlice.objectSlices || []),
1004
+ ...(usageSlice.userDefinedTypes || []),
1005
+ ]) {
1006
+ // Skip the library code typically without filename
1007
+ if (
1008
+ !slice.fileName?.trim().length ||
1009
+ slice.fileName === "<empty>" ||
1010
+ slice.fileName === "<unknown>"
1011
+ ) {
1012
+ continue;
1013
+ }
1014
+ await parseSliceUsages(
1015
+ language,
1016
+ userDefinedTypesMap,
1017
+ slice,
1018
+ dbObjMap,
1019
+ purlLocationMap,
1020
+ purlImportsMap,
1021
+ );
1022
+ // Prefer openapi for identifying services
1023
+ if (
1024
+ !openapiServicesMode &&
1025
+ openapiSpecFile &&
1026
+ safeExistsSync(openapiSpecFile)
1027
+ ) {
1028
+ detectServicesFromOpenAPI(language, openapiSpecFile, servicesMap);
1029
+ if (servicesMap && Object.keys(servicesMap).length) {
1030
+ openapiServicesMode = true;
1031
+ }
1032
+ }
1033
+ // Only identify services from usage slices as a fallback
1034
+ if (!openapiServicesMode) {
1035
+ detectServicesFromUsages(language, slice, servicesMap);
1036
+ }
1037
+ }
1038
+ // Only identify services from user defined types as a second fallback
1039
+ if (!openapiServicesMode) {
1040
+ detectServicesFromUDT(language, usageSlice.userDefinedTypes, servicesMap);
1041
+ }
1042
+ return {
1043
+ purlLocationMap,
1044
+ servicesMap,
1045
+ userDefinedTypesMap,
1046
+ };
1047
+ }
1048
+
1049
+ /**
1050
+ * The implementation of this function is based on the logic proposed in the atom slices specification
1051
+ * https://github.com/AppThreat/atom/blob/main/specification/docs/slices.md#use
1052
+ *
1053
+ * @param {string} language Application language
1054
+ * @param {Object} userDefinedTypesMap User Defined types in the application
1055
+ * @param {Array} slice Usages array for each objectSlice
1056
+ * @param {Object} dbObjMap DB Models
1057
+ * @param {Object} purlLocationMap Object to track locations where purls are used
1058
+ * @param {Object} purlImportsMap Object to track package urls and their import aliases
1059
+ * @returns
1060
+ */
1061
+ export async function parseSliceUsages(
1062
+ language,
1063
+ userDefinedTypesMap,
1064
+ slice,
1065
+ dbObjMap,
1066
+ purlLocationMap,
1067
+ purlImportsMap,
1068
+ ) {
1069
+ const fileName = slice.fileName;
1070
+ const typesToLookup = new Set();
1071
+ const lKeyOverrides = {};
1072
+ const usages = slice.usages || [];
1073
+ // What should be the line number to use. slice.lineNumber would be quite coarse and could lead to reports such as
1074
+ // #1670. Line numbers under targetObj and definedBy is a safe bet for dynamic languages, but occassionally leads to
1075
+ // confusion when inter-procedural tracking works better than expected.
1076
+ let sliceLineNumber;
1077
+ if (["java", "jar"].includes(language)) {
1078
+ sliceLineNumber = slice.lineNumber;
1079
+ }
1080
+ // Annotations from usages
1081
+ if (slice.signature?.startsWith("@") && !usages.length) {
1082
+ typesToLookup.add(slice.fullName);
1083
+ addToOverrides(lKeyOverrides, slice.fullName, fileName, slice.lineNumber);
1084
+ }
1085
+ // PHP imports from usages
1086
+ if (slice.code?.startsWith("use") && !usages.length) {
1087
+ typesToLookup.add(slice.fullName);
1088
+ addToOverrides(lKeyOverrides, slice.fullName, fileName, slice.lineNumber);
1089
+ }
1090
+ for (const ausage of usages) {
1091
+ const ausageLine =
1092
+ sliceLineNumber ||
1093
+ ausage?.targetObj?.lineNumber ||
1094
+ ausage?.definedBy?.lineNumber;
1095
+ // First capture the types in the targetObj and definedBy
1096
+ for (const atype of [
1097
+ [ausage?.targetObj?.isExternal, ausage?.targetObj?.typeFullName],
1098
+ [ausage?.targetObj?.isExternal, ausage?.targetObj?.resolvedMethod],
1099
+ [ausage?.definedBy?.name?.includes("::"), ausage?.definedBy?.name],
1100
+ [ausage?.definedBy?.isExternal, ausage?.definedBy?.typeFullName],
1101
+ [ausage?.definedBy?.isExternal, ausage?.definedBy?.resolvedMethod],
1102
+ ...(ausage?.fields || []).map((f) => [f?.isExternal, f?.typeFullName]),
1103
+ ]) {
1104
+ if (
1105
+ !atype[0] &&
1106
+ (!atype[1] || ["ANY", "(...)", "<empty>"].includes(atype[1]))
1107
+ ) {
1108
+ continue;
1109
+ }
1110
+ if (
1111
+ atype[0] !== false &&
1112
+ !isFilterableType(language, userDefinedTypesMap, atype[1])
1113
+ ) {
1114
+ if (!atype[1].includes("(") && !atype[1].includes(".py")) {
1115
+ typesToLookup.add(simplifyType(atype[1]));
1116
+ // Javascript and Ruby calls can be resolved to a precise line number only from the call nodes
1117
+ if (
1118
+ ["javascript", "js", "ts", "typescript", "ruby"].includes(
1119
+ language,
1120
+ ) &&
1121
+ ausageLine
1122
+ ) {
1123
+ if (atype[1].includes(":")) {
1124
+ typesToLookup.add(
1125
+ simplifyType(atype[1].split("::")[0].replace(/:/g, "/")),
1126
+ );
1127
+ }
1128
+ addToOverrides(lKeyOverrides, atype[1], fileName, ausageLine);
1129
+ }
1130
+ }
1131
+ const maybeClassType = getClassTypeFromSignature(language, atype[1]);
1132
+ typesToLookup.add(maybeClassType);
1133
+ if (ausageLine) {
1134
+ addToOverrides(lKeyOverrides, maybeClassType, fileName, ausageLine);
1135
+ }
1136
+ }
1137
+ }
1138
+ // Now capture full method signatures from invokedCalls, argToCalls including the paramtypes
1139
+ for (const acall of []
1140
+ .concat(ausage?.invokedCalls || [])
1141
+ .concat(ausage?.argToCalls || [])
1142
+ .concat(ausage?.procedures || [])) {
1143
+ if (
1144
+ acall.resolvedMethod?.startsWith("@") ||
1145
+ acall?.callName?.includes("::")
1146
+ ) {
1147
+ typesToLookup.add(acall.callName);
1148
+ if (acall.lineNumber) {
1149
+ addToOverrides(
1150
+ lKeyOverrides,
1151
+ acall.callName,
1152
+ fileName,
1153
+ acall.lineNumber,
1154
+ );
1155
+ }
1156
+ } else if (acall.isExternal === false) {
1157
+ continue;
1158
+ }
1159
+ if (
1160
+ !isFilterableType(language, userDefinedTypesMap, acall?.resolvedMethod)
1161
+ ) {
1162
+ if (
1163
+ !acall?.resolvedMethod.includes("(") &&
1164
+ !acall?.resolvedMethod.includes(".py")
1165
+ ) {
1166
+ typesToLookup.add(simplifyType(acall?.resolvedMethod));
1167
+ // Javascript calls can be resolved to a precise line number only from the call nodes
1168
+ if (acall.lineNumber) {
1169
+ addToOverrides(
1170
+ lKeyOverrides,
1171
+ acall?.resolvedMethod,
1172
+ fileName,
1173
+ acall.lineNumber,
1174
+ );
1175
+ }
1176
+ }
1177
+ const maybeClassType = getClassTypeFromSignature(
1178
+ language,
1179
+ acall?.resolvedMethod,
1180
+ );
1181
+ typesToLookup.add(maybeClassType);
1182
+ if (acall.lineNumber) {
1183
+ addToOverrides(
1184
+ lKeyOverrides,
1185
+ maybeClassType,
1186
+ fileName,
1187
+ acall.lineNumber,
1188
+ );
1189
+ }
1190
+ }
1191
+ for (const aparamType of acall?.paramTypes || []) {
1192
+ if (!isFilterableType(language, userDefinedTypesMap, aparamType)) {
1193
+ if (!aparamType.includes("(") && !aparamType.includes(".py")) {
1194
+ typesToLookup.add(simplifyType(aparamType));
1195
+ if (acall.lineNumber) {
1196
+ if (aparamType.includes(":")) {
1197
+ typesToLookup.add(
1198
+ simplifyType(aparamType.split("::")[0].replace(/:/g, "/")),
1199
+ );
1200
+ }
1201
+ addToOverrides(
1202
+ lKeyOverrides,
1203
+ aparamType,
1204
+ fileName,
1205
+ acall.lineNumber,
1206
+ );
1207
+ }
1208
+ }
1209
+ const maybeClassType = getClassTypeFromSignature(
1210
+ language,
1211
+ aparamType,
1212
+ );
1213
+ typesToLookup.add(maybeClassType);
1214
+ if (acall.lineNumber) {
1215
+ addToOverrides(
1216
+ lKeyOverrides,
1217
+ maybeClassType,
1218
+ fileName,
1219
+ acall.lineNumber,
1220
+ );
1221
+ }
1222
+ }
1223
+ }
1224
+ }
1225
+ }
1226
+ for (const atype of typesToLookup) {
1227
+ if (isFilterableType(language, userDefinedTypesMap, atype)) {
1228
+ continue;
1229
+ }
1230
+ if (purlImportsMap && Object.keys(purlImportsMap).length) {
1231
+ for (const apurl of Object.keys(purlImportsMap)) {
1232
+ const apurlImports = purlImportsMap[apurl];
1233
+ if (["php", "python", "ruby"].includes(language)) {
1234
+ for (const aimp of apurlImports) {
1235
+ if (
1236
+ atype.startsWith(aimp) ||
1237
+ (language === "ruby" && aimp.startsWith(atype))
1238
+ ) {
1239
+ if (!purlLocationMap[apurl]) {
1240
+ purlLocationMap[apurl] = new Set();
1241
+ }
1242
+ if (lKeyOverrides[atype]) {
1243
+ purlLocationMap[apurl].add(...lKeyOverrides[atype]);
1244
+ }
1245
+ }
1246
+ }
1247
+ } else {
1248
+ if (apurlImports?.includes(atype)) {
1249
+ if (!purlLocationMap[apurl]) {
1250
+ purlLocationMap[apurl] = new Set();
1251
+ }
1252
+ if (lKeyOverrides[atype]) {
1253
+ purlLocationMap[apurl].add(...lKeyOverrides[atype]);
1254
+ }
1255
+ }
1256
+ }
1257
+ }
1258
+ } else {
1259
+ // Check the namespaces db
1260
+ let nsHits = typePurlsCache[atype];
1261
+ if (!nsHits && ["java", "jar"].includes(language)) {
1262
+ nsHits = await dbObjMap.Namespaces.findAll({
1263
+ attributes: ["purl"],
1264
+ where: {
1265
+ data: {
1266
+ like: `%${atype}%`,
1267
+ },
1268
+ },
1269
+ });
1270
+ }
1271
+ if (nsHits?.length) {
1272
+ for (const ns of nsHits) {
1273
+ if (!purlLocationMap[ns.purl]) {
1274
+ purlLocationMap[ns.purl] = new Set();
1275
+ }
1276
+ if (lKeyOverrides[atype]) {
1277
+ purlLocationMap[ns.purl].add(...lKeyOverrides[atype]);
1278
+ }
1279
+ }
1280
+ typePurlsCache[atype] = nsHits;
1281
+ } else {
1282
+ // Avoid persistent lookups
1283
+ typePurlsCache[atype] = [];
1284
+ }
1285
+ }
1286
+ }
1287
+ }
1288
+
1289
+ /**
1290
+ * Method to parse semantic slice data. Currently supported for swift and scala languages.
1291
+ *
1292
+ * @param {String} language Project language.
1293
+ * @param {Array} components Components from the input SBOM
1294
+ * @param {Object} semanticsSlice Semantic slice data
1295
+ * @returns {Object} Parsed metadata
1296
+ */
1297
+ export function parseSemanticSlices(language, components, semanticsSlice) {
1298
+ // For scala, use the dedicated scalasem module.
1299
+ if (language === "scala") {
1300
+ return findPurlLocations(components, semanticsSlice);
1301
+ }
1302
+ if (!components) {
1303
+ return undefined;
1304
+ }
1305
+ const componentNamePurlMap = {};
1306
+ const componentSymbolsMap = {};
1307
+ const allObfuscationsMap = {};
1308
+ // We have two attributes in the semantics slice to expand a given module to its constituent symbols
1309
+ // - A less precise buildSymbols, which is obtained by parsing the various output-file-map.json files
1310
+ // - A granular and precise moduleInfos, which has the exact classes, protocols, enums etc belonging to each module
1311
+ // The objective then is to build the purlLocationMap, which is the list of locations where a given purl is used
1312
+ // For this, we have two attributes:
1313
+ // - A simpler fileStructures attribute that has the mapping between a swift file and the list of referenced types
1314
+ // - A granular fileIndexes attribute that contains information about the clang modules and line number information
1315
+
1316
+ // We first need to map out the component names to their purls
1317
+ // This is because the semantics slice use the module names everywhere
1318
+ for (const comp of components) {
1319
+ componentNamePurlMap[comp.name] = comp.purl;
1320
+ if (!componentSymbolsMap[comp.name]) {
1321
+ componentSymbolsMap[comp.name] = new Set();
1322
+ }
1323
+ if (semanticsSlice?.buildSymbols[comp.name]) {
1324
+ for (const asym of semanticsSlice.buildSymbols[comp.name]) {
1325
+ componentSymbolsMap[comp.name].add(asym);
1326
+ }
1327
+ }
1328
+ const moduleInfo = semanticsSlice?.moduleInfos[comp.name] || {};
1329
+ if (moduleInfo.classes) {
1330
+ for (const asym of moduleInfo.classes) {
1331
+ componentSymbolsMap[comp.name].add(asym);
1332
+ }
1333
+ }
1334
+ if (moduleInfo.protocols) {
1335
+ for (const asym of moduleInfo.protocols) {
1336
+ componentSymbolsMap[comp.name].add(asym);
1337
+ }
1338
+ }
1339
+ if (moduleInfo.enums) {
1340
+ for (const asym of moduleInfo.enums) {
1341
+ componentSymbolsMap[comp.name].add(asym);
1342
+ }
1343
+ }
1344
+ // Now collect the method signatures from class and protocol methods
1345
+ if (moduleInfo.classMethods) {
1346
+ for (const aclassName of Object.keys(moduleInfo.classMethods)) {
1347
+ for (const asym of moduleInfo.classMethods[aclassName]) {
1348
+ componentSymbolsMap[comp.name].add(asym);
1349
+ }
1350
+ }
1351
+ }
1352
+ if (moduleInfo.protocolMethods) {
1353
+ for (const aclassName of Object.keys(moduleInfo.protocolMethods)) {
1354
+ for (const asym of moduleInfo.protocolMethods[aclassName]) {
1355
+ componentSymbolsMap[comp.name].add(asym);
1356
+ }
1357
+ }
1358
+ }
1359
+ // Build a large obfuscation map
1360
+ if (moduleInfo.obfuscationMap) {
1361
+ for (const asym of Object.keys(moduleInfo.obfuscationMap)) {
1362
+ allObfuscationsMap[asym] = moduleInfo.obfuscationMap[asym];
1363
+ }
1364
+ }
1365
+ }
1366
+ const purlLocationsSet = {};
1367
+ // We now have a good set of data in componentSymbolsMap and allObfuscationsMap
1368
+ // We can iterate these symbols and check if they exist under fileIndexes.symbolLocations
1369
+ for (const compName of Object.keys(componentSymbolsMap)) {
1370
+ const compSymbols = Array.from(componentSymbolsMap[compName]);
1371
+ const searchHits = searchSymbolLocations(
1372
+ compSymbols,
1373
+ semanticsSlice?.fileIndexes,
1374
+ );
1375
+ // We have an occurrence hit. Let's populate the purlLocationMap
1376
+ if (searchHits?.length) {
1377
+ const locations =
1378
+ purlLocationsSet[componentNamePurlMap[compName]] || new Set();
1379
+ for (const ahit of searchHits) {
1380
+ for (const aline of ahit.lineNumbers) {
1381
+ locations.add(`${ahit.file}#${aline}`);
1382
+ }
1383
+ }
1384
+ purlLocationsSet[componentNamePurlMap[compName]] = locations;
1385
+ }
1386
+ }
1387
+ const purlLocationMap = {};
1388
+ for (const apurl of Object.keys(purlLocationsSet)) {
1389
+ purlLocationMap[apurl] = Array.from(purlLocationsSet[apurl]).sort();
1390
+ }
1391
+ return { purlLocationMap };
1392
+ }
1393
+
1394
+ function searchSymbolLocations(compSymbols, fileIndexes) {
1395
+ const searchHits = [];
1396
+ if (!fileIndexes) {
1397
+ return undefined;
1398
+ }
1399
+ for (const aswiftFile of Object.keys(fileIndexes)) {
1400
+ if (!fileIndexes[aswiftFile].symbolLocations) {
1401
+ continue;
1402
+ }
1403
+ for (const asym of Object.keys(fileIndexes[aswiftFile].symbolLocations)) {
1404
+ const lineNumbers = fileIndexes[aswiftFile].symbolLocations[asym];
1405
+ if (compSymbols.includes(asym)) {
1406
+ searchHits.push({
1407
+ file: aswiftFile,
1408
+ symbol: asym,
1409
+ lineNumbers,
1410
+ });
1411
+ }
1412
+ }
1413
+ }
1414
+ return searchHits;
1415
+ }
1416
+
1417
+ /**
1418
+ * Decide whether a type name should be ignored during slice analysis.
1419
+ *
1420
+ * Returns `true` for placeholder/builtin/unresolved names and for language
1421
+ * specific primitives and JDK/runtime packages. User-defined types recorded in
1422
+ * `userDefinedTypesMap` are also considered filterable.
1423
+ *
1424
+ * @param {string} language Application language
1425
+ * @param {Object<string, boolean>} userDefinedTypesMap Known user-defined type names
1426
+ * @param {string} typeFullName Full type name to test
1427
+ * @returns {boolean} `true` when the type should be filtered out
1428
+ */
1429
+ export function isFilterableType(language, userDefinedTypesMap, typeFullName) {
1430
+ if (
1431
+ !typeFullName ||
1432
+ ["ANY", "UNKNOWN", "VOID", "IMPORT"].includes(typeFullName.toUpperCase())
1433
+ ) {
1434
+ return true;
1435
+ }
1436
+ for (const ab of [
1437
+ "<operator",
1438
+ "<unresolved",
1439
+ "<unknownFullName",
1440
+ "__builtin",
1441
+ "LAMBDA",
1442
+ "../",
1443
+ ]) {
1444
+ if (typeFullName.startsWith(ab)) {
1445
+ return true;
1446
+ }
1447
+ }
1448
+ if (language && ["java", "jar"].includes(language)) {
1449
+ if (
1450
+ !typeFullName.includes(".") ||
1451
+ typeFullName.startsWith("@") ||
1452
+ typeFullName.startsWith("java.") ||
1453
+ typeFullName.startsWith("sun.") ||
1454
+ typeFullName.startsWith("jdk.") ||
1455
+ typeFullName.startsWith("org.w3c.") ||
1456
+ typeFullName.startsWith("org.xml.") ||
1457
+ typeFullName.startsWith("javax.xml.")
1458
+ ) {
1459
+ return true;
1460
+ }
1461
+ }
1462
+ if (["javascript", "js", "ts", "typescript"].includes(language)) {
1463
+ if (
1464
+ typeFullName.includes(".js") ||
1465
+ typeFullName.includes("=>") ||
1466
+ typeFullName.startsWith("__") ||
1467
+ typeFullName.startsWith("{ ") ||
1468
+ typeFullName.startsWith("JSON") ||
1469
+ typeFullName.startsWith("void:") ||
1470
+ typeFullName.startsWith("node:")
1471
+ ) {
1472
+ return true;
1473
+ }
1474
+ }
1475
+ if (["python", "py"].includes(language)) {
1476
+ if (
1477
+ typeFullName.startsWith("tmp") ||
1478
+ typeFullName.startsWith("self.") ||
1479
+ typeFullName.startsWith("_") ||
1480
+ typeFullName.startsWith("def ")
1481
+ ) {
1482
+ return true;
1483
+ }
1484
+ }
1485
+ if (["php"].includes(language)) {
1486
+ if (!typeFullName.includes("\\") && !typeFullName.startsWith("use")) {
1487
+ return true;
1488
+ }
1489
+ }
1490
+ if (["ruby"].includes(language)) {
1491
+ if (
1492
+ !typeFullName ||
1493
+ ["<empty>"].includes(typeFullName) ||
1494
+ typeFullName.startsWith("__core.") ||
1495
+ typeFullName.startsWith("@") ||
1496
+ typeFullName.toLowerCase() === typeFullName
1497
+ ) {
1498
+ return true;
1499
+ }
1500
+ }
1501
+ return !!userDefinedTypesMap[typeFullName];
1502
+ }
1503
+
1504
+ /**
1505
+ * Extract service and endpoint definitions from an OpenAPI spec file.
1506
+ *
1507
+ * Parses the JSON spec and, for every operation under `paths`, records a
1508
+ * service entry keyed by the URL pattern and HTTP method into `servicesMap`.
1509
+ *
1510
+ * @param {string} _language Application language (unused; reserved for future use)
1511
+ * @param {string} openapiSpecFile Path to the OpenAPI JSON spec file
1512
+ * @param {Object} servicesMap Map populated with detected services
1513
+ * @returns {void}
1514
+ */
1515
+ export function detectServicesFromOpenAPI(
1516
+ _language,
1517
+ openapiSpecFile,
1518
+ servicesMap,
1519
+ ) {
1520
+ try {
1521
+ const specData = JSON.parse(
1522
+ fs.readFileSync(openapiSpecFile, { encoding: "utf-8" }),
1523
+ );
1524
+ if (!specData?.paths || !Object.keys(specData.paths).length) {
1525
+ return;
1526
+ }
1527
+ for (const aurlPattern of Object.keys(specData.paths)) {
1528
+ const httpMethodObj = specData.paths[aurlPattern];
1529
+ for (const httpMethod of Object.keys(httpMethodObj)) {
1530
+ const hobj = httpMethodObj[httpMethod];
1531
+ const serviceName = `service-${aurlPattern.replaceAll("/", "")}-${httpMethod}`;
1532
+ const operationId = hobj["operationId"];
1533
+ const properties = [
1534
+ { name: "cdx:service:httpMethod", value: httpMethod },
1535
+ ];
1536
+ if (operationId) {
1537
+ properties.push({ name: "internal:operationId", value: operationId });
1538
+ }
1539
+ servicesMap[serviceName] = {
1540
+ endpoints: new Set([aurlPattern]),
1541
+ authenticated: undefined,
1542
+ xTrustBoundary: undefined,
1543
+ properties,
1544
+ };
1545
+ }
1546
+ }
1547
+ } catch (_e) {
1548
+ // Ignore malformed or unsupported OpenAPI files and fall back to usage slices.
1549
+ }
1550
+ }
1551
+
1552
+ /**
1553
+ * Method to detect services from annotation objects in the usage slice
1554
+ *
1555
+ * @param {string} language Application language
1556
+ * @param {Array} slice Usages array for each objectSlice
1557
+ * @param {Object} servicesMap Existing service map
1558
+ */
1559
+ export function detectServicesFromUsages(language, slice, servicesMap = {}) {
1560
+ const usages = slice.usages;
1561
+ if (!usages) {
1562
+ return [];
1563
+ }
1564
+ for (const usage of usages) {
1565
+ const targetObj = usage?.targetObj;
1566
+ const definedBy = usage?.definedBy;
1567
+ let endpoints = [];
1568
+ let authenticated;
1569
+ if (language === "ruby" && definedBy?.name?.includes("/")) {
1570
+ endpoints = extractEndpoints(language, definedBy.name);
1571
+ } else if (targetObj?.resolvedMethod) {
1572
+ if (language !== "php") {
1573
+ endpoints = extractEndpoints(language, targetObj?.resolvedMethod);
1574
+ }
1575
+ if (targetObj?.resolvedMethod.toLowerCase().includes("auth")) {
1576
+ authenticated = true;
1577
+ }
1578
+ } else if (definedBy?.resolvedMethod) {
1579
+ if (language !== "php") {
1580
+ endpoints = extractEndpoints(language, definedBy?.resolvedMethod);
1581
+ }
1582
+ if (definedBy?.resolvedMethod.toLowerCase().includes("auth")) {
1583
+ authenticated = true;
1584
+ }
1585
+ }
1586
+ if (usage.invokedCalls) {
1587
+ for (const acall of usage.invokedCalls) {
1588
+ if (acall.resolvedMethod) {
1589
+ if (language !== "php") {
1590
+ const tmpEndpoints = extractEndpoints(
1591
+ language,
1592
+ acall.resolvedMethod,
1593
+ );
1594
+ if (acall.resolvedMethod.toLowerCase().includes("auth")) {
1595
+ authenticated = true;
1596
+ }
1597
+ if (tmpEndpoints?.length) {
1598
+ endpoints = (endpoints || []).concat(tmpEndpoints);
1599
+ }
1600
+ }
1601
+ }
1602
+ }
1603
+ }
1604
+ if (endpoints?.length) {
1605
+ const serviceName = constructServiceName(language, slice);
1606
+ if (!servicesMap[serviceName]) {
1607
+ servicesMap[serviceName] = {
1608
+ endpoints: new Set(),
1609
+ authenticated,
1610
+ xTrustBoundary: authenticated === true ? true : undefined,
1611
+ };
1612
+ }
1613
+ for (const endpoint of endpoints) {
1614
+ servicesMap[serviceName].endpoints.add(endpoint);
1615
+ }
1616
+ }
1617
+ }
1618
+ }
1619
+
1620
+ /**
1621
+ * Method to detect services from user defined types in the usage slice
1622
+ *
1623
+ * @param {string} language Application language
1624
+ * @param {Array} userDefinedTypes User defined types
1625
+ * @param {Object} servicesMap Existing service map
1626
+ */
1627
+ export function detectServicesFromUDT(language, userDefinedTypes, servicesMap) {
1628
+ if (
1629
+ ["python", "py", "c", "cpp", "c++", "php", "ruby"].includes(language) &&
1630
+ userDefinedTypes?.length
1631
+ ) {
1632
+ for (const audt of userDefinedTypes) {
1633
+ if (
1634
+ audt.name.toLowerCase().includes("route") ||
1635
+ audt.name.toLowerCase().includes("path") ||
1636
+ audt.name.toLowerCase().includes("url") ||
1637
+ audt.name.toLowerCase().includes("registerhandler") ||
1638
+ audt.name.toLowerCase().includes("endpoint") ||
1639
+ audt.name.toLowerCase().includes("api") ||
1640
+ audt.name.toLowerCase().includes("add_method") ||
1641
+ audt.name.toLowerCase().includes("get") ||
1642
+ audt.name.toLowerCase().includes("post") ||
1643
+ audt.name.toLowerCase().includes("delete") ||
1644
+ audt.name.toLowerCase().includes("put") ||
1645
+ audt.name.toLowerCase().includes("head") ||
1646
+ audt.name.toLowerCase().includes("options") ||
1647
+ audt.name.toLowerCase().includes("addRoute") ||
1648
+ audt.name.toLowerCase().includes("connect")
1649
+ ) {
1650
+ const fields = audt.fields || [];
1651
+ if (fields.length && fields[0]?.name && fields[0].name.length > 1) {
1652
+ const endpoints = extractEndpoints(language, fields[0].name);
1653
+ let serviceName = "service";
1654
+ if (audt.fileName) {
1655
+ serviceName = `${path.basename(
1656
+ audt.fileName.replace(".py", ""),
1657
+ )}-service`;
1658
+ }
1659
+ if (endpoints?.length) {
1660
+ if (!servicesMap[serviceName]) {
1661
+ servicesMap[serviceName] = {
1662
+ endpoints: new Set(),
1663
+ authenticated: false,
1664
+ xTrustBoundary: undefined,
1665
+ };
1666
+ }
1667
+ for (const endpoint of endpoints) {
1668
+ servicesMap[serviceName].endpoints.add(endpoint);
1669
+ }
1670
+ }
1671
+ }
1672
+ }
1673
+ }
1674
+ }
1675
+ }
1676
+
1677
+ /**
1678
+ * Derive a service name from a slice's fullName or file name.
1679
+ *
1680
+ * Uses the portion of `slice.fullName` before the first `:` (dots replaced with
1681
+ * hyphens), falling back to the file basename. A `-service` suffix is appended
1682
+ * when not already present.
1683
+ *
1684
+ * @param {string} _language Application language (unused; reserved for future use)
1685
+ * @param {Object} slice Object slice containing `fullName` and/or `fileName`
1686
+ * @returns {string} Constructed service name
1687
+ */
1688
+ export function constructServiceName(_language, slice) {
1689
+ let serviceName = "service";
1690
+ if (slice?.fullName) {
1691
+ serviceName = slice.fullName.split(":")[0].replace(/\./g, "-");
1692
+ } else if (slice?.fileName) {
1693
+ serviceName = path.basename(slice.fileName).split(".")[0];
1694
+ }
1695
+ if (!serviceName.endsWith("service")) {
1696
+ serviceName = `${serviceName}-service`;
1697
+ }
1698
+ return serviceName;
1699
+ }
1700
+
1701
+ /**
1702
+ * Extract HTTP endpoint paths from source code annotations for a given language.
1703
+ *
1704
+ * Examines the supplied code snippet for framework-specific routing annotations
1705
+ * (e.g. Spring `@*Mapping`, Express `app.`/`route`, Rails route verbs) and
1706
+ * returns the matched endpoint path strings.
1707
+ *
1708
+ * @param {string} language Application language such as `java`, `js`, or `ruby`
1709
+ * @param {string} code Source code snippet to inspect
1710
+ * @returns {string[]|undefined} Array of endpoint paths or `undefined` when none are found
1711
+ */
1712
+ export function extractEndpoints(language, code) {
1713
+ if (!code) {
1714
+ return undefined;
1715
+ }
1716
+ let endpoints;
1717
+ switch (language) {
1718
+ case "java":
1719
+ case "jar":
1720
+ if (
1721
+ code.startsWith("@") &&
1722
+ (code.includes("Mapping") || code.includes("Path")) &&
1723
+ code.includes("(")
1724
+ ) {
1725
+ const matches = code.match(/['"](.*?)['"]/gi) || [];
1726
+ endpoints = matches
1727
+ .map((v) => v.replace(/["']/g, ""))
1728
+ .filter(
1729
+ (v) =>
1730
+ v.length &&
1731
+ !v.startsWith(".") &&
1732
+ v.includes("/") &&
1733
+ !v.startsWith("@"),
1734
+ );
1735
+ }
1736
+ break;
1737
+ case "js":
1738
+ case "ts":
1739
+ case "javascript":
1740
+ case "typescript":
1741
+ if (code.includes("app.") || code.includes("route")) {
1742
+ const matches = code.match(/['"](.*?)['"]/gi) || [];
1743
+ endpoints = matches
1744
+ .map((v) => v.replace(/["']/g, ""))
1745
+ .filter(
1746
+ (v) =>
1747
+ v.length &&
1748
+ !v.startsWith(".") &&
1749
+ v.includes("/") &&
1750
+ !v.startsWith("@") &&
1751
+ !v.startsWith("application/") &&
1752
+ !v.startsWith("text/"),
1753
+ );
1754
+ }
1755
+ break;
1756
+ case "ruby":
1757
+ case "rb": {
1758
+ let urlPrefix = "";
1759
+ let urlSuffix = "";
1760
+ // Remove the ellipsis added by the frontend
1761
+ code = code.replaceAll("...", "");
1762
+ if (code.includes("namespace ")) {
1763
+ urlPrefix = code.split("namespace ").pop().split(" ")[0];
1764
+ } else if (code.includes("collection do get ")) {
1765
+ urlPrefix = code.split("collection do get ").pop().split(" ")[0];
1766
+ }
1767
+ for (const m of ["get", "post", "delete", "options", "put", "head"]) {
1768
+ if (code.includes(`${m} `)) {
1769
+ urlSuffix = code.split(`${m} `).pop().split(" ")[0];
1770
+ }
1771
+ }
1772
+ if (code.includes("http") && code.includes('"')) {
1773
+ endpoints = code.split('"').filter((s) => s.startsWith("http"));
1774
+ }
1775
+ if (urlPrefix !== "" || urlSuffix !== "") {
1776
+ if (!endpoints) {
1777
+ endpoints = [];
1778
+ }
1779
+ endpoints.push(
1780
+ `${urlPrefix.replace(/['"]/g, "")}${urlSuffix.replace(/['"]/g, "")}`,
1781
+ );
1782
+ }
1783
+ endpoints =
1784
+ endpoints && Array.isArray(endpoints)
1785
+ ? endpoints.filter(
1786
+ (u) => u.length > 1 && !u.startsWith(".") && u !== "https:/",
1787
+ )
1788
+ : endpoints;
1789
+ break;
1790
+ }
1791
+ default:
1792
+ endpoints = (code.match(/['"](.*?)['"]/gi) || [])
1793
+ .map((v) => v.replace(/["']/g, "").replace("\n", ""))
1794
+ .filter((v) => v.length > 2 && v.includes("/"));
1795
+ break;
1796
+ }
1797
+ return endpoints;
1798
+ }
1799
+
1800
+ /**
1801
+ * Method to create the SBOM with evidence file called evinse file.
1802
+ *
1803
+ * @param {Object} sliceArtefacts Various artefacts from the slice operation
1804
+ * @param {Object} options Command line options
1805
+ * @returns
1806
+ */
1807
+ export function createEvinseFile(sliceArtefacts, options) {
1808
+ const {
1809
+ tempDir,
1810
+ tempDirOwned,
1811
+ usagesSlicesFile,
1812
+ dataFlowSlicesFile,
1813
+ reachablesSlicesFile,
1814
+ purlLocationMap,
1815
+ servicesMap,
1816
+ dataFlowFrames,
1817
+ componentPropertiesMap = {},
1818
+ metadataProperties = [],
1819
+ cryptoComponents,
1820
+ cryptoGeneratePurls,
1821
+ } = sliceArtefacts;
1822
+ const bomFile = options.input;
1823
+ const evinseOutFile = options.output;
1824
+ const bomJson = JSON.parse(fs.readFileSync(bomFile, "utf8"));
1825
+ const components = bomJson.components || [];
1826
+ // Clear existing annotations
1827
+ bomJson.annotations = [];
1828
+ appendComponentProperties(bomJson.metadata?.component, metadataProperties);
1829
+ let occEvidencePresent = false;
1830
+ let csEvidencePresent = false;
1831
+ let servicesPresent = false;
1832
+ const propertyEvidencePresent =
1833
+ metadataProperties.length || Object.keys(componentPropertiesMap).length;
1834
+ for (const comp of [bomJson.metadata?.component, ...components]) {
1835
+ if (!comp?.purl) {
1836
+ continue;
1837
+ }
1838
+ delete comp.signature;
1839
+ appendComponentProperties(comp, componentPropertiesMap[comp.purl]);
1840
+ const locationOccurrences = Array.from(
1841
+ purlLocationMap[comp.purl] || [],
1842
+ ).sort();
1843
+ if (locationOccurrences.length) {
1844
+ if (!comp.evidence) {
1845
+ comp.evidence = {};
1846
+ }
1847
+ // This step would replace any existing occurrences
1848
+ // This is fine as long as the input sbom was also generated by cdxgen
1849
+ comp.evidence.occurrences = locationOccurrences
1850
+ .filter((l) => !!l)
1851
+ .map((l) => parseOccurrenceEvidenceLocation(l));
1852
+ occEvidencePresent = true;
1853
+ }
1854
+ const dfFrames = dataFlowFrames[comp.purl];
1855
+ if (dfFrames?.length) {
1856
+ if (!comp.evidence) {
1857
+ comp.evidence = {};
1858
+ }
1859
+ if (!comp.evidence.callstack) {
1860
+ comp.evidence.callstack = {};
1861
+ }
1862
+ if (!comp.evidence.callstack.frames) {
1863
+ comp.evidence.callstack.frames = framePicker(dfFrames);
1864
+ csEvidencePresent = true;
1865
+ }
1866
+ }
1867
+ // Add crypto tags if this purl offers any generation algorithm
1868
+ if (
1869
+ cryptoGeneratePurls?.[comp.purl] &&
1870
+ Array.from(cryptoGeneratePurls[comp.purl]).length
1871
+ ) {
1872
+ comp.tags = ["crypto", "crypto-generate"];
1873
+ }
1874
+ } // for
1875
+ if (servicesMap && Object.keys(servicesMap).length) {
1876
+ const services = [];
1877
+ for (const serviceName of Object.keys(servicesMap)) {
1878
+ services.push({
1879
+ name: serviceName,
1880
+ endpoints: Array.from(servicesMap[serviceName].endpoints),
1881
+ authenticated: servicesMap[serviceName].authenticated,
1882
+ "x-trust-boundary": servicesMap[serviceName].xTrustBoundary,
1883
+ properties: servicesMap[serviceName].properties,
1884
+ });
1885
+ }
1886
+ // Add to existing services while preserving CycloneDX uniqueItems validity
1887
+ bomJson.services = mergeServices(bomJson.services || [], services);
1888
+ servicesPresent = true;
1889
+ }
1890
+ // Add the crypto components to the components list
1891
+ if (cryptoComponents?.length) {
1892
+ bomJson.components = bomJson.components.concat(cryptoComponents);
1893
+ }
1894
+ // Fix the dependencies section with provides information
1895
+ if (
1896
+ cryptoGeneratePurls &&
1897
+ Object.keys(cryptoGeneratePurls).length &&
1898
+ bomJson.dependencies
1899
+ ) {
1900
+ const newDependencies = [];
1901
+ for (const depObj of bomJson.dependencies) {
1902
+ if (depObj.ref && cryptoGeneratePurls[depObj.ref]) {
1903
+ const providedAlgos = Array.from(cryptoGeneratePurls[depObj.ref]);
1904
+ if (providedAlgos.length) {
1905
+ depObj.provides = providedAlgos;
1906
+ }
1907
+ }
1908
+ newDependencies.push(depObj);
1909
+ }
1910
+ bomJson.dependencies = newDependencies;
1911
+ }
1912
+ if (options.annotate) {
1913
+ if (usagesSlicesFile && safeExistsSync(usagesSlicesFile)) {
1914
+ bomJson.annotations.push({
1915
+ subjects: [bomJson.serialNumber],
1916
+ annotator: { component: bomJson.metadata.tools.components[0] },
1917
+ timestamp: getTimestamp(),
1918
+ text: fs.readFileSync(usagesSlicesFile, "utf8"),
1919
+ });
1920
+ }
1921
+ if (dataFlowSlicesFile && safeExistsSync(dataFlowSlicesFile)) {
1922
+ bomJson.annotations.push({
1923
+ subjects: [bomJson.serialNumber],
1924
+ annotator: { component: bomJson.metadata.tools.components[0] },
1925
+ timestamp: getTimestamp(),
1926
+ text: fs.readFileSync(dataFlowSlicesFile, "utf8"),
1927
+ });
1928
+ }
1929
+ if (reachablesSlicesFile && safeExistsSync(reachablesSlicesFile)) {
1930
+ bomJson.annotations.push({
1931
+ subjects: [bomJson.serialNumber],
1932
+ annotator: { component: bomJson.metadata.tools.components[0] },
1933
+ timestamp: getTimestamp(),
1934
+ text: fs.readFileSync(reachablesSlicesFile, "utf8"),
1935
+ });
1936
+ }
1937
+ }
1938
+ // Increment the version
1939
+ bomJson.version = (bomJson.version || 1) + 1;
1940
+ // Set the current timestamp to indicate this is newer
1941
+ bomJson.metadata.timestamp = getTimestamp();
1942
+ delete bomJson.signature;
1943
+ // Redo post-processing with evinse data. `executeOsQuery` has to be injected
1944
+ // because postgen no longer imports it directly (stages is below managers);
1945
+ // without it the crypto formulation path receives `undefined` and throws.
1946
+ const bomNSData = postProcess({ bomJson }, { ...options, executeOsQuery });
1947
+ safeWriteSync(
1948
+ evinseOutFile,
1949
+ JSON.stringify(bomNSData.bomJson, null, options.jsonPretty ? 2 : null),
1950
+ );
1951
+ if (
1952
+ occEvidencePresent ||
1953
+ csEvidencePresent ||
1954
+ servicesPresent ||
1955
+ propertyEvidencePresent
1956
+ ) {
1957
+ console.log(evinseOutFile, "created successfully.");
1958
+ } else {
1959
+ console.log(
1960
+ "Unable to identify component evidence for the input SBOM based on the slices from atom. The slices are either empty or lack appropriate tags.",
1961
+ );
1962
+ if (DEBUG_MODE) {
1963
+ console.log(
1964
+ "1. Ensure cdxgen was installed without omitting any optional dependencies.",
1965
+ );
1966
+ console.log(
1967
+ "2. Retry after removing the following files from the root directory: app.atom, usages.slices.json, reachables.slices.json.",
1968
+ );
1969
+ if (readEnvironmentVariable("CDXGEN_IN_CONTAINER") !== "true") {
1970
+ console.log(
1971
+ "3. Additional environment variables may have to be set for local invocations. Check the documentation for atom and evinse.",
1972
+ );
1973
+ } else {
1974
+ console.log(
1975
+ "TIP: Try creating the slices using the official atom container image `ghcr.io/appthreat/atom:main` directly. Refer to the documentation: https://atom-docs.appthreat.dev/",
1976
+ );
1977
+ }
1978
+ console.log(
1979
+ "Large projects may require more memory. Consider increasing the memory to 16GB or higher.",
1980
+ );
1981
+ }
1982
+ }
1983
+ if (tempDirOwned && tempDir?.startsWith(getTmpDir())) {
1984
+ safeRmSync(tempDir, { recursive: true, force: true });
1985
+ }
1986
+ return bomNSData?.bomJson;
1987
+ }
1988
+
1989
+ /**
1990
+ * Method to convert dataflow slice into usable callstack frames
1991
+ * Implemented based on the logic proposed here - https://github.com/AppThreat/atom/blob/main/specification/docs/slices.md#data-flow-slice
1992
+ *
1993
+ * @param {string} language Application language
1994
+ * @param {Object} userDefinedTypesMap User Defined types in the application
1995
+ * @param {Object} dataFlowSlice Data flow slice object from atom
1996
+ * @param {Object} dbObjMap DB models
1997
+ * @param {Object} _purlLocationMap Object to track locations where purls are used
1998
+ * @param {Object} purlImportsMap Object to track package urls and their import aliases
1999
+ */
2000
+ export async function collectDataFlowFrames(
2001
+ language,
2002
+ userDefinedTypesMap,
2003
+ dataFlowSlice,
2004
+ dbObjMap,
2005
+ _purlLocationMap,
2006
+ purlImportsMap,
2007
+ ) {
2008
+ const nodes = dataFlowSlice?.graph?.nodes || [];
2009
+ // Cache the nodes based on the id to improve lookup
2010
+ const nodeCache = {};
2011
+ // purl key and an array of frames array
2012
+ // CycloneDX 1.5 currently accepts only 1 frame as evidence
2013
+ // so this method is more future-proof
2014
+ const dfFrames = {};
2015
+ for (const n of nodes) {
2016
+ nodeCache[n.id] = n;
2017
+ }
2018
+ const paths = dataFlowSlice?.paths || [];
2019
+ for (const apath of paths) {
2020
+ const aframe = [];
2021
+ let referredPurls = new Set();
2022
+ for (const nid of apath) {
2023
+ const theNode = nodeCache[nid];
2024
+ if (!theNode) {
2025
+ continue;
2026
+ }
2027
+ let typeFullName = theNode.typeFullName;
2028
+ if (
2029
+ ["javascript", "js", "ts", "typescript"].includes(language) &&
2030
+ typeFullName === "ANY"
2031
+ ) {
2032
+ if (
2033
+ theNode.code &&
2034
+ (theNode.code.startsWith("new ") ||
2035
+ ["METHOD_PARAMETER_IN", "IDENTIFIER"].includes(theNode.label))
2036
+ ) {
2037
+ typeFullName = theNode.code.split("(")[0].replace("new ", "");
2038
+ } else {
2039
+ typeFullName = theNode.fullName || theNode.name;
2040
+ }
2041
+ }
2042
+ const maybeClassType = getClassTypeFromSignature(language, typeFullName);
2043
+ if (!isFilterableType(language, userDefinedTypesMap, typeFullName)) {
2044
+ if (purlImportsMap && Object.keys(purlImportsMap).length) {
2045
+ for (const apurl of Object.keys(purlImportsMap)) {
2046
+ const apurlImports = purlImportsMap[apurl];
2047
+ if (
2048
+ apurlImports &&
2049
+ (apurlImports.includes(typeFullName) ||
2050
+ apurlImports.includes(maybeClassType))
2051
+ ) {
2052
+ referredPurls.add(apurl);
2053
+ }
2054
+ }
2055
+ } else {
2056
+ // Check the namespaces db
2057
+ let nsHits = typePurlsCache[typeFullName];
2058
+ if (["java", "jar"].includes(language)) {
2059
+ nsHits = await dbObjMap.Namespaces.findAll({
2060
+ attributes: ["purl"],
2061
+ where: {
2062
+ data: {
2063
+ like: `%${typeFullName}%`,
2064
+ },
2065
+ },
2066
+ });
2067
+ }
2068
+ if (nsHits?.length) {
2069
+ for (const ns of nsHits) {
2070
+ referredPurls.add(ns.purl);
2071
+ }
2072
+ typePurlsCache[typeFullName] = nsHits;
2073
+ } else if (DEBUG_MODE) {
2074
+ console.log("Unable to identify purl for", typeFullName);
2075
+ }
2076
+ }
2077
+ }
2078
+ let parentPackageName = theNode.parentPackageName || "";
2079
+ if (
2080
+ parentPackageName === "<global>" &&
2081
+ theNode.parentClassName &&
2082
+ theNode.parentClassName.includes("::")
2083
+ ) {
2084
+ parentPackageName = theNode.parentClassName.split("::")[0];
2085
+ if (parentPackageName.includes(".js")) {
2086
+ const tmpA = parentPackageName.split("/");
2087
+ if (tmpA.length > 1) {
2088
+ tmpA.pop();
2089
+ }
2090
+ parentPackageName = tmpA.join("/");
2091
+ }
2092
+ }
2093
+ aframe.push({
2094
+ package: parentPackageName,
2095
+ module: theNode.parentClassName || "",
2096
+ function: theNode.parentMethodName || "",
2097
+ line: theNode.lineNumber || undefined,
2098
+ column: theNode.columnNumber || undefined,
2099
+ fullFilename: theNode.parentFileName || "",
2100
+ });
2101
+ }
2102
+ referredPurls = Array.from(referredPurls);
2103
+ if (referredPurls.length) {
2104
+ for (const apurl of referredPurls) {
2105
+ if (!dfFrames[apurl]) {
2106
+ dfFrames[apurl] = [];
2107
+ }
2108
+ // Store this frame as an evidence for this purl
2109
+ dfFrames[apurl].push(aframe);
2110
+ }
2111
+ }
2112
+ }
2113
+ return dfFrames;
2114
+ }
2115
+
2116
+ /**
2117
+ * Method to convert reachable slice into usable callstack frames and crypto components
2118
+ *
2119
+ * Implemented based on the logic proposed here - https://github.com/AppThreat/atom/blob/main/specification/docs/slices.md#data-flow-slice
2120
+ *
2121
+ * @param {string} _language Application language
2122
+ * @param {Object} reachablesSlice Reachables slice object from atom
2123
+ */
2124
+ export function collectReachableFrames(_language, reachablesSlice) {
2125
+ const reachableNodes = Array.isArray(reachablesSlice)
2126
+ ? reachablesSlice
2127
+ : reachablesSlice?.reachables || [];
2128
+ // purl key and an array of frames array
2129
+ // CycloneDX 1.5 currently accepts only 1 frame as evidence
2130
+ // so this method is more future-proof
2131
+ const dfFrames = {};
2132
+ const cryptoComponentsMap = {};
2133
+ // Track purls which provide the specific generate algorithm
2134
+ const cryptoGeneratePurls = {};
2135
+ for (const anode of reachableNodes) {
2136
+ const aframe = [];
2137
+ let referredPurls = new Set(anode.purls || []);
2138
+ let isCryptoFlow = false;
2139
+ let codeSnippets = "";
2140
+ let cpurls = [];
2141
+ for (const fnode of anode.flows) {
2142
+ const tagStr = fnode.tags || "";
2143
+ if (tagStr.includes("crypto")) {
2144
+ isCryptoFlow = true;
2145
+ }
2146
+ if (isCryptoFlow && fnode.code) {
2147
+ codeSnippets = `${codeSnippets}\\n${fnode.code}`;
2148
+ }
2149
+ if (
2150
+ tagStr.includes("crypto-generate") ||
2151
+ fnode.code.includes("encrypt") ||
2152
+ fnode.code.includes("decrypt") ||
2153
+ fnode.code.includes("sign")
2154
+ ) {
2155
+ cpurls = tagStr.split(", ").filter((t) => t.startsWith("pkg:"));
2156
+ for (const cpurl of cpurls) {
2157
+ if (!cryptoGeneratePurls[cpurl]) {
2158
+ cryptoGeneratePurls[cpurl] = new Set();
2159
+ }
2160
+ }
2161
+ }
2162
+ if (!fnode.parentFileName || fnode.parentFileName === "<unknown>") {
2163
+ continue;
2164
+ }
2165
+ aframe.push({
2166
+ package: fnode.parentPackageName,
2167
+ module: fnode.parentClassName || "",
2168
+ function: fnode.parentMethodName || "",
2169
+ line: fnode.lineNumber || undefined,
2170
+ column: fnode.columnNumber || undefined,
2171
+ fullFilename: fnode.parentFileName || "",
2172
+ });
2173
+ }
2174
+ referredPurls = Array.from(referredPurls);
2175
+ if (referredPurls.length) {
2176
+ for (const apurl of referredPurls) {
2177
+ if (!dfFrames[apurl]) {
2178
+ dfFrames[apurl] = [];
2179
+ }
2180
+ // Store this frame as an evidence for this purl
2181
+ dfFrames[apurl].push(aframe);
2182
+ }
2183
+ }
2184
+ // Detect crypto algorithms used in a crypto flow
2185
+ if (isCryptoFlow && codeSnippets.length) {
2186
+ const cryptoAlgos = findCryptoAlgos(codeSnippets);
2187
+ for (const algo of cryptoAlgos) {
2188
+ cryptoComponentsMap[algo.ref] = algo;
2189
+ for (const cpurl of cpurls) {
2190
+ cryptoGeneratePurls[cpurl].add(algo.ref);
2191
+ }
2192
+ }
2193
+ }
2194
+ }
2195
+ const cryptoComponents = [];
2196
+ for (const cref of Object.keys(cryptoComponentsMap)) {
2197
+ const algoObj = cryptoComponentsMap[cref];
2198
+ cryptoComponents.push({
2199
+ type: "cryptographic-asset",
2200
+ name: algoObj.name,
2201
+ "bom-ref": algoObj.ref,
2202
+ description: algoObj.description || "",
2203
+ cryptoProperties: {
2204
+ assetType: "algorithm",
2205
+ oid: algoObj.oid,
2206
+ },
2207
+ });
2208
+ }
2209
+ return {
2210
+ dataFlowFrames: dfFrames,
2211
+ cryptoComponents,
2212
+ cryptoGeneratePurls,
2213
+ };
2214
+ }
2215
+
2216
+ /**
2217
+ * Method to pick a callstack frame as an evidence. This method is required since CycloneDX 1.5 accepts only a single frame as evidence.
2218
+ *
2219
+ * @param {Array} dfFrames Data flow frames
2220
+ * @returns
2221
+ */
2222
+ export function framePicker(dfFrames) {
2223
+ if (!dfFrames?.length) {
2224
+ return undefined;
2225
+ }
2226
+ const normalizedStacks = dfFrames
2227
+ .filter((stack) => Array.isArray(stack) && stack.length)
2228
+ .map((stack) =>
2229
+ stack.filter(
2230
+ (frame) => frame?.fullFilename && frame.fullFilename !== "<unknown>",
2231
+ ),
2232
+ )
2233
+ .filter((stack) => stack.length);
2234
+ if (!normalizedStacks.length) {
2235
+ return undefined;
2236
+ }
2237
+ const frameKey = (frame) =>
2238
+ `${frame.fullFilename}#${frame.line || ""}#${frame.column || ""}`;
2239
+ const dedupeFrames = (frames) => {
2240
+ const seen = new Set();
2241
+ const out = [];
2242
+ for (const frame of frames) {
2243
+ const key = frameKey(frame);
2244
+ if (seen.has(key)) {
2245
+ continue;
2246
+ }
2247
+ seen.add(key);
2248
+ out.push(frame);
2249
+ }
2250
+ return out;
2251
+ };
2252
+ const stackScore = (stack) => {
2253
+ const deduped = dedupeFrames(stack);
2254
+ const uniqueFiles = new Set(deduped.map((frame) => frame.fullFilename))
2255
+ .size;
2256
+ return uniqueFiles * 100 + deduped.length;
2257
+ };
2258
+ const rankedStacks = normalizedStacks
2259
+ .map((stack) => dedupeFrames(stack))
2260
+ .sort((left, right) => stackScore(right) - stackScore(left));
2261
+ const best = rankedStacks[0];
2262
+ const bestUniqueFiles = new Set(best.map((frame) => frame.fullFilename)).size;
2263
+ if (bestUniqueFiles >= 2 || best.length >= 2) {
2264
+ return best;
2265
+ }
2266
+ const synthesized = [];
2267
+ const seen = new Set();
2268
+ for (const stack of rankedStacks) {
2269
+ for (const frame of stack) {
2270
+ const key = frameKey(frame);
2271
+ if (seen.has(key)) {
2272
+ continue;
2273
+ }
2274
+ seen.add(key);
2275
+ synthesized.push(frame);
2276
+ if (synthesized.length >= 8) {
2277
+ return synthesized;
2278
+ }
2279
+ }
2280
+ }
2281
+ return synthesized.length ? synthesized : best;
2282
+ }
2283
+
2284
+ /**
2285
+ * Method to simplify types. For example, arrays ending with [] could be simplified.
2286
+ *
2287
+ * @param {string} typeFullName Full name of the type to simplify
2288
+ * @returns Simplified type string
2289
+ */
2290
+ export function simplifyType(typeFullName) {
2291
+ return typeFullName.replace("[]", "");
2292
+ }
2293
+
2294
+ /**
2295
+ * Reduce a full type signature to its enclosing class type for a given language.
2296
+ *
2297
+ * Strips method, call, and member qualifiers from the signature using language
2298
+ * specific rules (e.g. java method selectors, javascript `::`/`new`/`await`,
2299
+ * python module/body suffixes) and returns the simplified class type via
2300
+ * {@link simplifyType}.
2301
+ *
2302
+ * @param {string} language Application language
2303
+ * @param {string} typeFullName Full type signature to reduce
2304
+ * @returns {string|undefined} Enclosing class type or `undefined` when it cannot be resolved
2305
+ */
2306
+ export function getClassTypeFromSignature(language, typeFullName) {
2307
+ if (["java", "jar"].includes(language) && typeFullName.includes(":")) {
2308
+ typeFullName = typeFullName.split(":")[0];
2309
+ const tmpA = typeFullName.split(".");
2310
+ tmpA.pop();
2311
+ typeFullName = tmpA.join(".");
2312
+ } else if (["javascript", "js", "ts", "typescript"].includes(language)) {
2313
+ typeFullName = typeFullName.replace("new: ", "").replace("await ", "");
2314
+ if (typeFullName.includes(":")) {
2315
+ const tmpA = typeFullName.split("::")[0].replace(/:/g, "/").split("/");
2316
+ if (tmpA.length > 1) {
2317
+ tmpA.pop();
2318
+ }
2319
+ typeFullName = tmpA.join("/");
2320
+ }
2321
+ } else if (["python", "py"].includes(language)) {
2322
+ if (typeFullName.includes("/")) {
2323
+ typeFullName = typeFullName.split("/").pop();
2324
+ }
2325
+ typeFullName = typeFullName
2326
+ .replace(".py:<module>", "")
2327
+ .replace(/\//g, ".")
2328
+ .replace(".<metaClassCallHandler>", "")
2329
+ .replace(".<fakeNew>", "")
2330
+ .replace(".<body>", "")
2331
+ .replace(".__iter__", "")
2332
+ .replace(".__init__", "");
2333
+ } else if (["php"].includes(language)) {
2334
+ typeFullName = typeFullName.split("->")[0].split("::")[0];
2335
+ } else if (["ruby"].includes(language)) {
2336
+ if (
2337
+ ["<empty>"].includes(typeFullName) ||
2338
+ typeFullName.startsWith("__core.")
2339
+ ) {
2340
+ return undefined;
2341
+ }
2342
+ typeFullName = typeFullName.split("::")[0].split(" ").pop();
2343
+ }
2344
+ if (
2345
+ typeFullName.startsWith("<unresolved") ||
2346
+ typeFullName.startsWith("<operator") ||
2347
+ typeFullName.startsWith("<unknownFullName")
2348
+ ) {
2349
+ return undefined;
2350
+ }
2351
+ if (typeFullName.includes("$")) {
2352
+ typeFullName = typeFullName.split("$")[0];
2353
+ }
2354
+ return simplifyType(typeFullName);
2355
+ }
2356
+
2357
+ function addToOverrides(lKeyOverrides, atype, fileName, ausageLineNumber) {
2358
+ if (!lKeyOverrides[atype]) {
2359
+ lKeyOverrides[atype] = new Set();
2360
+ }
2361
+ lKeyOverrides[atype].add(`${fileName}#${ausageLineNumber}`);
2362
+ }