@cdxgen/cdxgen 13.2.0 → 13.4.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 (311) hide show
  1. package/README.md +33 -14
  2. package/bin/cdxgen.js +310 -138
  3. package/bin/evinse.js +22 -0
  4. package/bin/repl.js +86 -0
  5. package/bin/sign.js +119 -15
  6. package/bin/tracebom.js +69 -5
  7. package/bin/validate.js +107 -19
  8. package/bin/verify.js +172 -63
  9. package/data/helpers/init.gradle +46 -21
  10. package/data/remediations.json +85 -0
  11. package/data/rules/dependency-sources.yaml +67 -0
  12. package/data/rules/scala-evidence.yaml +115 -0
  13. package/lib/audit/index.js +38 -6
  14. package/lib/cli/bomAssembly.js +105 -15
  15. package/lib/cli/cliOptions.js +93 -1
  16. package/lib/cli/index.js +156 -58
  17. package/lib/cli/jsBom.js +99 -53
  18. package/lib/cli/jvmBom.js +708 -147
  19. package/lib/cli/managedBom.js +415 -224
  20. package/lib/cli/nativeBom.js +264 -44
  21. package/lib/core/activity.js +12 -0
  22. package/lib/core/env.js +22 -15
  23. package/lib/core/fs.js +176 -7
  24. package/lib/core/httpClient.js +159 -24
  25. package/lib/core/metadataProperties.js +97 -0
  26. package/lib/core/pluginRun.js +184 -0
  27. package/lib/core/processTree.js +100 -0
  28. package/lib/core/projectConfig.js +31 -4
  29. package/lib/core/runState.js +39 -0
  30. package/lib/core/toml.js +72 -0
  31. package/lib/ecosystems/asarutils.js +4 -2
  32. package/lib/ecosystems/bunutils.js +274 -108
  33. package/lib/ecosystems/cargoCache.js +231 -8
  34. package/lib/ecosystems/caxa.js +434 -2
  35. package/lib/ecosystems/cmakeResolver.js +76 -21
  36. package/lib/ecosystems/cppBuildContext.js +631 -0
  37. package/lib/ecosystems/cppEvidence.js +399 -51
  38. package/lib/ecosystems/cppIncludeResolver.js +294 -0
  39. package/lib/ecosystems/denoutils.js +93 -66
  40. package/lib/ecosystems/ecosystems.js +1983 -793
  41. package/lib/ecosystems/golem.js +28 -12
  42. package/lib/ecosystems/gradleutils.js +356 -37
  43. package/lib/ecosystems/jsEvidence.js +104 -44
  44. package/lib/ecosystems/kosi.js +31 -7
  45. package/lib/ecosystems/npmutils.js +174 -9
  46. package/lib/ecosystems/parsers-dotnet.js +616 -159
  47. package/lib/ecosystems/parsers-elm.js +46 -28
  48. package/lib/ecosystems/parsers-gleam.js +3 -4
  49. package/lib/ecosystems/parsers-go.js +371 -70
  50. package/lib/ecosystems/parsers-js.js +263 -76
  51. package/lib/ecosystems/parsers-julia.js +2 -3
  52. package/lib/ecosystems/parsers-jvm.js +204 -12
  53. package/lib/ecosystems/parsers-misc.js +929 -216
  54. package/lib/ecosystems/parsers-python.js +167 -24
  55. package/lib/ecosystems/parsers-rust.js +28 -22
  56. package/lib/ecosystems/parsers-terraform.js +1573 -19
  57. package/lib/ecosystems/pythonutils.js +102 -6
  58. package/lib/ecosystems/remote/huggingface.js +3 -0
  59. package/lib/ecosystems/rubyutils.js +1492 -182
  60. package/lib/ecosystems/rusi.js +54 -10
  61. package/lib/ecosystems/sbtutils.js +243 -187
  62. package/lib/ecosystems/scalacliutils.js +342 -0
  63. package/lib/ecosystems/scalasem.js +2618 -0
  64. package/lib/ecosystems/terraformRegistry.js +288 -0
  65. package/lib/ecosystems/utils.js +2 -0
  66. package/lib/ecosystems/vendoredCode.js +156 -0
  67. package/lib/evinser/evinser.js +1008 -191
  68. package/lib/evinser/swiftsem.js +1914 -302
  69. package/lib/helpers/agenticToolutils.js +2 -2
  70. package/lib/helpers/bomSigner.js +1101 -222
  71. package/lib/inventory/aiCollector.js +4 -1
  72. package/lib/inventory/analyzer.js +110 -127
  73. package/lib/inventory/atomUtils.js +703 -43
  74. package/lib/inventory/auditCategories.js +7 -0
  75. package/lib/inventory/buildToolRateLimit.js +144 -0
  76. package/lib/inventory/cacheCommands.js +2 -1
  77. package/lib/inventory/cdxrs.js +103 -12
  78. package/lib/inventory/cmakeBuildDirs.js +254 -0
  79. package/lib/inventory/deps.js +501 -124
  80. package/lib/inventory/depsUtils.js +183 -11
  81. package/lib/inventory/display.js +4 -5
  82. package/lib/inventory/dosai.js +798 -57
  83. package/lib/inventory/dosaiParsers.js +310 -15
  84. package/lib/inventory/dynamic.js +90 -6
  85. package/lib/inventory/evidenceUtils.js +166 -113
  86. package/lib/inventory/fetchBatch.js +690 -55
  87. package/lib/inventory/fetchDiskCache.js +597 -0
  88. package/lib/inventory/forgeEnricher.js +14 -5
  89. package/lib/inventory/jvmLocalRepos.js +917 -0
  90. package/lib/inventory/mcpConfigParser.js +2 -3
  91. package/lib/inventory/parentSelection.js +146 -0
  92. package/lib/inventory/protobom.js +8 -1
  93. package/lib/inventory/purl.js +152 -3
  94. package/lib/inventory/rustFormulationParser.js +3 -4
  95. package/lib/inventory/sbomDocument.js +2 -70
  96. package/lib/inventory/scalaCoords.js +153 -0
  97. package/lib/inventory/source.js +45 -27
  98. package/lib/inventory/spdx.js +101 -0
  99. package/lib/inventory/traceRunner.js +295 -25
  100. package/lib/inventory/tracedCrypto.js +294 -0
  101. package/lib/managers/binary.js +115 -24
  102. package/lib/managers/docker.js +467 -46
  103. package/lib/managers/oci.js +63 -3
  104. package/lib/parsers/cmakeCache.js +69 -0
  105. package/lib/parsers/cmakeLists.js +466 -0
  106. package/lib/parsers/cmakePresets.js +400 -0
  107. package/lib/parsers/compileCommands.js +557 -0
  108. package/lib/parsers/largeJson.js +454 -0
  109. package/lib/parsers/npmrc.js +19 -1
  110. package/lib/parsers/strictJson.js +322 -0
  111. package/lib/parsers/terraformConfig.js +1073 -0
  112. package/lib/parsers/terraformSource.js +652 -0
  113. package/lib/server/server.js +23 -0
  114. package/lib/stages/postgen/introspection/markers.js +1 -1
  115. package/lib/stages/postgen/postgen.js +83 -16
  116. package/lib/stages/postgen/specVersionCompat.js +32 -0
  117. package/lib/validator/bomValidator.js +6 -16
  118. package/lib/validator/index.js +64 -11
  119. package/lib/validator/reporters/console.js +3 -0
  120. package/lib/validator/reporters/json.js +2 -0
  121. package/lib/validator/reporters/sarif.js +7 -0
  122. package/package.json +23 -23
  123. package/types/lib/audit/index.d.ts.map +1 -1
  124. package/types/lib/cli/bomAssembly.d.ts +2 -1
  125. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  126. package/types/lib/cli/cliOptions.d.ts +33 -1
  127. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  128. package/types/lib/cli/index.d.ts.map +1 -1
  129. package/types/lib/cli/jsBom.d.ts.map +1 -1
  130. package/types/lib/cli/jvmBom.d.ts +24 -1
  131. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  132. package/types/lib/cli/managedBom.d.ts +7 -4
  133. package/types/lib/cli/managedBom.d.ts.map +1 -1
  134. package/types/lib/cli/nativeBom.d.ts +3 -1
  135. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  136. package/types/lib/core/activity.d.ts.map +1 -1
  137. package/types/lib/core/env.d.ts +9 -0
  138. package/types/lib/core/env.d.ts.map +1 -1
  139. package/types/lib/core/fs.d.ts +55 -0
  140. package/types/lib/core/fs.d.ts.map +1 -1
  141. package/types/lib/core/httpClient.d.ts +13 -8
  142. package/types/lib/core/httpClient.d.ts.map +1 -1
  143. package/types/lib/core/metadataProperties.d.ts +39 -0
  144. package/types/lib/core/metadataProperties.d.ts.map +1 -0
  145. package/types/lib/core/pluginRun.d.ts +97 -0
  146. package/types/lib/core/pluginRun.d.ts.map +1 -0
  147. package/types/lib/core/processTree.d.ts +36 -0
  148. package/types/lib/core/processTree.d.ts.map +1 -0
  149. package/types/lib/core/projectConfig.d.ts +6 -2
  150. package/types/lib/core/projectConfig.d.ts.map +1 -1
  151. package/types/lib/core/runState.d.ts +29 -0
  152. package/types/lib/core/runState.d.ts.map +1 -0
  153. package/types/lib/core/toml.d.ts +20 -0
  154. package/types/lib/core/toml.d.ts.map +1 -0
  155. package/types/lib/ecosystems/asarutils.d.ts.map +1 -1
  156. package/types/lib/ecosystems/bunutils.d.ts +12 -5
  157. package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
  158. package/types/lib/ecosystems/cargoCache.d.ts +12 -0
  159. package/types/lib/ecosystems/cargoCache.d.ts.map +1 -1
  160. package/types/lib/ecosystems/caxa.d.ts +14 -0
  161. package/types/lib/ecosystems/caxa.d.ts.map +1 -1
  162. package/types/lib/ecosystems/cmakeResolver.d.ts +28 -3
  163. package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
  164. package/types/lib/ecosystems/cppBuildContext.d.ts +55 -0
  165. package/types/lib/ecosystems/cppBuildContext.d.ts.map +1 -0
  166. package/types/lib/ecosystems/cppEvidence.d.ts +17 -1
  167. package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
  168. package/types/lib/ecosystems/cppIncludeResolver.d.ts +92 -0
  169. package/types/lib/ecosystems/cppIncludeResolver.d.ts.map +1 -0
  170. package/types/lib/ecosystems/denoutils.d.ts +9 -3
  171. package/types/lib/ecosystems/denoutils.d.ts.map +1 -1
  172. package/types/lib/ecosystems/ecosystems.d.ts +62 -13
  173. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  174. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  175. package/types/lib/ecosystems/gradleutils.d.ts +70 -4
  176. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  177. package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
  178. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  179. package/types/lib/ecosystems/kosi.d.ts +13 -0
  180. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  181. package/types/lib/ecosystems/npmutils.d.ts +57 -0
  182. package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
  183. package/types/lib/ecosystems/parsers-dotnet.d.ts +118 -4
  184. package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
  185. package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -1
  186. package/types/lib/ecosystems/parsers-gleam.d.ts.map +1 -1
  187. package/types/lib/ecosystems/parsers-go.d.ts +54 -2
  188. package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
  189. package/types/lib/ecosystems/parsers-js.d.ts +16 -5
  190. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  191. package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -1
  192. package/types/lib/ecosystems/parsers-jvm.d.ts +46 -1
  193. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  194. package/types/lib/ecosystems/parsers-misc.d.ts +70 -4
  195. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  196. package/types/lib/ecosystems/parsers-python.d.ts +53 -3
  197. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  198. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  199. package/types/lib/ecosystems/parsers-terraform.d.ts +38 -21
  200. package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -1
  201. package/types/lib/ecosystems/pythonutils.d.ts +27 -2
  202. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  203. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  204. package/types/lib/ecosystems/rubyutils.d.ts +164 -3
  205. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  206. package/types/lib/ecosystems/rusi.d.ts +13 -0
  207. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  208. package/types/lib/ecosystems/sbtutils.d.ts +88 -2
  209. package/types/lib/ecosystems/sbtutils.d.ts.map +1 -1
  210. package/types/lib/ecosystems/scalacliutils.d.ts +56 -0
  211. package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -0
  212. package/types/lib/ecosystems/scalasem.d.ts +311 -0
  213. package/types/lib/ecosystems/scalasem.d.ts.map +1 -0
  214. package/types/lib/ecosystems/terraformRegistry.d.ts +9 -0
  215. package/types/lib/ecosystems/terraformRegistry.d.ts.map +1 -0
  216. package/types/lib/ecosystems/utils.d.ts +2 -2
  217. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  218. package/types/lib/ecosystems/vendoredCode.d.ts +32 -0
  219. package/types/lib/ecosystems/vendoredCode.d.ts.map +1 -0
  220. package/types/lib/evinser/evinser.d.ts +132 -15
  221. package/types/lib/evinser/evinser.d.ts.map +1 -1
  222. package/types/lib/evinser/swiftsem.d.ts +280 -15
  223. package/types/lib/evinser/swiftsem.d.ts.map +1 -1
  224. package/types/lib/helpers/bomSigner.d.ts +182 -13
  225. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  226. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  227. package/types/lib/inventory/analyzer.d.ts.map +1 -1
  228. package/types/lib/inventory/atomUtils.d.ts +219 -5
  229. package/types/lib/inventory/atomUtils.d.ts.map +1 -1
  230. package/types/lib/inventory/auditCategories.d.ts +3 -0
  231. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  232. package/types/lib/inventory/buildToolRateLimit.d.ts +38 -0
  233. package/types/lib/inventory/buildToolRateLimit.d.ts.map +1 -0
  234. package/types/lib/inventory/cacheCommands.d.ts +2 -1
  235. package/types/lib/inventory/cacheCommands.d.ts.map +1 -1
  236. package/types/lib/inventory/cdxrs.d.ts +8 -3
  237. package/types/lib/inventory/cdxrs.d.ts.map +1 -1
  238. package/types/lib/inventory/cmakeBuildDirs.d.ts +58 -0
  239. package/types/lib/inventory/cmakeBuildDirs.d.ts.map +1 -0
  240. package/types/lib/inventory/deps.d.ts +59 -1
  241. package/types/lib/inventory/deps.d.ts.map +1 -1
  242. package/types/lib/inventory/depsUtils.d.ts +15 -0
  243. package/types/lib/inventory/depsUtils.d.ts.map +1 -1
  244. package/types/lib/inventory/display.d.ts.map +1 -1
  245. package/types/lib/inventory/dosai.d.ts +121 -9
  246. package/types/lib/inventory/dosai.d.ts.map +1 -1
  247. package/types/lib/inventory/dosaiParsers.d.ts +53 -7
  248. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  249. package/types/lib/inventory/dynamic.d.ts +10 -0
  250. package/types/lib/inventory/dynamic.d.ts.map +1 -1
  251. package/types/lib/inventory/evidenceUtils.d.ts +19 -4
  252. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  253. package/types/lib/inventory/fetchBatch.d.ts +83 -14
  254. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  255. package/types/lib/inventory/fetchDiskCache.d.ts +169 -0
  256. package/types/lib/inventory/fetchDiskCache.d.ts.map +1 -0
  257. package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
  258. package/types/lib/inventory/jvmLocalRepos.d.ts +178 -0
  259. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -0
  260. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  261. package/types/lib/inventory/parentSelection.d.ts +34 -0
  262. package/types/lib/inventory/parentSelection.d.ts.map +1 -0
  263. package/types/lib/inventory/plugins.d.ts.map +1 -1
  264. package/types/lib/inventory/protobom.d.ts.map +1 -1
  265. package/types/lib/inventory/purl.d.ts +71 -2
  266. package/types/lib/inventory/purl.d.ts.map +1 -1
  267. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  268. package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
  269. package/types/lib/inventory/scalaCoords.d.ts +80 -0
  270. package/types/lib/inventory/scalaCoords.d.ts.map +1 -0
  271. package/types/lib/inventory/source.d.ts.map +1 -1
  272. package/types/lib/inventory/spdx.d.ts +9 -0
  273. package/types/lib/inventory/spdx.d.ts.map +1 -1
  274. package/types/lib/inventory/traceRunner.d.ts +59 -1
  275. package/types/lib/inventory/traceRunner.d.ts.map +1 -1
  276. package/types/lib/inventory/tracedCrypto.d.ts +42 -0
  277. package/types/lib/inventory/tracedCrypto.d.ts.map +1 -0
  278. package/types/lib/managers/binary.d.ts +30 -1
  279. package/types/lib/managers/binary.d.ts.map +1 -1
  280. package/types/lib/managers/docker.d.ts +29 -0
  281. package/types/lib/managers/docker.d.ts.map +1 -1
  282. package/types/lib/managers/oci.d.ts +17 -1
  283. package/types/lib/managers/oci.d.ts.map +1 -1
  284. package/types/lib/parsers/cmakeCache.d.ts +16 -0
  285. package/types/lib/parsers/cmakeCache.d.ts.map +1 -1
  286. package/types/lib/parsers/cmakeLists.d.ts +77 -0
  287. package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
  288. package/types/lib/parsers/cmakePresets.d.ts +78 -0
  289. package/types/lib/parsers/cmakePresets.d.ts.map +1 -0
  290. package/types/lib/parsers/compileCommands.d.ts +115 -0
  291. package/types/lib/parsers/compileCommands.d.ts.map +1 -0
  292. package/types/lib/parsers/largeJson.d.ts +52 -0
  293. package/types/lib/parsers/largeJson.d.ts.map +1 -0
  294. package/types/lib/parsers/npmrc.d.ts +5 -0
  295. package/types/lib/parsers/npmrc.d.ts.map +1 -1
  296. package/types/lib/parsers/strictJson.d.ts +46 -0
  297. package/types/lib/parsers/strictJson.d.ts.map +1 -0
  298. package/types/lib/parsers/terraformConfig.d.ts +70 -0
  299. package/types/lib/parsers/terraformConfig.d.ts.map +1 -0
  300. package/types/lib/parsers/terraformSource.d.ts +66 -0
  301. package/types/lib/parsers/terraformSource.d.ts.map +1 -0
  302. package/types/lib/server/server.d.ts.map +1 -1
  303. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  304. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  305. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  306. package/types/lib/validator/index.d.ts +18 -1
  307. package/types/lib/validator/index.d.ts.map +1 -1
  308. package/types/lib/validator/reporters/console.d.ts.map +1 -1
  309. package/types/lib/validator/reporters/json.d.ts.map +1 -1
  310. package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
  311. package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +0 -462
@@ -1,4 +1,3 @@
1
- import { readFileSync } from "node:fs";
2
1
  import { platform } from "node:os";
3
2
  import { extname } from "node:path";
4
3
 
@@ -6,12 +5,17 @@ import { build } from "@cdxgen/cdx-purl";
6
5
 
7
6
  import { safeExistsSync } from "../core/fs.js";
8
7
  import { thoughtLog } from "../core/logger.js";
9
- import { addDosaiReachabilityProperties } from "./dosai.js";
8
+ import {
9
+ addDosaiReachabilityProperties,
10
+ readDosaiMethodsReport,
11
+ } from "./dosai.js";
10
12
  import {
11
13
  addDosaiSetValue,
12
14
  buildDosaiPurlAliasMap,
15
+ dosaiCallSiteLocation,
13
16
  dosaiSourceLocation,
14
17
  dosaiSourceLocationFromNode,
18
+ pickDosaiComponentByLocation,
15
19
  resolveDosaiComponentPurl,
16
20
  } from "./dosaiParsers.js";
17
21
  import { createGtfoBinsPropertiesFromRow } from "./gtfobins.js";
@@ -238,27 +242,47 @@ function addDotnetIdentityMethod(apkg, value) {
238
242
  * Enrich .NET package components with occurrence evidence and imported module/method
239
243
  * information from a dosai dependency slices file.
240
244
  *
241
- * Builds a mapping of DLL filenames to purls using the `internal:PackageFiles` property of each
242
- * package, then reads the slices file to add occurrence locations, imported modules,
243
- * called methods, and assembly version information where available.
245
+ * Each dosai record names its package by purl, resolved in the project of the
246
+ * record's file, and is attached to the component of that version (see
247
+ * {@link resolveDosaiComponentPurl}). The DLL files of each package
248
+ * (`internal:PackageFiles`) only fill in for a record whose purl names no
249
+ * component, and only when the DLL belongs to one component or the record's
250
+ * location says which: two versions of a package ship the same DLL names.
251
+ *
252
+ * The fallback is needed because dosai names a `System.*` assembly it cannot
253
+ * map to a package with a framework purl (`pkg:nuget/System.Runtime`), as it
254
+ * does whenever the tree has lock files but no restore output: the DLL of
255
+ * System.Data.SQLite.Core is still the package's. A DLL that only a package's
256
+ * build-time folders carry is not listed in `internal:PackageFiles`, so it
257
+ * credits no package with the project's framework calls (issue 4441).
244
258
  *
245
259
  * @param {Object[]} pkgList Array of .NET package component objects to enrich
246
260
  * @param {string} slicesFile Path to the dosai dependency slices JSON file
261
+ * @param {Object} [options] Options
262
+ * @param {string} [options.srcPath] Directory dosai analyzed, which its relative locations
263
+ * and the components' relative manifest paths are relative to
247
264
  * @returns {Object[]} The enriched package list (same array, mutated in place)
248
265
  */
249
- export function addEvidenceForDotnet(pkgList, slicesFile) {
266
+ export function addEvidenceForDotnet(pkgList, slicesFile, options = {}) {
250
267
  // We need two datastructures.
251
268
  // dll to purl mapping from the pkgList
252
269
  // purl to occurrences list using the slicesFile
253
270
  if (!slicesFile || !safeExistsSync(slicesFile)) {
254
271
  return pkgList;
255
272
  }
256
- const pkgFilePurlMap = {};
273
+ const pkgFilePurls = {};
257
274
  const purlLocationMap = {};
258
275
  const purlModulesMap = {};
259
276
  const purlMethodsMap = {};
260
277
  const purlReachabilityMap = {};
261
- const purlAliasMap = buildDosaiPurlAliasMap(pkgList);
278
+ const purlAliasMap = buildDosaiPurlAliasMap(pkgList, options);
279
+ const componentPurls = new Set(pkgList.map((apkg) => apkg.purl));
280
+ // A dosai purl that names a component of this list, resolved in the
281
+ // record's project.
282
+ const componentPurlFor = (purl, location) => {
283
+ const resolved = resolveDosaiComponentPurl(purl, purlAliasMap, location);
284
+ return componentPurls.has(resolved) ? resolved : undefined;
285
+ };
262
286
  for (const apkg of pkgList) {
263
287
  if (apkg.properties && Array.isArray(apkg.properties)) {
264
288
  apkg.properties
@@ -268,17 +292,35 @@ export function addEvidenceForDotnet(pkgList, slicesFile) {
268
292
  const tmpA = aprop.value.split(", ");
269
293
  if (tmpA?.length) {
270
294
  tmpA.forEach((dllFile) => {
271
- pkgFilePurlMap[dllFile] = apkg.purl;
295
+ pkgFilePurls[dllFile] ??= new Set();
296
+ pkgFilePurls[dllFile].add(apkg.purl);
272
297
  });
273
298
  }
274
299
  }
275
300
  });
276
301
  }
277
302
  }
278
- let slicesData;
279
- try {
280
- slicesData = JSON.parse(readFileSync(slicesFile, "utf-8"));
281
- } catch (_err) {
303
+ // The component a DLL belongs to: its only package, else the candidate of the
304
+ // record's project.
305
+ const purlForDll = (dllFile, location) =>
306
+ pkgFilePurls[dllFile]
307
+ ? pickDosaiComponentByLocation(
308
+ pkgFilePurls[dllFile],
309
+ purlAliasMap,
310
+ location,
311
+ )
312
+ : undefined;
313
+ // Only MethodCalls into a package .dll produce evidence below, so a report
314
+ // too large to parse whole keeps just those.
315
+ const slicesData = readDosaiMethodsReport(slicesFile, {
316
+ keepMethodCall: (methodCall) =>
317
+ Boolean(
318
+ methodCall?.Purl ||
319
+ (methodCall?.Module?.endsWith(".dll") &&
320
+ pkgFilePurls[methodCall.Module]),
321
+ ),
322
+ });
323
+ if (!slicesData) {
282
324
  return pkgList;
283
325
  }
284
326
  if (slicesData && Object.keys(slicesData)) {
@@ -287,43 +329,36 @@ export function addEvidenceForDotnet(pkgList, slicesFile) {
287
329
  );
288
330
  if (slicesData.Dependencies) {
289
331
  for (const adep of slicesData.Dependencies) {
290
- if (adep.Purl) {
291
- const modPurl = resolveDosaiComponentPurl(adep.Purl, purlAliasMap);
292
- if (modPurl) {
293
- addDosaiSetValue(
294
- purlLocationMap,
295
- modPurl,
296
- dosaiSourceLocation(adep),
297
- );
298
- addDosaiSetValue(
299
- purlModulesMap,
300
- modPurl,
301
- adep.Name || adep.Namespace,
302
- );
303
- }
332
+ const location = dosaiSourceLocation(adep);
333
+ const resolvedPurl = adep.Purl
334
+ ? componentPurlFor(adep.Purl, adep.Path)
335
+ : undefined;
336
+ if (resolvedPurl) {
337
+ addDosaiSetValue(purlLocationMap, resolvedPurl, location);
338
+ addDosaiSetValue(
339
+ purlModulesMap,
340
+ resolvedPurl,
341
+ adep.Name || adep.Namespace,
342
+ );
343
+ continue;
304
344
  }
305
345
  // Case 1: Dependencies slice has the .dll file
306
- if (adep.Module?.endsWith(".dll") && pkgFilePurlMap[adep.Module]) {
307
- const modPurl = pkgFilePurlMap[adep.Module];
308
- if (!purlLocationMap[modPurl]) {
309
- purlLocationMap[modPurl] = new Set();
310
- }
311
- purlLocationMap[modPurl].add(`${adep.Path}#${adep.LineNumber}`);
312
- } else if (
313
- adep?.Name &&
314
- (adep?.Namespace?.startsWith("System") ||
315
- adep?.Namespace?.startsWith("Microsoft"))
316
- ) {
317
- // Case 2: System packages where the .dll information is missing
318
- // In this case, the dll file name is the name followed by dll.
319
- const moduleDll = `${adep.Name}.dll`;
320
- if (pkgFilePurlMap[moduleDll]) {
321
- const modPurl = pkgFilePurlMap[moduleDll];
322
- if (!purlLocationMap[modPurl]) {
323
- purlLocationMap[modPurl] = new Set();
324
- }
325
- purlLocationMap[modPurl].add(`${adep.Path}#${adep.LineNumber}`);
326
- }
346
+ // Case 2: System packages where the .dll information is missing. In
347
+ // this case, the dll file name is the name followed by dll.
348
+ const moduleDll = adep.Module?.endsWith(".dll")
349
+ ? adep.Module
350
+ : adep?.Name &&
351
+ (adep?.Namespace?.startsWith("System") ||
352
+ adep?.Namespace?.startsWith("Microsoft"))
353
+ ? `${adep.Name}.dll`
354
+ : undefined;
355
+ const modPurl = moduleDll && purlForDll(moduleDll, adep.Path);
356
+ if (modPurl) {
357
+ addDosaiSetValue(
358
+ purlLocationMap,
359
+ modPurl,
360
+ `${adep.Path}#${adep.LineNumber}`,
361
+ );
327
362
  }
328
363
  }
329
364
  }
@@ -334,95 +369,113 @@ export function addEvidenceForDotnet(pkgList, slicesFile) {
334
369
  const graphNodes = Object.fromEntries(
335
370
  (slicesData.CallGraph?.Nodes || []).map((node) => [node.Id, node]),
336
371
  );
337
- for (const reachability of slicesData.PackageReachability) {
338
- const modPurl = resolveDosaiComponentPurl(
339
- reachability.Purl,
340
- purlAliasMap,
341
- );
342
- if (!modPurl) {
343
- continue;
344
- }
345
- // Confidence/evidence facts for this purl, applied to the component below so an
346
- // unbuilt tree stays distinguishable from an only-imported package.
347
- purlReachabilityMap[modPurl] ??= reachability;
372
+ // The first fact for a component wins, so a fact that names the
373
+ // component's exact purl goes before one that reached it by name.
374
+ const facts = [...slicesData.PackageReachability].sort(
375
+ (a, b) =>
376
+ Number(!componentPurls.has(a.Purl)) -
377
+ Number(!componentPurls.has(b.Purl)),
378
+ );
379
+ for (const reachability of facts) {
380
+ // A fact can span projects (a versionless purl from unrestored
381
+ // projects), so each location is resolved in its own project, and the
382
+ // fact applies to every component one of its locations reached.
383
+ const factPurls = new Set();
384
+ // Modules and methods of call-graph items that name no source file
385
+ // (frames inside the package), held for the fact's own version.
386
+ const unplaced = [];
387
+ const resolveAt = (location) =>
388
+ resolveDosaiComponentPurl(reachability.Purl, purlAliasMap, location);
348
389
  let hasExplicitSourceLocations = false;
349
390
  for (const sourceLocation of reachability.SourceLocations || []) {
350
391
  const location = dosaiSourceLocation(sourceLocation);
392
+ const modPurl = resolveAt(location);
351
393
  addDosaiSetValue(purlLocationMap, modPurl, location);
394
+ if (modPurl) {
395
+ factPurls.add(modPurl);
396
+ }
352
397
  hasExplicitSourceLocations ||= Boolean(location);
353
398
  }
354
399
  for (const edgeId of reachability.EdgeIds || []) {
355
400
  const edge = graphEdges[edgeId];
401
+ const location = dosaiSourceLocation(edge);
402
+ const modPurl = resolveAt(location ?? edge?.Path);
403
+ const method = edge?.CalledMethodName || edge?.TargetName;
404
+ if (!modPurl) {
405
+ unplaced.push([purlMethodsMap, method]);
406
+ continue;
407
+ }
408
+ factPurls.add(modPurl);
356
409
  if (!hasExplicitSourceLocations) {
357
- addDosaiSetValue(
358
- purlLocationMap,
359
- modPurl,
360
- dosaiSourceLocation(edge),
361
- );
410
+ addDosaiSetValue(purlLocationMap, modPurl, location);
362
411
  }
363
- addDosaiSetValue(
364
- purlMethodsMap,
365
- modPurl,
366
- edge?.CalledMethodName || edge?.TargetName,
367
- );
412
+ addDosaiSetValue(purlMethodsMap, modPurl, method);
368
413
  }
369
414
  for (const nodeId of reachability.NodeIds || []) {
370
415
  const node = graphNodes[nodeId];
416
+ const location = dosaiSourceLocationFromNode(node);
417
+ const modPurl = resolveAt(location);
418
+ const module = node?.ClassName || node?.Module;
419
+ const method = node?.Name || node?.Identity?.MethodName;
420
+ if (!modPurl) {
421
+ unplaced.push([purlModulesMap, module], [purlMethodsMap, method]);
422
+ continue;
423
+ }
424
+ factPurls.add(modPurl);
371
425
  if (!hasExplicitSourceLocations) {
372
- addDosaiSetValue(
373
- purlLocationMap,
374
- modPurl,
375
- dosaiSourceLocationFromNode(node),
376
- );
426
+ addDosaiSetValue(purlLocationMap, modPurl, location);
377
427
  }
378
- addDosaiSetValue(
379
- purlModulesMap,
380
- modPurl,
381
- node?.ClassName || node?.Module,
382
- );
383
- addDosaiSetValue(
384
- purlMethodsMap,
385
- modPurl,
386
- node?.Name || node?.Identity?.MethodName,
387
- );
428
+ addDosaiSetValue(purlModulesMap, modPurl, module);
429
+ addDosaiSetValue(purlMethodsMap, modPurl, method);
388
430
  }
389
- }
390
- }
391
- if (slicesData.MethodCalls) {
392
- for (const amethodCall of slicesData.MethodCalls) {
393
- if (
394
- amethodCall.Module?.endsWith(".dll") &&
395
- pkgFilePurlMap[amethodCall.Module]
396
- ) {
397
- const modPurl = pkgFilePurlMap[amethodCall.Module];
398
- if (!purlLocationMap[modPurl]) {
399
- purlLocationMap[modPurl] = new Set();
400
- }
401
- if (!purlModulesMap[modPurl]) {
402
- purlModulesMap[modPurl] = new Set();
431
+ if (!factPurls.size) {
432
+ const modPurl = resolveAt(undefined);
433
+ if (modPurl) {
434
+ factPurls.add(modPurl);
403
435
  }
404
- if (!purlMethodsMap[modPurl]) {
405
- purlMethodsMap[modPurl] = new Set();
436
+ }
437
+ if (factPurls.size === 1) {
438
+ const [modPurl] = factPurls;
439
+ for (const [map, value] of unplaced) {
440
+ addDosaiSetValue(map, modPurl, value);
406
441
  }
407
- purlLocationMap[modPurl].add(
408
- `${amethodCall.Path}#${amethodCall.LineNumber}`,
409
- );
410
- purlModulesMap[modPurl].add(amethodCall.ClassName);
411
- purlMethodsMap[modPurl].add(amethodCall.CalledMethod);
442
+ }
443
+ // Confidence/evidence facts for this purl, applied to the component below so an
444
+ // unbuilt tree stays distinguishable from an only-imported package.
445
+ for (const modPurl of factPurls) {
446
+ purlReachabilityMap[modPurl] ??= reachability;
412
447
  }
413
448
  }
414
449
  }
415
- if (slicesData.AssemblyInformation) {
416
- for (const apkg of pkgList) {
417
- if (!apkg.version) {
418
- for (const assemblyInfo of slicesData.AssemblyInformation) {
419
- if (apkg.name === assemblyInfo.Name) {
420
- apkg.version = assemblyInfo.Version;
421
- }
422
- }
450
+ if (slicesData.MethodCalls) {
451
+ for (const amethodCall of slicesData.MethodCalls) {
452
+ // dosai's purl is resolved in the caller's project; the DLL name is the
453
+ // same for every version of the package, and fills in for a purl that
454
+ // names no component, such as the framework purl dosai gives a
455
+ // System.* assembly it cannot map to a package.
456
+ const modPurl =
457
+ (amethodCall.Purl
458
+ ? componentPurlFor(amethodCall.Purl, amethodCall.Path)
459
+ : undefined) ||
460
+ (amethodCall.Module?.endsWith(".dll")
461
+ ? purlForDll(amethodCall.Module, amethodCall.Path)
462
+ : undefined);
463
+ if (!modPurl) {
464
+ continue;
423
465
  }
466
+ addDosaiSetValue(
467
+ purlLocationMap,
468
+ modPurl,
469
+ dosaiCallSiteLocation(amethodCall, options.srcPath),
470
+ );
471
+ addDosaiSetValue(purlModulesMap, modPurl, amethodCall.ClassName);
472
+ addDosaiSetValue(purlMethodsMap, modPurl, amethodCall.CalledMethod);
424
473
  }
425
474
  }
475
+ // AssemblyInformation used to fill in a missing version by assembly name.
476
+ // An assembly version is not the package version (Newtonsoft.Json 13.0.3
477
+ // ships assembly 13.0.0.0), the purl and bom-ref kept no version, and the
478
+ // last same-named assembly won, so the component described two packages.
426
479
  }
427
480
  if (Object.keys(purlLocationMap).length) {
428
481
  for (const apkg of pkgList) {