@cdxgen/cdxgen 13.3.0 → 13.5.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 (372) hide show
  1. package/README.md +12 -9
  2. package/bin/cdxgen-secure.js +15 -0
  3. package/bin/cdxgen.js +115 -31
  4. package/bin/evinse.js +23 -1
  5. package/bin/hbom.js +15 -7
  6. package/bin/repl.js +115 -0
  7. package/bin/sign.js +45 -15
  8. package/bin/tracebom.js +5 -1
  9. package/bin/validate.js +84 -18
  10. package/bin/verify.js +155 -86
  11. package/data/cbomosdb-queries.json +13 -1
  12. package/data/component-tags.json +8 -0
  13. package/data/helpers/init.gradle +46 -21
  14. package/data/queries-darwin.json +25 -1
  15. package/data/queries-win.json +19 -1
  16. package/data/queries.json +41 -0
  17. package/data/remediations.json +344 -1
  18. package/data/rules/cbom-security.yaml +235 -4
  19. package/data/rules/dependency-sources.yaml +67 -0
  20. package/data/rules/go-binaries.yaml +46 -0
  21. package/data/rules/golem-go.yaml +80 -0
  22. package/data/rules/hbom-compliance.yaml +40 -22
  23. package/data/rules/hbom-performance.yaml +7 -1
  24. package/data/rules/host-topology.yaml +1 -1
  25. package/data/rules/obom-runtime.yaml +31 -2
  26. package/data/rules/php-runtime.yaml +143 -0
  27. package/data/rules/scala-evidence.yaml +115 -0
  28. package/lib/audit/index.js +52 -6
  29. package/lib/cli/bomAssembly.js +28 -3
  30. package/lib/cli/cliOptions.js +110 -3
  31. package/lib/cli/index.js +186 -58
  32. package/lib/cli/jsBom.js +50 -29
  33. package/lib/cli/jvmBom.js +779 -146
  34. package/lib/cli/managedBom.js +1021 -261
  35. package/lib/cli/nativeBom.js +364 -58
  36. package/lib/core/activity.js +12 -0
  37. package/lib/core/env.js +47 -17
  38. package/lib/core/fs.js +31 -21
  39. package/lib/core/httpClient.js +233 -69
  40. package/lib/core/pluginRun.js +184 -0
  41. package/lib/core/processTree.js +100 -0
  42. package/lib/core/projectConfig.js +1 -0
  43. package/lib/core/projectTooling.js +97 -0
  44. package/lib/core/propertySanitizer.js +72 -0
  45. package/lib/core/runState.js +39 -0
  46. package/lib/core/toml.js +72 -0
  47. package/lib/ecosystems/asarutils.js +4 -2
  48. package/lib/ecosystems/bunutils.js +5 -7
  49. package/lib/ecosystems/cargoCache.js +231 -8
  50. package/lib/ecosystems/cmakeResolver.js +10 -21
  51. package/lib/ecosystems/cppBuildContext.js +631 -0
  52. package/lib/ecosystems/cppEvidence.js +337 -22
  53. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  54. package/lib/ecosystems/denoutils.js +93 -66
  55. package/lib/ecosystems/ecosystems.js +1967 -788
  56. package/lib/ecosystems/golem.js +578 -18
  57. package/lib/ecosystems/gradleutils.js +340 -46
  58. package/lib/ecosystems/jsEvidence.js +85 -24
  59. package/lib/ecosystems/kosi.js +21 -4
  60. package/lib/ecosystems/npmutils.js +64 -0
  61. package/lib/ecosystems/parsers-dotnet.js +668 -153
  62. package/lib/ecosystems/parsers-elm.js +46 -28
  63. package/lib/ecosystems/parsers-gleam.js +3 -4
  64. package/lib/ecosystems/parsers-go.js +1283 -297
  65. package/lib/ecosystems/parsers-js.js +92 -26
  66. package/lib/ecosystems/parsers-julia.js +2 -3
  67. package/lib/ecosystems/parsers-jvm.js +463 -29
  68. package/lib/ecosystems/parsers-misc.js +405 -93
  69. package/lib/ecosystems/parsers-python.js +152 -20
  70. package/lib/ecosystems/parsers-rust.js +4 -4
  71. package/lib/ecosystems/parsers-terraform.js +1573 -19
  72. package/lib/ecosystems/pythonutils.js +102 -6
  73. package/lib/ecosystems/remote/huggingface.js +3 -0
  74. package/lib/ecosystems/rubyutils.js +73 -20
  75. package/lib/ecosystems/rusi.js +32 -9
  76. package/lib/ecosystems/sbtutils.js +243 -187
  77. package/lib/ecosystems/scalacliutils.js +343 -0
  78. package/lib/ecosystems/scalasem.js +2620 -0
  79. package/lib/ecosystems/terraformRegistry.js +288 -0
  80. package/lib/ecosystems/utils.js +2 -0
  81. package/lib/ecosystems/vendoredCode.js +156 -0
  82. package/lib/evinser/evinser.js +695 -78
  83. package/lib/evinser/swiftsem.js +68 -5
  84. package/lib/helpers/agenticToolutils.js +2 -2
  85. package/lib/helpers/bomSigner.js +576 -128
  86. package/lib/inventory/agentSkillSpec.js +17 -0
  87. package/lib/inventory/aiCollector.js +4 -1
  88. package/lib/inventory/analyzer.js +110 -127
  89. package/lib/inventory/apkInstalledDb.js +243 -0
  90. package/lib/inventory/atomUtils.js +204 -48
  91. package/lib/inventory/auditCategories.js +10 -3
  92. package/lib/inventory/buildToolRateLimit.js +144 -0
  93. package/lib/inventory/cacheCommands.js +2 -1
  94. package/lib/inventory/cacheDir.js +52 -9
  95. package/lib/inventory/certificates.js +2371 -0
  96. package/lib/inventory/cmakeBuildDirs.js +254 -0
  97. package/lib/inventory/communityAiConfigParser.js +4 -9
  98. package/lib/inventory/deps.js +507 -128
  99. package/lib/inventory/display.js +200 -3
  100. package/lib/inventory/dosai.js +747 -101
  101. package/lib/inventory/dosaiParsers.js +310 -15
  102. package/lib/inventory/dynamic.js +96 -8
  103. package/lib/inventory/envcontext.js +168 -5
  104. package/lib/inventory/evidenceUtils.js +590 -132
  105. package/lib/inventory/fetchBatch.js +574 -38
  106. package/lib/inventory/fetchDiskCache.js +597 -0
  107. package/lib/inventory/forgeEnricher.js +14 -5
  108. package/lib/inventory/formulationParsers.js +3 -2
  109. package/lib/inventory/goBuildInfo.js +140 -0
  110. package/lib/inventory/hbom.js +67 -7
  111. package/lib/inventory/hbomAnalysis.js +16 -0
  112. package/lib/inventory/hostTopology.js +21 -0
  113. package/lib/inventory/jvmLocalRepos.js +917 -0
  114. package/lib/inventory/mcpConfigParser.js +2 -3
  115. package/lib/inventory/osPackagePurl.js +244 -0
  116. package/lib/inventory/osPackageResolver.js +89 -87
  117. package/lib/inventory/osinfo.js +79 -31
  118. package/lib/inventory/osqueryTransform.js +114 -5
  119. package/lib/inventory/parentSelection.js +146 -0
  120. package/lib/inventory/phpBuildInfo.js +487 -0
  121. package/lib/inventory/phpHostProbe.js +641 -0
  122. package/lib/inventory/rootfsCerts.js +426 -0
  123. package/lib/inventory/rustFormulationParser.js +3 -4
  124. package/lib/inventory/scalaCoords.js +153 -0
  125. package/lib/inventory/source.js +215 -11
  126. package/lib/inventory/spdx.js +245 -0
  127. package/lib/inventory/traceRunner.js +93 -8
  128. package/lib/inventory/tracedCrypto.js +423 -0
  129. package/lib/managers/binary.js +529 -183
  130. package/lib/managers/docker.js +199 -44
  131. package/lib/managers/oci.js +24 -2
  132. package/lib/parsers/cmakeCache.js +69 -0
  133. package/lib/parsers/cmakeLists.js +16 -2
  134. package/lib/parsers/cmakePresets.js +400 -0
  135. package/lib/parsers/compileCommands.js +557 -0
  136. package/lib/parsers/email.js +277 -0
  137. package/lib/parsers/largeJson.js +34 -2
  138. package/lib/parsers/strictJson.js +322 -0
  139. package/lib/parsers/terraformConfig.js +1073 -0
  140. package/lib/parsers/terraformSource.js +652 -0
  141. package/lib/server/server.js +209 -1
  142. package/lib/stages/postgen/annotator.js +17 -1
  143. package/lib/stages/postgen/introspection/markers.js +1 -1
  144. package/lib/stages/postgen/introspection/reflect.js +4 -1
  145. package/lib/stages/postgen/postgen.js +20 -6
  146. package/lib/stages/postgen/ruleEngine.js +58 -1
  147. package/lib/stages/postgen/specVersionCompat.js +62 -0
  148. package/lib/stages/pregen/pregen.js +117 -5
  149. package/lib/validator/bomValidator.js +26 -5
  150. package/lib/validator/generated/runtime.mjs +13 -0
  151. package/lib/validator/generated/validate-1.6.mjs +26 -7
  152. package/lib/validator/generated/validate-1.7.mjs +31 -12
  153. package/lib/validator/generated/validate-2.0.mjs +30 -11
  154. package/lib/validator/index.js +25 -10
  155. package/lib/validator/reporters/json.js +1 -0
  156. package/package.json +23 -23
  157. package/types/bin/cdxgen-secure.d.ts +3 -0
  158. package/types/bin/cdxgen-secure.d.ts.map +1 -0
  159. package/types/bin/repl.d.ts.map +1 -1
  160. package/types/lib/audit/index.d.ts.map +1 -1
  161. package/types/lib/cli/bomAssembly.d.ts +9 -2
  162. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  163. package/types/lib/cli/cliOptions.d.ts +43 -0
  164. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  165. package/types/lib/cli/index.d.ts.map +1 -1
  166. package/types/lib/cli/jsBom.d.ts.map +1 -1
  167. package/types/lib/cli/jvmBom.d.ts +44 -1
  168. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  169. package/types/lib/cli/managedBom.d.ts +7 -4
  170. package/types/lib/cli/managedBom.d.ts.map +1 -1
  171. package/types/lib/cli/nativeBom.d.ts +3 -1
  172. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  173. package/types/lib/core/activity.d.ts.map +1 -1
  174. package/types/lib/core/env.d.ts +27 -2
  175. package/types/lib/core/env.d.ts.map +1 -1
  176. package/types/lib/core/fs.d.ts.map +1 -1
  177. package/types/lib/core/httpClient.d.ts +13 -8
  178. package/types/lib/core/httpClient.d.ts.map +1 -1
  179. package/types/lib/core/pluginRun.d.ts +97 -0
  180. package/types/lib/core/pluginRun.d.ts.map +1 -0
  181. package/types/lib/core/processTree.d.ts +36 -0
  182. package/types/lib/core/processTree.d.ts.map +1 -0
  183. package/types/lib/core/projectConfig.d.ts.map +1 -1
  184. package/types/lib/core/projectTooling.d.ts +41 -0
  185. package/types/lib/core/projectTooling.d.ts.map +1 -0
  186. package/types/lib/core/propertySanitizer.d.ts +26 -0
  187. package/types/lib/core/propertySanitizer.d.ts.map +1 -1
  188. package/types/lib/core/runState.d.ts +29 -0
  189. package/types/lib/core/runState.d.ts.map +1 -0
  190. package/types/lib/core/toml.d.ts +20 -0
  191. package/types/lib/core/toml.d.ts.map +1 -0
  192. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  193. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  194. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  195. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  196. package/types/lib/ecosystems/cmakeResolver.d.ts +5 -3
  197. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  198. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  199. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  200. package/types/lib/ecosystems/cppEvidence.d.ts +19 -3
  201. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  202. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  203. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  204. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  205. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  206. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  207. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  208. package/types/lib/ecosystems/golem.d.ts +81 -1
  209. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  210. package/types/lib/ecosystems/gradleutils.d.ts +59 -7
  211. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  212. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  213. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  214. package/types/lib/ecosystems/npmutils.d.ts +15 -0
  215. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  216. package/types/lib/ecosystems/parsers-dotnet.d.ts +155 -5
  217. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  218. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  219. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  220. package/types/lib/ecosystems/parsers-go.d.ts +119 -5
  221. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  222. package/types/lib/ecosystems/parsers-js.d.ts +6 -1
  223. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  224. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  225. package/types/lib/ecosystems/parsers-jvm.d.ts +74 -3
  226. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  227. package/types/lib/ecosystems/parsers-misc.d.ts +21 -59
  228. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  229. package/types/lib/ecosystems/parsers-python.d.ts +57 -6
  230. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  231. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  232. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  233. package/types/lib/ecosystems/pythonutils.d.ts +26 -1
  234. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  235. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  236. package/types/lib/ecosystems/rubyutils.d.ts +21 -1
  237. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  238. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  239. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  240. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  241. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  242. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  243. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  244. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  245. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  246. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  247. package/types/lib/ecosystems/utils.d.ts +2 -2
  248. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  249. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  250. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  251. package/types/lib/evinser/evinser.d.ts +100 -8
  252. package/types/lib/evinser/evinser.d.ts.map +1 -1
  253. package/types/lib/evinser/swiftsem.d.ts +10 -0
  254. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  255. package/types/lib/helpers/bomSigner.d.ts +80 -4
  256. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  257. package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
  258. package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
  259. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  260. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  261. package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
  262. package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
  263. package/types/lib/inventory/atomUtils.d.ts +96 -10
  264. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  265. package/types/lib/inventory/auditCategories.d.ts +3 -0
  266. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  267. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  268. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  269. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  270. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  271. package/types/lib/inventory/cacheDir.d.ts +12 -4
  272. package/types/lib/inventory/cacheDir.d.ts.map +1 -1
  273. package/types/lib/inventory/certificates.d.ts +205 -0
  274. package/types/lib/inventory/certificates.d.ts.map +1 -0
  275. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  276. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  277. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  278. package/types/lib/inventory/deps.d.ts +61 -2
  279. package/types/lib/inventory/deps.d.ts.map +1 -1
  280. package/types/lib/inventory/display.d.ts +18 -0
  281. package/types/lib/inventory/display.d.ts.map +1 -1
  282. package/types/lib/inventory/dosai.d.ts +202 -14
  283. package/types/lib/inventory/dosai.d.ts.map +1 -1
  284. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  285. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  286. package/types/lib/inventory/dynamic.d.ts +16 -1
  287. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  288. package/types/lib/inventory/envcontext.d.ts +40 -2
  289. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  290. package/types/lib/inventory/evidenceUtils.d.ts +48 -14
  291. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  292. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  293. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  294. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  295. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  296. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  297. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  298. package/types/lib/inventory/goBuildInfo.d.ts +10 -0
  299. package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
  300. package/types/lib/inventory/hbom.d.ts.map +1 -1
  301. package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
  302. package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
  303. package/types/lib/inventory/hostTopology.d.ts.map +1 -1
  304. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  305. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  306. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  307. package/types/lib/inventory/osPackagePurl.d.ts +99 -0
  308. package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
  309. package/types/lib/inventory/osPackageResolver.d.ts +6 -2
  310. package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
  311. package/types/lib/inventory/osinfo.d.ts +31 -6
  312. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  313. package/types/lib/inventory/osqueryTransform.d.ts +51 -0
  314. package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
  315. package/types/lib/inventory/parentSelection.d.ts +34 -0
  316. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  317. package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
  318. package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
  319. package/types/lib/inventory/phpHostProbe.d.ts +35 -0
  320. package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
  321. package/types/lib/inventory/rootfsCerts.d.ts +27 -0
  322. package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
  323. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  324. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  325. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  326. package/types/lib/inventory/source.d.ts +28 -0
  327. package/types/lib/inventory/source.d.ts.map +1 -1
  328. package/types/lib/inventory/spdx.d.ts +24 -0
  329. package/types/lib/inventory/spdx.d.ts.map +1 -1
  330. package/types/lib/inventory/traceRunner.d.ts +16 -1
  331. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  332. package/types/lib/inventory/tracedCrypto.d.ts +64 -0
  333. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  334. package/types/lib/managers/binary.d.ts +10 -0
  335. package/types/lib/managers/binary.d.ts.map +1 -1
  336. package/types/lib/managers/docker.d.ts +12 -0
  337. package/types/lib/managers/docker.d.ts.map +1 -1
  338. package/types/lib/managers/oci.d.ts +3 -1
  339. package/types/lib/managers/oci.d.ts.map +1 -1
  340. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  341. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  342. package/types/lib/parsers/cmakeLists.d.ts +2 -1
  343. package/types/lib/parsers/cmakeLists.d.ts.map +1 -1
  344. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  345. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  346. package/types/lib/parsers/compileCommands.d.ts +115 -0
  347. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  348. package/types/lib/parsers/email.d.ts +19 -0
  349. package/types/lib/parsers/email.d.ts.map +1 -0
  350. package/types/lib/parsers/largeJson.d.ts +13 -0
  351. package/types/lib/parsers/largeJson.d.ts.map +1 -1
  352. package/types/lib/parsers/strictJson.d.ts +46 -0
  353. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  354. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  355. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  356. package/types/lib/parsers/terraformSource.d.ts +66 -0
  357. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  358. package/types/lib/server/server.d.ts +8 -0
  359. package/types/lib/server/server.d.ts.map +1 -1
  360. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  361. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -1
  362. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  363. package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
  364. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  365. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  366. package/types/lib/stages/pregen/pregen.d.ts +14 -1
  367. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  368. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  369. package/types/lib/validator/index.d.ts +8 -0
  370. package/types/lib/validator/index.d.ts.map +1 -1
  371. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  372. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
@@ -5,6 +5,7 @@ import process from "node:process";
5
5
  import { Purl } from "@cdxgen/cdx-purl";
6
6
 
7
7
  import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
8
+ import { recordDegradation } from "../core/buildLedger.js";
8
9
  import { PROJECT_TYPE_ALIASES } from "../core/env.js";
9
10
  import {
10
11
  getAllFiles,
@@ -15,14 +16,20 @@ import {
15
16
  safeRmSync,
16
17
  safeWriteSync,
17
18
  } from "../core/fs.js";
19
+ import { isProjectToolingAllowed } from "../core/projectTooling.js";
20
+ import { hasCppSources } from "../ecosystems/cppEvidence.js";
21
+ import {
22
+ createIncludeAttributor,
23
+ parseCIncludeSlices,
24
+ } from "../ecosystems/cppIncludeResolver.js";
18
25
  import {
19
26
  analyzeGolemProject,
20
27
  collectGolemEvidence,
28
+ collectGolemServices,
21
29
  isGolemGoLanguage,
22
30
  } from "../ecosystems/golem.js";
23
31
  import {
24
32
  collectGradleDependencies,
25
- getGradleCommand,
26
33
  getMavenCommand,
27
34
  } from "../ecosystems/gradleutils.js";
28
35
  import {
@@ -40,6 +47,14 @@ import {
40
47
  isRusiRustLanguage,
41
48
  } from "../ecosystems/rusi.js";
42
49
  import {
50
+ analyzeScalaProject,
51
+ collectScalasemApiEndpoints,
52
+ collectScalasemEvidence,
53
+ collectScalasemServices,
54
+ isScalasemLanguage,
55
+ } from "../ecosystems/scalasem.js";
56
+ import {
57
+ atomBuildToolArgs,
43
58
  atomCompileCommandsArgs,
44
59
  buildAtomCommandEnv,
45
60
  executeAtom,
@@ -53,6 +68,7 @@ import {
53
68
  collectDosaiCryptoComponents,
54
69
  findCryptoAlgos,
55
70
  } from "../inventory/cbomutils.js";
71
+ import { cmakeBuildDirCandidates } from "../inventory/cmakeBuildDirs.js";
56
72
  import { collectMvnDependencies } from "../inventory/deps.js";
57
73
  import { mergeServices } from "../inventory/depsUtils.js";
58
74
  import {
@@ -64,6 +80,10 @@ import {
64
80
  collectDosaiServicesFromMethods,
65
81
  createDosaiDataFlowSlice,
66
82
  createDosaiMethodsSlice,
83
+ dosaiFailureReported,
84
+ dosaiOmittedSections,
85
+ dosaiReportFileOmittedSections,
86
+ dosaiSectionsCdxgenReads,
67
87
  isDosaiDotnetLanguage,
68
88
  normalizeDosaiServiceMap,
69
89
  persistDosaiSemanticsReport,
@@ -141,6 +161,81 @@ function filterAtomSliceData(sliceData, options = {}) {
141
161
  return filterAtomSlicesByExcludePatterns(sliceData, options.exclude);
142
162
  }
143
163
 
164
+ /**
165
+ * The project type whose evidence a package URL type belongs to. Generic
166
+ * purls are left out because many collectors use that type; they count as C
167
+ * only when the tree holds C or C++ sources.
168
+ */
169
+ const EVIDENCE_LANGUAGE_BY_PURL_TYPE = {
170
+ cargo: "rust",
171
+ cocoapods: "swift",
172
+ composer: "php",
173
+ conan: "c",
174
+ gem: "ruby",
175
+ golang: "go",
176
+ maven: "java",
177
+ npm: "js",
178
+ nuget: "csharp",
179
+ pypi: "python",
180
+ swift: "swift",
181
+ };
182
+
183
+ /**
184
+ * The evidence language of a `cdxgen --evidence` run that names no project
185
+ * type.
186
+ *
187
+ * A directory with a Scala build takes the scalasem path. Otherwise the
188
+ * generated BOM decides: Java when it lists Maven packages, as before, and
189
+ * else the language most of its packages belong to. A BOM with no package in
190
+ * a language the evidence step analyses gives no language, so no analyzer
191
+ * runs over sources it cannot read.
192
+ *
193
+ * @param {string} dirPath Project directory
194
+ * @param {Object} [bomJson] The BOM generated for the directory
195
+ * @param {Object} [options] CLI options, for the exclude patterns
196
+ * @returns {string|undefined} Project type for the evidence phase
197
+ */
198
+ export function defaultEvidenceLanguage(dirPath, bomJson, options = {}) {
199
+ const scalaBuildFiles = [
200
+ "build.sbt",
201
+ "build.sc",
202
+ "build.mill",
203
+ "project.scala",
204
+ ];
205
+ if (
206
+ scalaBuildFiles.some((file) => safeExistsSync(join(dirPath || ".", file)))
207
+ ) {
208
+ return "scala";
209
+ }
210
+ const counts = new Map();
211
+ let cppSources;
212
+ for (const component of bomJson?.components || []) {
213
+ const purl = `${component?.purl || ""}`;
214
+ if (!purl.startsWith("pkg:") || !purl.includes("/")) {
215
+ continue;
216
+ }
217
+ const purlType = purl.slice(4, purl.indexOf("/")).toLowerCase();
218
+ let language = EVIDENCE_LANGUAGE_BY_PURL_TYPE[purlType];
219
+ if (!language && purlType === "generic") {
220
+ cppSources ??= hasCppSources(dirPath || ".", options);
221
+ language = cppSources ? "c" : undefined;
222
+ }
223
+ if (language) {
224
+ counts.set(language, (counts.get(language) || 0) + 1);
225
+ }
226
+ }
227
+ if (counts.has("java")) {
228
+ return "java";
229
+ }
230
+ let chosen;
231
+ for (const [language, count] of counts) {
232
+ if (!chosen || count > counts.get(chosen)) {
233
+ chosen = language;
234
+ }
235
+ }
236
+ return chosen;
237
+ }
238
+
144
239
  /**
145
240
  * Build the options for the evinse phase (prepareDB, analyzeProject, and
146
241
  * createEvinseFile) from the cdxgen CLI options.
@@ -178,6 +273,18 @@ export function buildEvinseOptions(options, args, input) {
178
273
  profile: options.profile,
179
274
  jsonPretty: options.jsonPretty,
180
275
  exclude: options.exclude,
276
+ // Forwarded so a failed rusi/golem run can claim the --fail-on-error exit
277
+ // instead of degrading the reachability evidence in silence.
278
+ failOnError: options.failOnError,
279
+ // The scala analyzer reads these: a command override, the test-source
280
+ // switch and the install policy that decides whether builds may run.
281
+ scalasemCommand: options.scalasemCommand,
282
+ scalasemIncludeTests: options.scalasemIncludeTests,
283
+ scalasem: options.scalasem,
284
+ installDeps: options.installDeps,
285
+ requiredOnly: options.requiredOnly,
286
+ // --deep publishes the namespaces the Scala classpath join reads.
287
+ scalasemNamespaces: options.deep,
181
288
  // Absolute path of the deps slice createCsharpBom writes for --deep runs,
182
289
  // so analyzeProject can reuse it instead of rerunning dosai methods.
183
290
  depsSlicesFile: options.depsSlicesFile
@@ -239,7 +346,7 @@ export async function prepareDB(options) {
239
346
  await catalogMavenDeps(dirPath, purlsJars, Namespaces, options);
240
347
  }
241
348
  if (gradleFiles?.length) {
242
- await catalogGradleDeps(dirPath, purlsJars, Namespaces);
349
+ await catalogGradleDeps(dirPath, purlsJars, Namespaces, options);
243
350
  }
244
351
  }
245
352
  for (const purl of Object.keys(purlsToSlice)) {
@@ -248,11 +355,42 @@ export async function prepareDB(options) {
248
355
  return { sequelize, Namespaces, Usages, DataFlows };
249
356
  }
250
357
 
358
+ /**
359
+ * Read the jar namespace map cdxgen writes next to the BOM it generates
360
+ * (`<bom>.map`). The map for the BOM passed as `--input` is preferred; a
361
+ * `bom.json.map` in the project directory is the fallback.
362
+ *
363
+ * @param {string} dirPath Project directory scanned by evinse
364
+ * @param {Object} [options] CLI options, for `input`
365
+ * @returns {Object|undefined} The mapping, when a non-empty one exists.
366
+ */
367
+ export function readNamespaceMap(dirPath, options = {}) {
368
+ const candidates = [
369
+ options?.input ? `${options.input}.map` : undefined,
370
+ join(dirPath, "bom.json.map"),
371
+ ];
372
+ for (const mapFile of candidates) {
373
+ if (!mapFile || !safeExistsSync(mapFile)) {
374
+ continue;
375
+ }
376
+ try {
377
+ const mapData = JSON.parse(fs.readFileSync(mapFile, "utf-8"));
378
+ if (mapData && Object.keys(mapData).length) {
379
+ return mapData;
380
+ }
381
+ } catch (_err) {
382
+ // An unreadable map is the same as no map.
383
+ }
384
+ }
385
+ return undefined;
386
+ }
387
+
251
388
  /**
252
389
  * Collect Maven jar namespace mappings for the purls found in the SBOM.
253
390
  *
254
- * If a previously generated `bom.json.map` is present in `dirPath` it is reused;
255
- * otherwise the maven command is invoked to collect the jar dependencies. Each
391
+ * If cdxgen left a namespace map next to the BOM (see {@link readNamespaceMap})
392
+ * it is reused; otherwise the maven command is invoked to collect the jar
393
+ * dependencies. Each
256
394
  * resolved purl is recorded in the `purlsJars` map and persisted in the
257
395
  * `Namespaces` model.
258
396
  *
@@ -268,22 +406,26 @@ export async function catalogMavenDeps(
268
406
  Namespaces,
269
407
  options = {},
270
408
  ) {
271
- let jarNSMapping;
272
- if (safeExistsSync(join(dirPath, "bom.json.map"))) {
273
- try {
274
- const mapData = JSON.parse(
275
- fs.readFileSync(join(dirPath, "bom.json.map"), "utf-8"),
409
+ let jarNSMapping = readNamespaceMap(dirPath, options);
410
+ if (!jarNSMapping && !isProjectToolingAllowed(options)) {
411
+ // `mvn dependency:copy-dependencies` evaluates the project's build and
412
+ // its `.mvn` configuration, so only the local repository is read.
413
+ console.log(
414
+ options.withDeepJarCollector
415
+ ? "Skipping 'mvn dependency:copy-dependencies' since dependency installation is disabled. Only the jars already in the local Maven repository are used."
416
+ : "Skipping 'mvn dependency:copy-dependencies' since dependency installation is disabled. Pass --with-deep-jar-collector to use the jars already in the local Maven repository.",
417
+ );
418
+ if (options.withDeepJarCollector) {
419
+ jarNSMapping = await collectMvnDependencies(
420
+ undefined,
421
+ undefined,
422
+ true,
423
+ true,
276
424
  );
277
- if (mapData && Object.keys(mapData).length) {
278
- jarNSMapping = mapData;
279
- }
280
- } catch (_err) {
281
- // ignore
282
425
  }
283
- }
284
- if (!jarNSMapping) {
426
+ } else if (!jarNSMapping) {
285
427
  console.log("About to collect jar dependencies for the path", dirPath);
286
- const mavenCmd = getMavenCommand(dirPath, dirPath);
428
+ const mavenCmd = getMavenCommand(dirPath, dirPath, options);
287
429
  // collect all jars including from the cache if data-flow mode is enabled
288
430
  jarNSMapping = await collectMvnDependencies(
289
431
  mavenCmd,
@@ -324,18 +466,28 @@ export async function catalogMavenDeps(
324
466
  * @param {Object} Namespaces Sequelize-like Namespaces model used to persist pom and namespaces
325
467
  * @returns {Promise<void>}
326
468
  */
327
- export async function catalogGradleDeps(dirPath, purlsJars, Namespaces) {
328
- console.log(
329
- "About to collect jar dependencies from the gradle cache. This would take a while ...",
330
- );
331
- const gradleCmd = getGradleCommand(dirPath, dirPath);
332
- // collect all jars including from the cache if data-flow mode is enabled
333
- const jarNSMapping = await collectGradleDependencies(
334
- gradleCmd,
335
- dirPath,
336
- false,
337
- true,
338
- );
469
+ export async function catalogGradleDeps(
470
+ dirPath,
471
+ purlsJars,
472
+ Namespaces,
473
+ options = {},
474
+ ) {
475
+ // The map cdxgen wrote next to the BOM already holds the namespaces; the
476
+ // Gradle cache is walked only without it.
477
+ let jarNSMapping = readNamespaceMap(dirPath, options);
478
+ if (!jarNSMapping) {
479
+ console.log(
480
+ "About to collect jar dependencies from the gradle cache. This would take a while ...",
481
+ );
482
+ // Only the Gradle cache is read, so no Gradle command is resolved: that
483
+ // would probe the project's own gradlew.
484
+ jarNSMapping = await collectGradleDependencies(
485
+ undefined,
486
+ dirPath,
487
+ false,
488
+ true,
489
+ );
490
+ }
339
491
  if (jarNSMapping) {
340
492
  for (const purl of Object.keys(jarNSMapping)) {
341
493
  purlsJars[purl] = jarNSMapping[purl].jarFile;
@@ -515,7 +667,9 @@ export async function createSlice(
515
667
  resolve(slicesFile),
516
668
  options,
517
669
  );
518
- if (!sliceResult) {
670
+ // A stopped run, or one the .NET host refused to start, has already said
671
+ // why; it says nothing about whether the project is supported (issue 4438).
672
+ if (!sliceResult && !dosaiFailureReported(resolve(slicesFile))) {
519
673
  console.warn(
520
674
  `Unable to generate ${sliceType} slice using dosai. Check if this is a supported .NET project.`,
521
675
  );
@@ -526,6 +680,13 @@ export async function createSlice(
526
680
  slicesFile,
527
681
  };
528
682
  }
683
+ const buildToolArgs = atomBuildToolArgs(language, options);
684
+ if (!buildToolArgs) {
685
+ console.log(
686
+ `Skipping the ${sliceType} slice for ${resolve(filePath)} since dependency installation is disabled and the installed atom cannot be stopped from running Maven. Upgrade atom to slice this project without its build.`,
687
+ );
688
+ return { tempDir: sliceOutputDir, tempDirOwned };
689
+ }
529
690
  console.log(
530
691
  `Creating ${sliceType} slice for ${resolve(filePath)}. Please wait ...`,
531
692
  );
@@ -552,15 +713,9 @@ export async function createSlice(
552
713
  "Use an official cdxgen container image to improve the precision of endpoints detection (for SaaSBOM).",
553
714
  );
554
715
  }
555
- if (["ruby", "scala"].includes(language)) {
716
+ if (language === "ruby") {
556
717
  args.push("--remove-atom");
557
718
  }
558
- if (["scala"].includes(language)) {
559
- semanticsSlicesFile = join(
560
- sliceOutputDir,
561
- basename(options.semanticsSlicesFile || "semantics.slices.json"),
562
- );
563
- }
564
719
  }
565
720
  args = args.concat([
566
721
  "-l",
@@ -580,6 +735,7 @@ export async function createSlice(
580
735
  args.push(readEnvironmentVariable("ATOM_SLICE_DEPTH"));
581
736
  }
582
737
  args.push(...atomCompileCommandsArgs(filePath, language, options));
738
+ args.push(...buildToolArgs);
583
739
  args.push(resolve(filePath));
584
740
  const atomExcludeEnv = buildAtomCommandEnv(options, language);
585
741
  // Execute atom
@@ -632,15 +788,6 @@ export async function createSlice(
632
788
  `openapi spec file "${join(filePath, openapiSpecFile)}" was not generated successfully. Check if atom-tools pypi package is installed and available in PATH.`,
633
789
  );
634
790
  }
635
- if (
636
- ["scala"].includes(language) &&
637
- sliceType === "usages" &&
638
- !safeExistsSync(semanticsSlicesFile)
639
- ) {
640
- console.log(
641
- `Semantics slices file "${semanticsSlicesFile}" was not generated successfully. Try running atom cli in Java mode.`,
642
- );
643
- }
644
791
  return {
645
792
  tempDir: sliceOutputDir,
646
793
  tempDirOwned,
@@ -769,8 +916,8 @@ function usableSlicesFile(slicesFile) {
769
916
  * describe the same project directory and must not predate the input SBOM:
770
917
  * `cdxgen --evidence` writes a fresh SBOM before evinse runs, so an older
771
918
  * slice was made from an earlier state of the sources and is regenerated.
772
- * Scala slices cannot be regenerated by evinse and are reused whenever their
773
- * shape matches.
919
+ * Scala reports are handled by the scalasem analyzer, which applies its own
920
+ * rules.
774
921
  *
775
922
  * @param {string} language Project language
776
923
  * @param {string} slicesFile Candidate semantics slices file
@@ -797,10 +944,7 @@ export function loadReusableSemanticsSlice(
797
944
  return undefined;
798
945
  }
799
946
  if (!PROJECT_TYPE_ALIASES.swift.includes(language)) {
800
- // Scala semantics slices are keyed by .scala source file names
801
- return Object.keys(parsed).some((key) => key.endsWith(".scala"))
802
- ? parsed
803
- : undefined;
947
+ return undefined;
804
948
  }
805
949
  if (!(parsed.fileIndexes || parsed.buildSymbols || parsed.moduleInfos)) {
806
950
  return undefined;
@@ -818,6 +962,260 @@ export function loadReusableSemanticsSlice(
818
962
  return parsed;
819
963
  }
820
964
 
965
+ /**
966
+ * The Scala evidence path: scalasem only, never atom. The branch returns
967
+ * early for every entry point (evinse, --evidence, cbom, saasbom) and every
968
+ * Scala project type, so no usages, reachables, data-flow or atom-run
969
+ * scalasem happens here. Slices the user passes are still read: a version 1
970
+ * semantics slice keeps its usedTypes join, usages and reachables slices
971
+ * keep their own evidence, and an OpenAPI spec keeps its services.
972
+ *
973
+ * @param {string} language Project language, normalized to `scala`.
974
+ * @param {string} dirPath Project directory.
975
+ * @param {Object} options Command line options.
976
+ * @param {Object} bomJson Parsed input SBOM.
977
+ * @param {Object[]} components Components of the input SBOM.
978
+ * @param {Object} purlLocationMap Occurrences seeded from the input SBOM.
979
+ * @param {Object} purlImportsMap Imports seeded from the input SBOM.
980
+ * @param {Object} dbObjMap The evinse database objects.
981
+ * @returns {Promise<Object>} Slice artefacts for `createEvinseFile`.
982
+ */
983
+ async function analyzeScalaEvidenceProject(
984
+ language,
985
+ dirPath,
986
+ options,
987
+ bomJson,
988
+ components,
989
+ purlLocationMap,
990
+ purlImportsMap,
991
+ dbObjMap,
992
+ ) {
993
+ let servicesMap = {};
994
+ const dataFlowFrames = {};
995
+ let componentPropertiesMap = {};
996
+ let metadataProperties = [];
997
+ let cryptoComponents = [];
998
+ const cryptoGeneratePurls = {};
999
+ const mergeLocations = (locationMap) => {
1000
+ for (const [purl, locations] of Object.entries(locationMap || {})) {
1001
+ purlLocationMap[purl] ??= new Set();
1002
+ for (const location of locations) {
1003
+ purlLocationMap[purl].add(location);
1004
+ }
1005
+ }
1006
+ };
1007
+ const mergeFrames = (framesMap) => {
1008
+ for (const [purl, stacks] of Object.entries(framesMap || {})) {
1009
+ dataFlowFrames[purl] = [...(dataFlowFrames[purl] || []), ...stacks];
1010
+ }
1011
+ };
1012
+ const mergeProvides = (providesMap) => {
1013
+ for (const [purl, refs] of Object.entries(providesMap || {})) {
1014
+ cryptoGeneratePurls[purl] ??= new Set();
1015
+ for (const ref of refs || []) {
1016
+ cryptoGeneratePurls[purl].add(ref);
1017
+ }
1018
+ }
1019
+ };
1020
+ const analysis = analyzeScalaProject(dirPath, options);
1021
+ if (analysis?.report) {
1022
+ const evidence = await collectScalasemEvidence(
1023
+ analysis.report,
1024
+ [bomJson.metadata?.component, ...components].filter(Boolean),
1025
+ { ...options, scalaNamespaceMap: readNamespaceMap(dirPath, options) },
1026
+ );
1027
+ mergeLocations(evidence.purlLocationMap);
1028
+ mergeFrames(evidence.dataFlowFrames);
1029
+ mergeProvides(evidence.cryptoGeneratePurls);
1030
+ componentPropertiesMap = evidence.componentPropertiesMap;
1031
+ cryptoComponents = evidence.cryptoComponents;
1032
+ metadataProperties = evidence.metadataProperties;
1033
+ servicesMap = collectScalasemServices(analysis.report, servicesMap);
1034
+ servicesMap = collectScalasemApiEndpoints(analysis.report, servicesMap);
1035
+ }
1036
+ if (analysis?.metadataProperties?.length) {
1037
+ for (const property of analysis.metadataProperties) {
1038
+ if (
1039
+ !metadataProperties.some(
1040
+ (known) =>
1041
+ known.name === property.name && known.value === property.value,
1042
+ )
1043
+ ) {
1044
+ metadataProperties.push(property);
1045
+ }
1046
+ }
1047
+ }
1048
+ // A version 1 semantics slice still goes through the usedTypes join, merged
1049
+ // with the report evidence rather than replacing it.
1050
+ if (analysis?.v1Slice) {
1051
+ mergeLocations(
1052
+ findPurlLocations(components, analysis.v1Slice).purlLocationMap,
1053
+ );
1054
+ }
1055
+ // Usages and reachables slices the user made, with atom or otherwise, are
1056
+ // read as they are for any other language; none is ever generated here.
1057
+ let usagesSlicesFile;
1058
+ let reachablesSlicesFile;
1059
+ if (
1060
+ (options.withReachables || options.includeCrypto) &&
1061
+ usableSlicesFile(options.reachablesSlicesFile)
1062
+ ) {
1063
+ reachablesSlicesFile = options.reachablesSlicesFile;
1064
+ const reachables = collectReachableFrames(
1065
+ language,
1066
+ filterAtomSliceData(readReachablesSlices(reachablesSlicesFile), options),
1067
+ );
1068
+ mergeFrames(reachables.dataFlowFrames);
1069
+ mergeProvides(reachables.cryptoGeneratePurls);
1070
+ const knownRefs = new Set(
1071
+ cryptoComponents.map((asset) => asset["bom-ref"]),
1072
+ );
1073
+ cryptoComponents = cryptoComponents.concat(
1074
+ (reachables.cryptoComponents || []).filter(
1075
+ (asset) => !knownRefs.has(asset["bom-ref"]),
1076
+ ),
1077
+ );
1078
+ }
1079
+ let userDefinedTypesMap = {};
1080
+ if (usableSlicesFile(options.usagesSlicesFile)) {
1081
+ usagesSlicesFile = options.usagesSlicesFile;
1082
+ const usageSlice = filterAtomSliceData(
1083
+ readSlicesFile(usagesSlicesFile, "usages"),
1084
+ options,
1085
+ );
1086
+ if (usageSlice && Object.keys(usageSlice).length) {
1087
+ const retMap = await parseObjectSlices(
1088
+ language,
1089
+ usageSlice,
1090
+ dbObjMap,
1091
+ servicesMap,
1092
+ purlLocationMap,
1093
+ purlImportsMap,
1094
+ );
1095
+ servicesMap = retMap.servicesMap || servicesMap;
1096
+ userDefinedTypesMap = retMap.userDefinedTypesMap || {};
1097
+ }
1098
+ }
1099
+ // An OpenAPI spec the user has (atom-tools' conversion of this report, or
1100
+ // the project's own) adds its routes to the report's: a route both name is
1101
+ // one service.
1102
+ const openapiSpecFile = options.openapiSpecFile
1103
+ ? resolve(dirPath, options.openapiSpecFile)
1104
+ : undefined;
1105
+ if (openapiSpecFile && safeExistsSync(openapiSpecFile)) {
1106
+ const openapiServices = {};
1107
+ detectServicesFromOpenAPI(language, openapiSpecFile, openapiServices);
1108
+ mergeServiceDefinitions(servicesMap, openapiServices);
1109
+ }
1110
+ // A BOM without a project component still needs somewhere for the run's
1111
+ // metadata properties to live; a nameless entry would explain nothing.
1112
+ let workspaceComponent;
1113
+ if (!bomJson.metadata?.component) {
1114
+ workspaceComponent = {
1115
+ type: "application",
1116
+ name: basename(resolve(dirPath)),
1117
+ };
1118
+ }
1119
+ return {
1120
+ purlLocationMap,
1121
+ servicesMap,
1122
+ dataFlowFrames,
1123
+ componentPropertiesMap,
1124
+ metadataProperties,
1125
+ cryptoComponents,
1126
+ cryptoGeneratePurls,
1127
+ usagesSlicesFile,
1128
+ reachablesSlicesFile,
1129
+ semanticsSlicesFile: analysis?.reportFile,
1130
+ userDefinedTypesMap,
1131
+ workspaceComponent,
1132
+ };
1133
+ }
1134
+
1135
+ /**
1136
+ * Merge service definitions into a services map: endpoints join, and a
1137
+ * property another source already carries is not added again, whatever the
1138
+ * case of an HTTP method.
1139
+ *
1140
+ * @param {Object} target Services map that receives the definitions.
1141
+ * @param {Object} source Services map to merge in.
1142
+ * @returns {Object} The target map.
1143
+ */
1144
+ export function mergeServiceDefinitions(target, source) {
1145
+ const sameProperty = (left, right) =>
1146
+ left.name === right.name &&
1147
+ (left.name === "cdx:service:httpMethod"
1148
+ ? String(left.value).toUpperCase() === String(right.value).toUpperCase()
1149
+ : left.value === right.value);
1150
+ for (const [name, definition] of Object.entries(source || {})) {
1151
+ const existing = target[name];
1152
+ if (!existing) {
1153
+ target[name] = definition;
1154
+ continue;
1155
+ }
1156
+ existing.endpoints ??= new Set();
1157
+ for (const endpoint of definition.endpoints || []) {
1158
+ existing.endpoints.add(endpoint);
1159
+ }
1160
+ existing.properties ??= [];
1161
+ for (const property of definition.properties || []) {
1162
+ if (!existing.properties.some((known) => sameProperty(known, property))) {
1163
+ existing.properties.push(property);
1164
+ }
1165
+ }
1166
+ if (definition.authenticated) {
1167
+ existing.authenticated = true;
1168
+ }
1169
+ if (definition.xTrustBoundary) {
1170
+ existing.xTrustBoundary = true;
1171
+ }
1172
+ }
1173
+ return target;
1174
+ }
1175
+
1176
+ /**
1177
+ * Collect the services of a Go project for the golem evidence path (#4498).
1178
+ *
1179
+ * Two sources are kept and merged by service name, the way the Scala path
1180
+ * merges a user-provided spec into its report's services. They roll services
1181
+ * up differently — golem per server, OpenAPI per path and method — so a
1182
+ * route both name appears under both entries:
1183
+ *
1184
+ * - the golem report itself, whose `services[]` section rolls the registered
1185
+ * routes up per server (a gin engine, a net/http listener) with methods and
1186
+ * frameworks, and the outbound hosts it calls into `kind: "external"`
1187
+ * services flagged as trust-boundary crossings;
1188
+ * - an OpenAPI document, either the one `--openapi-spec-file` names or the
1189
+ * `openapi.json` an earlier `atom-tools convert -t go` left in the repo,
1190
+ * which is reused when present so `golem`/`atom-tools` and `cdxgen` can be
1191
+ * run as separate steps.
1192
+ *
1193
+ * @param {object|null|undefined} golemReport Parsed golem analysis report,
1194
+ * when one was produced; a missing or `null` report adds no services.
1195
+ * @param {string} dirPath Project directory the report was produced from.
1196
+ * @param {Object} options Command line options.
1197
+ * @param {Object} [servicesMap={}] Service map to populate.
1198
+ * @returns {Object} The service map
1199
+ */
1200
+ export function collectGoServices(
1201
+ golemReport,
1202
+ dirPath,
1203
+ options = {},
1204
+ servicesMap = {},
1205
+ ) {
1206
+ servicesMap = collectGolemServices(golemReport, servicesMap);
1207
+ const openapiSpecFile = resolve(
1208
+ dirPath,
1209
+ options.openapiSpecFile || "openapi.json",
1210
+ );
1211
+ if (safeExistsSync(openapiSpecFile)) {
1212
+ const openapiServices = {};
1213
+ detectServicesFromOpenAPI("go", openapiSpecFile, openapiServices);
1214
+ mergeServiceDefinitions(servicesMap, openapiServices);
1215
+ }
1216
+ return servicesMap;
1217
+ }
1218
+
821
1219
  /**
822
1220
  * Function to analyze the project
823
1221
  *
@@ -827,7 +1225,12 @@ export function loadReusableSemanticsSlice(
827
1225
  export async function analyzeProject(dbObjMap, options) {
828
1226
  const dirPath = options._[0] || ".";
829
1227
  const languages = options.language;
830
- const language = Array.isArray(languages) ? languages[0] : languages;
1228
+ let language = Array.isArray(languages) ? languages[0] : languages;
1229
+ // cdxgen --evidence forwards the raw project type, so -t sbt, -t mill and
1230
+ // -t scala3 must be normalized once to behave like -l scala everywhere.
1231
+ if (PROJECT_TYPE_ALIASES.scala.includes(language)) {
1232
+ language = "scala";
1233
+ }
831
1234
  let usageSlice;
832
1235
  let dataFlowSlice;
833
1236
  let reachablesSlice;
@@ -853,6 +1256,7 @@ export async function analyzeProject(dbObjMap, options) {
853
1256
 
854
1257
  if (isGolemGoLanguage(language)) {
855
1258
  const golemReport = analyzeGolemProject(dirPath, options);
1259
+ servicesMap = collectGoServices(golemReport, dirPath, options, servicesMap);
856
1260
  if (golemReport && Object.keys(golemReport).length) {
857
1261
  const golemEvidence = collectGolemEvidence(golemReport, [
858
1262
  bomJson.metadata?.component,
@@ -981,29 +1385,76 @@ export async function analyzeProject(dbObjMap, options) {
981
1385
  };
982
1386
  }
983
1387
 
1388
+ if (isScalasemLanguage(language)) {
1389
+ return analyzeScalaEvidenceProject(
1390
+ language,
1391
+ dirPath,
1392
+ options,
1393
+ bomJson,
1394
+ components,
1395
+ purlLocationMap,
1396
+ purlImportsMap,
1397
+ dbObjMap,
1398
+ );
1399
+ }
1400
+
984
1401
  if (isDosaiDotnetLanguage(language)) {
985
1402
  if (options.profile === "research") {
986
1403
  options.withDataFlow = true;
987
1404
  options.includeCrypto = true;
988
1405
  }
1406
+ let needsUsagesRun = true;
989
1407
  if (
990
1408
  options.usagesSlicesFile &&
991
1409
  usableSlicesFile(options.usagesSlicesFile)
992
1410
  ) {
993
1411
  usageSlice = readDosaiMethodsReport(options.usagesSlicesFile);
994
1412
  usagesSlicesFile = options.usagesSlicesFile;
1413
+ needsUsagesRun = false;
1414
+ // A user-supplied usages slice is used as given. When it was written
1415
+ // with --sections and left out a section cdxgen reads, the evidence
1416
+ // lacks that input; persistDosaiSemanticsReport reports separately
1417
+ // that the persisted report is partial.
1418
+ const missingInputs = dosaiSectionsCdxgenReads(
1419
+ dosaiOmittedSections(usageSlice),
1420
+ );
1421
+ if (missingInputs.length) {
1422
+ recordDegradation("dosai.usages.trimmed-report", {
1423
+ ecosystem: "csharp",
1424
+ tool: "dosai",
1425
+ impact: "components",
1426
+ command: `usages ${usagesSlicesFile}`,
1427
+ detail: `The supplied dosai methods report was written without sections cdxgen reads: ${missingInputs.join(", ")}. The evidence from those sections is missing. Run dosai methods without --sections, or with every section cdxgen reads.`,
1428
+ });
1429
+ }
995
1430
  } else if (usableSlicesFile(options.depsSlicesFile)) {
996
1431
  // createCsharpBom already ran dosai methods on this tree for --deep runs
997
1432
  // and wrote the deps slice. For dosai, that output IS the usages slice,
998
- // so reuse it instead of invoking dosai a second time.
999
- if (DEBUG_MODE) {
1000
- console.log(
1001
- `Reusing the dosai deps slices file "${options.depsSlicesFile}" for the usages analysis.`,
1002
- );
1433
+ // so reuse it instead of invoking dosai a second time - unless it was
1434
+ // written with --sections (a --deep run that performs no evidence
1435
+ // analysis trims it): evinse persists this file as the full native
1436
+ // report, so it must not build on a trimmed one and dosai runs again.
1437
+ const omittedSections = dosaiReportFileOmittedSections(
1438
+ options.depsSlicesFile,
1439
+ );
1440
+ if (omittedSections.length) {
1441
+ if (DEBUG_MODE) {
1442
+ console.log(
1443
+ `The dosai deps slices file "${options.depsSlicesFile}" omits sections (${omittedSections.join(", ")}); running dosai for the full usages report.`,
1444
+ );
1445
+ }
1446
+ } else {
1447
+ if (DEBUG_MODE) {
1448
+ console.log(
1449
+ `Reusing the dosai deps slices file "${options.depsSlicesFile}" for the usages analysis.`,
1450
+ );
1451
+ }
1452
+ usageSlice = readDosaiMethodsReport(options.depsSlicesFile);
1453
+ usagesSlicesFile = options.depsSlicesFile;
1454
+ needsUsagesRun = false;
1003
1455
  }
1004
- usageSlice = readDosaiMethodsReport(options.depsSlicesFile);
1005
- usagesSlicesFile = options.depsSlicesFile;
1006
- } else {
1456
+ }
1457
+ if (needsUsagesRun) {
1007
1458
  retMap = await createSlice(language, dirPath, "usages", options);
1008
1459
  if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
1009
1460
  usageSlice = readDosaiMethodsReport(retMap.slicesFile);
@@ -1011,11 +1462,17 @@ export async function analyzeProject(dbObjMap, options) {
1011
1462
  }
1012
1463
  }
1013
1464
  if (usageSlice && Object.keys(usageSlice).length) {
1014
- const dosaiEvidence = collectDosaiPurlEvidence(usageSlice, components);
1465
+ // dosai locations are relative to the analyzed directory; a package the
1466
+ // BOM holds in several versions is matched by the project of the file.
1467
+ const dosaiEvidence = collectDosaiPurlEvidence(usageSlice, components, {
1468
+ srcPath: resolve(dirPath),
1469
+ });
1015
1470
  // Confidence/evidence facts ride along as properties so an unbuilt tree (Low
1016
1471
  // confidence, unresolved evidence) stays distinguishable from an only-imported
1017
1472
  // package in the emitted BOM.
1018
- applyDosaiReachabilityEvidence(usageSlice, components);
1473
+ applyDosaiReachabilityEvidence(usageSlice, components, {
1474
+ srcPath: resolve(dirPath),
1475
+ });
1019
1476
  for (const [purl, locations] of Object.entries(
1020
1477
  dosaiEvidence.purlLocationMap,
1021
1478
  )) {
@@ -1049,7 +1506,9 @@ export async function analyzeProject(dbObjMap, options) {
1049
1506
  }
1050
1507
  }
1051
1508
  if (dataFlowSlice && Object.keys(dataFlowSlice).length) {
1052
- dataFlowFrames = collectDosaiDataFlowFrames(dataFlowSlice, components);
1509
+ dataFlowFrames = collectDosaiDataFlowFrames(dataFlowSlice, components, {
1510
+ srcPath: resolve(dirPath),
1511
+ });
1053
1512
  }
1054
1513
  }
1055
1514
  if (options.includeCrypto) {
@@ -1145,12 +1604,8 @@ export async function analyzeProject(dbObjMap, options) {
1145
1604
  }
1146
1605
  }
1147
1606
  // Support for semantics slicing
1148
- if (
1149
- (PROJECT_TYPE_ALIASES.swift.includes(language) ||
1150
- PROJECT_TYPE_ALIASES.scala.includes(language)) &&
1151
- components.length
1152
- ) {
1153
- // Reuse existing semantics slices for swift and scala
1607
+ if (PROJECT_TYPE_ALIASES.swift.includes(language) && components.length) {
1608
+ // Reuse existing semantics slices for swift
1154
1609
  const reusableSlice = loadReusableSemanticsSlice(
1155
1610
  language,
1156
1611
  options.semanticsSlicesFile,
@@ -1160,7 +1615,7 @@ export async function analyzeProject(dbObjMap, options) {
1160
1615
  if (reusableSlice) {
1161
1616
  semanticsSlice = reusableSlice;
1162
1617
  semanticsSlicesFile = options.semanticsSlicesFile;
1163
- } else if (PROJECT_TYPE_ALIASES.swift.includes(language)) {
1618
+ } else {
1164
1619
  // Generate our own slices for swift
1165
1620
  retMap = await createSlice(language, dirPath, "semantics", options);
1166
1621
  if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
@@ -1169,6 +1624,28 @@ export async function analyzeProject(dbObjMap, options) {
1169
1624
  }
1170
1625
  }
1171
1626
  }
1627
+ let cIncludeLocations = new Map();
1628
+ // A C/C++ include names the file it resolved to and the functions the
1629
+ // including file calls from it (atom 4 and later): those functions are the
1630
+ // usages of the component that provides the file
1631
+ if (
1632
+ ["c", "cpp", "c++"].includes(language) &&
1633
+ usageSlice &&
1634
+ Object.keys(usageSlice).length
1635
+ ) {
1636
+ const attributed = attributeCIncludes(
1637
+ usageSlice,
1638
+ components,
1639
+ dirPath,
1640
+ options,
1641
+ );
1642
+ for (const [purl, names] of attributed.symbols) {
1643
+ purlImportsMap[purl] = Array.from(
1644
+ new Set([...(purlImportsMap[purl] || []), ...names]),
1645
+ );
1646
+ }
1647
+ cIncludeLocations = attributed.locations;
1648
+ }
1172
1649
  // Parse usage slices
1173
1650
  if (usageSlice && Object.keys(usageSlice).length) {
1174
1651
  const retMap = await parseObjectSlices(
@@ -1184,7 +1661,14 @@ export async function analyzeProject(dbObjMap, options) {
1184
1661
  servicesMap = retMap.servicesMap;
1185
1662
  userDefinedTypesMap = retMap.userDefinedTypesMap;
1186
1663
  }
1187
- // Parse the semantics slices for swift and scala
1664
+ // A component whose calls the slices do not show (code inside the project,
1665
+ // such as a vendored library) is still used where its header is included
1666
+ for (const [purl, includeLocations] of cIncludeLocations) {
1667
+ if (!purlLocationMap[purl]?.size) {
1668
+ purlLocationMap[purl] = new Set(includeLocations);
1669
+ }
1670
+ }
1671
+ // Parse the semantics slices for swift
1188
1672
  if (
1189
1673
  semanticsSlice &&
1190
1674
  Object.keys(semanticsSlice).length &&
@@ -1192,7 +1676,25 @@ export async function analyzeProject(dbObjMap, options) {
1192
1676
  ) {
1193
1677
  // Identify the purl locations
1194
1678
  const retMap = parseSemanticSlices(language, components, semanticsSlice);
1195
- purlLocationMap = retMap.purlLocationMap;
1679
+ // The usages step may already have locations for a purl; the semantics
1680
+ // pass adds its own rather than replacing them.
1681
+ for (const [apurl, locations] of Object.entries(
1682
+ retMap.purlLocationMap || {},
1683
+ )) {
1684
+ purlLocationMap[apurl] ??= new Set();
1685
+ for (const alocation of locations) {
1686
+ purlLocationMap[apurl].add(alocation);
1687
+ }
1688
+ }
1689
+ }
1690
+ // A user-supplied OpenAPI spec must yield services even when the usages
1691
+ // slice is empty.
1692
+ if (
1693
+ openapiSpecFile &&
1694
+ safeExistsSync(openapiSpecFile) &&
1695
+ !Object.keys(servicesMap).length
1696
+ ) {
1697
+ detectServicesFromOpenAPI(language, openapiSpecFile, servicesMap);
1196
1698
  }
1197
1699
  if (options.withDataFlow) {
1198
1700
  if (
@@ -1261,6 +1763,125 @@ export async function analyzeProject(dbObjMap, options) {
1261
1763
  * @param {string} [openapiSpecFile=undefined] Optional path to an OpenAPI spec used for service detection
1262
1764
  * @returns {Promise<Object>} Map with `purlLocationMap`, `servicesMap`, and `userDefinedTypesMap`
1263
1765
  */
1766
+ /**
1767
+ * Attribute the include slices of a C/C++ usages report to the BOM
1768
+ * components that provide the files they resolved to: each header's imported
1769
+ * symbols (the functions the including file calls that it declares), and the
1770
+ * include directive's location. Reports from atom releases that do not name
1771
+ * the resolved file attribute nothing.
1772
+ *
1773
+ * @param {Object} usageSlice Parsed usages report
1774
+ * @param {Object[]} components Components of the input BOM
1775
+ * @param {string} dirPath Source directory
1776
+ * @param {Object} options CLI options
1777
+ * @returns {{symbols: Map<string, Set<string>>, locations: Map<string, Set<string>>}}
1778
+ * Imported symbols and include locations (`file#line`) by purl
1779
+ */
1780
+ export function attributeCIncludes(
1781
+ usageSlice,
1782
+ components,
1783
+ dirPath,
1784
+ options = {},
1785
+ ) {
1786
+ const symbols = new Map();
1787
+ const locations = new Map();
1788
+ const includes = [...parseCIncludeSlices(usageSlice)].filter(
1789
+ ([, e]) => e.paths.size && e.symbols.size,
1790
+ );
1791
+ if (!includes.length || !components?.length) {
1792
+ return { symbols, locations };
1793
+ }
1794
+ const attribute = createIncludeAttributor({
1795
+ src: dirPath,
1796
+ components,
1797
+ buildDirs: cmakeBuildDirCandidates(dirPath, options),
1798
+ });
1799
+ const withPurl = components.filter((c) => c?.purl);
1800
+ const byName = (name, purlType) =>
1801
+ withPurl.find(
1802
+ (c) =>
1803
+ c.name?.toLowerCase() === `${name}`.toLowerCase() &&
1804
+ (!purlType || c.purl.startsWith(`pkg:${purlType}/`)),
1805
+ );
1806
+ const purlByHeader = new Map();
1807
+ for (const [header, entry] of includes) {
1808
+ const target = attribute(entry.paths);
1809
+ let component;
1810
+ if (target?.kind === "component") {
1811
+ component = target.component;
1812
+ } else if (target?.kind === "os") {
1813
+ component =
1814
+ withPurl.find((c) => c.purl === target.pkgInfo.purl) ||
1815
+ byName(target.pkgInfo.name);
1816
+ } else if (target?.kind === "vcpkg") {
1817
+ component = byName(target.port);
1818
+ } else if (target?.kind === "conan") {
1819
+ component = byName(target.name, "conan") || byName(target.name);
1820
+ }
1821
+ if (!component?.purl) {
1822
+ continue;
1823
+ }
1824
+ purlByHeader.set(header, component.purl);
1825
+ const known = symbols.get(component.purl) || new Set();
1826
+ for (const symbol of entry.symbols) {
1827
+ known.add(symbol);
1828
+ }
1829
+ symbols.set(component.purl, known);
1830
+ }
1831
+ // the directives that include an attributed header and use it
1832
+ for (const slice of usageSlice?.objectSlices || []) {
1833
+ const purl = purlByHeader.get(slice?.fullName);
1834
+ if (
1835
+ !purl ||
1836
+ !`${slice.code || ""}`.startsWith("#include") ||
1837
+ !slice.fileName ||
1838
+ !slice.importedSymbols?.length
1839
+ ) {
1840
+ continue;
1841
+ }
1842
+ const known = locations.get(purl) || new Set();
1843
+ known.add(
1844
+ slice.lineNumber
1845
+ ? `${slice.fileName}#${slice.lineNumber}`
1846
+ : slice.fileName,
1847
+ );
1848
+ locations.set(purl, known);
1849
+ }
1850
+ return { symbols, locations };
1851
+ }
1852
+
1853
+ /**
1854
+ * Seed the C/C++ imports from the include slices of a usages report (see
1855
+ * `attributeCIncludes`).
1856
+ *
1857
+ * @param {Object} usageSlice Parsed usages report
1858
+ * @param {Object[]} components Components of the input BOM
1859
+ * @param {string} dirPath Source directory
1860
+ * @param {Object} purlImportsMap Imports by purl, extended in place
1861
+ * @param {Object} options CLI options
1862
+ * @returns {Object} purlImportsMap
1863
+ */
1864
+ export function seedCImportsFromIncludes(
1865
+ usageSlice,
1866
+ components,
1867
+ dirPath,
1868
+ purlImportsMap = {},
1869
+ options = {},
1870
+ ) {
1871
+ const { symbols } = attributeCIncludes(
1872
+ usageSlice,
1873
+ components,
1874
+ dirPath,
1875
+ options,
1876
+ );
1877
+ for (const [purl, names] of symbols) {
1878
+ purlImportsMap[purl] = Array.from(
1879
+ new Set([...(purlImportsMap[purl] || []), ...names]),
1880
+ );
1881
+ }
1882
+ return purlImportsMap;
1883
+ }
1884
+
1264
1885
  export async function parseObjectSlices(
1265
1886
  language,
1266
1887
  usageSlice,
@@ -1581,11 +2202,7 @@ export async function parseSliceUsages(
1581
2202
  * @param {Object} semanticsSlice Semantic slice data
1582
2203
  * @returns {Object} Parsed metadata
1583
2204
  */
1584
- export function parseSemanticSlices(language, components, semanticsSlice) {
1585
- // For scala, use the dedicated scalasem module.
1586
- if (language === "scala") {
1587
- return findPurlLocations(components, semanticsSlice);
1588
- }
2205
+ export function parseSemanticSlices(_language, components, semanticsSlice) {
1589
2206
  if (!components) {
1590
2207
  return undefined;
1591
2208
  }