@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
@@ -1,5 +1,13 @@
1
1
  import { readFileSync } from "node:fs";
2
- import { basename, dirname, join, resolve } from "node:path";
2
+ import {
3
+ basename,
4
+ dirname,
5
+ isAbsolute,
6
+ join,
7
+ posix,
8
+ resolve,
9
+ win32,
10
+ } from "node:path";
3
11
 
4
12
  import { build } from "@cdxgen/cdx-purl";
5
13
 
@@ -41,6 +49,304 @@ export function applyNugetVersion(pkg, version) {
41
49
  return applyPurl(pkg, nugetPurl(pkg.name, concrete));
42
50
  }
43
51
 
52
+ /**
53
+ * Compare two NuGet versions: up to four numeric release parts (missing parts
54
+ * read as 0), then the prerelease label, with a release sorting after its own
55
+ * prereleases. Build metadata after `+` is ignored, as NuGet ignores it.
56
+ *
57
+ * @param {string} a Version
58
+ * @param {string} b Version
59
+ * @returns {number} Negative, zero or positive, like a sort comparator
60
+ */
61
+ export function compareNugetVersions(a, b) {
62
+ const split = (version) => {
63
+ const withoutMetadata = `${version ?? ""}`.trim().split("+")[0];
64
+ const dash = withoutMetadata.indexOf("-");
65
+ const release =
66
+ dash >= 0 ? withoutMetadata.slice(0, dash) : withoutMetadata;
67
+ const prerelease = dash >= 0 ? withoutMetadata.slice(dash + 1) : "";
68
+ const parts = release.split(".").map((part) => Number.parseInt(part, 10));
69
+ return { parts, prerelease };
70
+ };
71
+ const left = split(a);
72
+ const right = split(b);
73
+ for (let index = 0; index < 4; index++) {
74
+ const l = Number.isNaN(left.parts[index]) ? 0 : (left.parts[index] ?? 0);
75
+ const r = Number.isNaN(right.parts[index]) ? 0 : (right.parts[index] ?? 0);
76
+ if (l !== r) {
77
+ return l < r ? -1 : 1;
78
+ }
79
+ }
80
+ if (left.prerelease === right.prerelease) {
81
+ return 0;
82
+ }
83
+ if (!left.prerelease) {
84
+ return 1;
85
+ }
86
+ if (!right.prerelease) {
87
+ return -1;
88
+ }
89
+ const leftLabels = left.prerelease.toLowerCase().split(".");
90
+ const rightLabels = right.prerelease.toLowerCase().split(".");
91
+ for (
92
+ let index = 0;
93
+ index < Math.max(leftLabels.length, rightLabels.length);
94
+ index++
95
+ ) {
96
+ const l = leftLabels[index];
97
+ const r = rightLabels[index];
98
+ if (l === undefined || r === undefined) {
99
+ return l === undefined ? -1 : 1;
100
+ }
101
+ if (l === r) {
102
+ continue;
103
+ }
104
+ const lNumeric = /^\d+$/.test(l);
105
+ const rNumeric = /^\d+$/.test(r);
106
+ if (lNumeric && rNumeric) {
107
+ return Number(l) < Number(r) ? -1 : 1;
108
+ }
109
+ if (lNumeric !== rNumeric) {
110
+ return lNumeric ? -1 : 1;
111
+ }
112
+ return l < r ? -1 : 1;
113
+ }
114
+ return 0;
115
+ }
116
+
117
+ /**
118
+ * Whether a NuGet version range allows a version. A bare version is a minimum
119
+ * (`1.0` means at least 1.0); brackets are inclusive and parentheses exclusive
120
+ * (`[1.0]`, `[1.0,2.0)`, `(,2.0]`). An empty or unparseable range allows
121
+ * anything.
122
+ *
123
+ * @param {string} range Range as a nuspec or lock file states it
124
+ * @param {string} version Version to test
125
+ * @returns {boolean} True when the range allows the version
126
+ */
127
+ export function nugetRangeAllows(range, version) {
128
+ const text = `${range ?? ""}`.trim();
129
+ if (!text || !version) {
130
+ return true;
131
+ }
132
+ const opening = text[0];
133
+ if (opening !== "[" && opening !== "(") {
134
+ return compareNugetVersions(version, text) >= 0;
135
+ }
136
+ const closing = text[text.length - 1];
137
+ if (closing !== "]" && closing !== ")") {
138
+ return true;
139
+ }
140
+ const inner = text.slice(1, -1);
141
+ const comma = inner.indexOf(",");
142
+ if (comma < 0) {
143
+ return compareNugetVersions(version, inner.trim()) === 0;
144
+ }
145
+ const minimum = inner.slice(0, comma).trim();
146
+ const maximum = inner.slice(comma + 1).trim();
147
+ if (minimum) {
148
+ const order = compareNugetVersions(version, minimum);
149
+ if (order < 0 || (order === 0 && opening === "(")) {
150
+ return false;
151
+ }
152
+ }
153
+ if (maximum) {
154
+ const order = compareNugetVersions(version, maximum);
155
+ if (order > 0 || (order === 0 && closing === ")")) {
156
+ return false;
157
+ }
158
+ }
159
+ return true;
160
+ }
161
+
162
+ /**
163
+ * Choose the installed version a declared dependency resolves to: the lowest
164
+ * installed version the range allows, as NuGet picks the lowest applicable
165
+ * version; else the only installed version, which is what binding redirects
166
+ * unify a dependency onto. Undefined when nothing installed can be named.
167
+ *
168
+ * @param {string} range Declared range
169
+ * @param {string[]} installedVersions Versions installed for the package
170
+ * @returns {string|undefined} The chosen version
171
+ */
172
+ export function resolveInstalledNugetVersion(range, installedVersions = []) {
173
+ const allowed = installedVersions
174
+ .filter((version) => nugetRangeAllows(range, version))
175
+ .sort(compareNugetVersions);
176
+ if (allowed.length) {
177
+ return allowed[0];
178
+ }
179
+ return installedVersions.length === 1 ? installedVersions[0] : undefined;
180
+ }
181
+
182
+ /**
183
+ * The project directory a .NET manifest belongs to. Restore output records its
184
+ * project file; otherwise project.assets.json sits in the project's `obj/`,
185
+ * and lock files, packages.config and project files sit in the project
186
+ * directory itself.
187
+ *
188
+ * @param {string} manifestFile Manifest path
189
+ * @param {string} [projectPath] Project file the manifest names, when it does
190
+ * @returns {string} Project directory
191
+ */
192
+ export function dotnetManifestProjectDir(manifestFile, projectPath) {
193
+ // A path recorded on another operating system, such as `D:\src\App.csproj`
194
+ // read on Linux, is not absolute here and would resolve against the working
195
+ // directory.
196
+ if (projectPath && isAbsolute(projectPath)) {
197
+ const projectDir = dirname(resolve(projectPath));
198
+ if (safeExistsSync(projectDir)) {
199
+ return projectDir;
200
+ }
201
+ }
202
+ const dir = dirname(resolve(manifestFile));
203
+ return basename(dir) === "obj" ? dirname(dir) : dir;
204
+ }
205
+
206
+ /**
207
+ * The path, on this machine, of the project file a project.assets.json belongs to.
208
+ *
209
+ * Restore records the project file and the directory it wrote the assets file
210
+ * to (`outputPath`) as absolute paths on the machine that ran it, which can be
211
+ * a CI agent, a container, another operating system, or this tree reached
212
+ * through another path. Where the project file sits relative to that
213
+ * directory does not change when the tree moves, so the project file is that
214
+ * offset applied to the directory the assets file is in. An assets file that
215
+ * records no output path is taken to be in its project's `obj/`. The recorded
216
+ * path itself is used only when no offset can be worked out, as when restore
217
+ * wrote to another drive than the project's.
218
+ *
219
+ * Nothing is read from disk: the name is spelled as restore recorded it, which
220
+ * can differ in case from the file a case-insensitive file system holds.
221
+ *
222
+ * @param {string} assetsFile project.assets.json path
223
+ * @param {string} [projectPath] Project file restore recorded
224
+ * @param {string} [outputPath] Directory restore recorded writing the assets file to
225
+ * @returns {string|undefined} Project file path, or undefined when none can be named
226
+ */
227
+ export function dotnetRestoredProjectFile(assetsFile, projectPath, outputPath) {
228
+ if (!assetsFile || typeof projectPath !== "string" || !projectPath.trim()) {
229
+ return undefined;
230
+ }
231
+ // Read the recorded paths the way the machine that wrote them would
232
+ const recorded =
233
+ win32.isAbsolute(projectPath) && !posix.isAbsolute(projectPath)
234
+ ? win32
235
+ : posix;
236
+ const assetsDir = dirname(resolve(assetsFile));
237
+ let offset;
238
+ if (typeof outputPath === "string" && outputPath.trim()) {
239
+ const relativePath = recorded.relative(outputPath, projectPath);
240
+ // Across two drives there is no relative path, only the recorded one
241
+ if (!recorded.isAbsolute(relativePath)) {
242
+ offset = relativePath.split(recorded.sep);
243
+ }
244
+ } else if (basename(assetsDir) === "obj") {
245
+ offset = ["..", recorded.basename(projectPath)];
246
+ }
247
+ // The offset must end at a file, not at the output directory or a parent
248
+ const fileName = offset?.at(-1);
249
+ if (fileName && fileName !== "." && fileName !== "..") {
250
+ return resolve(assetsDir, ...offset);
251
+ }
252
+ return isAbsolute(projectPath) ? resolve(projectPath) : undefined;
253
+ }
254
+
255
+ /**
256
+ * Index the MSBuild properties that props files define, by the directory each
257
+ * file sits in and across the whole scan, so the candidates for every project
258
+ * come from one read of each file.
259
+ *
260
+ * @param {string[]} propsFiles Props files of the scan
261
+ * @returns {{byDir: Object, treeWide: Object}} Property values per directory and
262
+ * across the scan, each a map of property name to values
263
+ */
264
+ export function indexPropsProperties(propsFiles = []) {
265
+ const byDir = {};
266
+ const treeWide = {};
267
+ for (const propsFile of propsFiles) {
268
+ if (!propsFile) {
269
+ continue;
270
+ }
271
+ const nodes = getPropertyGroupTextNodes([propsFile]);
272
+ const dir = dirname(resolve(propsFile));
273
+ byDir[dir] ??= {};
274
+ for (const [key, values] of Object.entries(nodes)) {
275
+ byDir[dir][key] ??= [];
276
+ treeWide[key] ??= [];
277
+ for (const value of values) {
278
+ if (!byDir[dir][key].includes(value)) {
279
+ byDir[dir][key].push(value);
280
+ }
281
+ if (!treeWide[key].includes(value)) {
282
+ treeWide[key].push(value);
283
+ }
284
+ }
285
+ }
286
+ }
287
+ return { byDir, treeWide };
288
+ }
289
+
290
+ /**
291
+ * The candidate values of MSBuild properties that can apply to a project file,
292
+ * from the props files beside it and above it: the nearest directory that
293
+ * defines a property wins, as the nearest Directory.Build.props is the one
294
+ * MSBuild imports. A property no such directory defines falls back to the
295
+ * values the whole scan's props files give it, which a caller only trusts when
296
+ * there is exactly one.
297
+ *
298
+ * @param {string} projFile Project file
299
+ * @param {{byDir: Object, treeWide: Object}} propsIndex Index built by {@link indexPropsProperties}
300
+ * @returns {Object} Map of property name to candidate values
301
+ */
302
+ export function projectPropertyCandidates(projFile, propsIndex = {}) {
303
+ const candidates = { ...(propsIndex.treeWide || {}) };
304
+ const settled = new Set();
305
+ let dir = dirname(resolve(projFile));
306
+ for (;;) {
307
+ for (const [key, values] of Object.entries(propsIndex.byDir?.[dir] || {})) {
308
+ if (!settled.has(key)) {
309
+ candidates[key] = values;
310
+ settled.add(key);
311
+ }
312
+ }
313
+ const parent = dirname(dir);
314
+ if (parent === dir) {
315
+ break;
316
+ }
317
+ dir = parent;
318
+ }
319
+ return candidates;
320
+ }
321
+
322
+ /**
323
+ * The version of each package that every manifest of a scan agrees on, keyed by
324
+ * lowercased package id. A package that two manifests give different versions
325
+ * is left out: without knowing which project a declaration belongs to, picking
326
+ * one of them would be a guess.
327
+ *
328
+ * @param {Object[]} pkgList Components collected from the scan's manifests
329
+ * @returns {Object} Map of lowercased package id to version
330
+ */
331
+ export function agreedNugetVersions(pkgList = []) {
332
+ const versions = {};
333
+ for (const p of pkgList) {
334
+ if (!p?.name || !p.version || ["application", "project"].includes(p.type)) {
335
+ continue;
336
+ }
337
+ const key = p.name.toLowerCase();
338
+ versions[key] ??= new Set();
339
+ versions[key].add(p.version);
340
+ }
341
+ const agreed = {};
342
+ for (const [key, values] of Object.entries(versions)) {
343
+ if (values.size === 1) {
344
+ agreed[key] = [...values][0];
345
+ }
346
+ }
347
+ return agreed;
348
+ }
349
+
44
350
  /**
45
351
  * Method to parse .nupkg files
46
352
  *
@@ -70,9 +376,16 @@ export function parseNuspecData(nupkgFile, nuspecData) {
70
376
  const pkg = { group: "" };
71
377
  let npkg;
72
378
  const dependenciesMap = {};
379
+ // The version range each dependency accepts, per depending package, so a
380
+ // caller holding the installed packages can point an edge at one of them.
381
+ const dependencyRanges = {};
73
382
  const addedMap = {};
74
383
  try {
75
- npkg = xml2js(nuspecData, {
384
+ // A nuspec written on Windows often starts with a UTF-8 byte order mark,
385
+ // which the XML parser rejects as text before the declaration.
386
+ const data =
387
+ nuspecData.charCodeAt(0) === 0xfeff ? nuspecData.slice(1) : nuspecData;
388
+ npkg = xml2js(data, {
76
389
  compact: true,
77
390
  alwaysArray: false,
78
391
  spaces: 4,
@@ -94,6 +407,7 @@ export function parseNuspecData(nupkgFile, nuspecData) {
94
407
  return {
95
408
  pkgList,
96
409
  dependenciesMap,
410
+ dependencyRanges,
97
411
  };
98
412
  }
99
413
  const m = npkg.metadata;
@@ -101,7 +415,12 @@ export function parseNuspecData(nupkgFile, nuspecData) {
101
415
  pkg.version = m.version._;
102
416
  pkg.description = m.description._;
103
417
  applyPurl(pkg, nugetPurl(pkg.name, pkg.version));
104
- if (m.licenseUrl) {
418
+ // A licence expression is the machine-readable licence NuGet records; the
419
+ // licence URL is the older, often unresolvable form, so the expression wins.
420
+ if (m.license?.$?.type === "expression" && m.license?._?.trim()) {
421
+ pkg.license = findLicenseId(m.license._.trim()) || m.license._.trim();
422
+ }
423
+ if (!pkg.license && m.licenseUrl) {
105
424
  pkg.license = findLicenseId(m.licenseUrl._);
106
425
  }
107
426
  if (m.authors) {
@@ -128,18 +447,19 @@ export function parseNuspecData(nupkgFile, nuspecData) {
128
447
  };
129
448
  pkg.scope = "required";
130
449
  pkgList.push(pkg);
450
+ const ranges = {};
131
451
  if (m?.dependencies?.dependency) {
132
452
  const dependsOn = [];
133
- if (Array.isArray(m.dependencies.dependency)) {
134
- for (const adep of m.dependencies.dependency) {
135
- const d = adep.$;
136
- dependsOn.push(d.id);
137
- }
138
- } else {
139
- const d = m.dependencies.dependency.$;
453
+ const declared = Array.isArray(m.dependencies.dependency)
454
+ ? m.dependencies.dependency
455
+ : [m.dependencies.dependency];
456
+ for (const adep of declared) {
457
+ const d = adep.$;
140
458
  dependsOn.push(d.id);
459
+ ranges[d.id] ??= d.version;
141
460
  }
142
461
  dependenciesMap[pkg["bom-ref"]] = dependsOn;
462
+ dependencyRanges[pkg["bom-ref"]] = ranges;
143
463
  } else if (m?.dependencies?.group) {
144
464
  let dependencyGroups;
145
465
  if (Array.isArray(m.dependencies.group)) {
@@ -168,6 +488,7 @@ export function parseNuspecData(nupkgFile, nuspecData) {
168
488
  continue;
169
489
  }
170
490
  groupPkg.name = agroupdep.id;
491
+ ranges[agroupdep.id] ??= agroupdep.version;
171
492
  let declaredRange;
172
493
  if (agroupdep?.version) {
173
494
  let versionStr = agroupdep.version;
@@ -225,11 +546,13 @@ export function parseNuspecData(nupkgFile, nuspecData) {
225
546
  } // for
226
547
  } // group dependency block
227
548
  dependenciesMap[pkg["bom-ref"]] = dependsOn;
549
+ dependencyRanges[pkg["bom-ref"]] = ranges;
228
550
  } // for
229
551
  }
230
552
  return {
231
553
  pkgList,
232
554
  dependenciesMap,
555
+ dependencyRanges,
233
556
  };
234
557
  }
235
558
 
@@ -289,7 +612,7 @@ export function parseCsPkgData(pkgData, pkgFile, pkgNameVersions = {}) {
289
612
  // Backfill from a version already resolved by a more precise manifest.
290
613
  // Concrete versions are never overridden - a disagreement means both
291
614
  // versions must be tracked.
292
- pkg.version = pkgNameVersions[pkg.name];
615
+ pkg.version = lookupNugetVersion(pkgNameVersions, pkg.name);
293
616
  }
294
617
  applyNugetVersion(pkg, pkg.version);
295
618
  if (pkgFile) {
@@ -481,18 +804,121 @@ function propertyGroupText(propertyGroup, key) {
481
804
  return text.trim();
482
805
  }
483
806
 
807
+ /**
808
+ * Look up a package version in a name - version map. NuGet package ids are
809
+ * case-insensitive, so a map may be keyed by the id as written or lowercased.
810
+ *
811
+ * @param {Object} versions Map of package id to version
812
+ * @param {string} name Package id
813
+ * @returns {string|undefined} The version, or undefined when the map has none
814
+ */
815
+ export function lookupNugetVersion(versions, name) {
816
+ if (!versions || !name) {
817
+ return undefined;
818
+ }
819
+ return versions[name] ?? versions[name.toLowerCase()];
820
+ }
821
+
822
+ /**
823
+ * Collect the MSBuild properties a project file defines itself, from every
824
+ * PropertyGroup, keyed by property name. A later definition wins, as it does
825
+ * when MSBuild evaluates the file.
826
+ *
827
+ * @param {Object} project Project element parsed by xml2js in compact mode
828
+ * @returns {Object} Map of property name to text value
829
+ */
830
+ function projectOwnProperties(project) {
831
+ const properties = {};
832
+ for (const propertyGroup of project?.PropertyGroup || []) {
833
+ for (const key of Object.keys(propertyGroup)) {
834
+ if (key === "$" || key === "value") {
835
+ continue;
836
+ }
837
+ const text = propertyGroupText(propertyGroup, key);
838
+ if (text !== undefined) {
839
+ properties[key] = text;
840
+ }
841
+ }
842
+ }
843
+ return properties;
844
+ }
845
+
846
+ /**
847
+ * Evaluate MSBuild properties of one project with a single `dotnet msbuild`
848
+ * call. Asking for every property at once costs one evaluation per project,
849
+ * where one call per package reference evaluated the same project over and
850
+ * over.
851
+ *
852
+ * @param {string} projFile Project file
853
+ * @param {string[]} names Property names
854
+ * @returns {Object} Map of property name to evaluated value, without empty values
855
+ */
856
+ function evaluateMsbuildProperties(projFile, names) {
857
+ const values = {};
858
+ if (!projFile || !names.length) {
859
+ return values;
860
+ }
861
+ const result = safeSpawnSync(
862
+ DOTNET_CMD,
863
+ [
864
+ "msbuild",
865
+ projFile,
866
+ "-nologo",
867
+ ...names.map((name) => `-getProperty:${name}`),
868
+ ],
869
+ { shell: isWin },
870
+ );
871
+ if (result.status !== 0 || result.error || !result.stdout?.trim()) {
872
+ return values;
873
+ }
874
+ const stdout = result.stdout.trim();
875
+ if (names.length === 1) {
876
+ values[names[0]] = stdout;
877
+ return values;
878
+ }
879
+ // Several properties come back as one JSON document.
880
+ try {
881
+ const evaluated = JSON.parse(stdout)?.Properties || {};
882
+ for (const name of names) {
883
+ if (typeof evaluated[name] === "string" && evaluated[name].trim()) {
884
+ values[name] = evaluated[name].trim();
885
+ }
886
+ }
887
+ } catch (_e) {
888
+ // Unexpected output: treat every property as unresolved.
889
+ }
890
+ return values;
891
+ }
892
+
484
893
  /**
485
894
  * Method to parse .csproj like xml files
486
895
  *
896
+ * A version the project file does not state is taken, in order, from the
897
+ * versions this project restored, from central package management, and last
898
+ * from a version the whole scan agrees on. Two projects of one tree routinely
899
+ * restore different versions of a package, so a version taken from another
900
+ * project would describe a package this project does not use.
901
+ *
487
902
  * @param {String} csProjData Raw data
488
903
  * @param {String} projFile File name
489
- * @param {Object} pkgNameVersions Package name - version map object
904
+ * @param {Object} pkgNameVersions Versions this project restored (its project.assets.json,
905
+ * packages.lock.json or packages.config), keyed by package id or lowercased id
490
906
  * @param {Boolean} msbuildInstalled Whether msbuild is available to resolve properties
491
- * @param {Object} pkgVersionLabelCandidates Candidate values for msbuild version properties
907
+ * @param {Object} pkgVersionLabelCandidates Candidate values for msbuild version properties,
908
+ * from the props files that can apply to this project
492
909
  * @param {Object} centralVersions Versions declared centrally in Directory.Packages.props,
493
910
  * keyed by lowercased package id. See {@link getCentralPackageVersions}.
911
+ * @param {Object} fallbackVersions Versions every project of the scan agrees on, keyed by
912
+ * lowercased package id, used only when nothing closer states one
913
+ * @param {Object} restoredNames Package ids as this project's restore output spells them,
914
+ * keyed by lowercased id. NuGet ids are case-insensitive, so a reference spelled
915
+ * differently names the same package and takes the restored spelling.
916
+ * @param {{name: string, version: (string|undefined)}} [projectIdentity] Package id and
917
+ * version this project's project.assets.json records for the project. They name the
918
+ * project in place of what the project file states.
494
919
  *
495
- * @returns {Object} Containing parent component, package, and dependencies
920
+ * @returns {Object} Containing parent component, package, dependencies, and the
921
+ * `$(Name)` version labels left unresolved without msbuild
496
922
  */
497
923
  export function parseCsProjData(
498
924
  csProjData,
@@ -501,6 +927,9 @@ export function parseCsProjData(
501
927
  msbuildInstalled = false,
502
928
  pkgVersionLabelCandidates = {},
503
929
  centralVersions = {},
930
+ fallbackVersions = {},
931
+ restoredNames = {},
932
+ projectIdentity = undefined,
504
933
  ) {
505
934
  const pkgList = [];
506
935
  const parentComponent = { type: "application", properties: [] };
@@ -529,6 +958,39 @@ export function parseCsProjData(
529
958
  return pkgList;
530
959
  }
531
960
  const project = projects[0];
961
+ const restoredName = (name) => restoredNames[name.toLowerCase()] || name;
962
+ const resolvedVersion = (name) =>
963
+ lookupNugetVersion(pkgNameVersions, name) ||
964
+ lookupNugetVersion(centralVersions, name) ||
965
+ lookupNugetVersion(fallbackVersions, name);
966
+ // `$(Name)` version labels: the project's own properties, then the props
967
+ // files that can apply to it, then (only for what neither settles) one
968
+ // msbuild evaluation of the project.
969
+ const ownProperties = projectOwnProperties(project);
970
+ const labelOf = (version) => version?.match(/^\$\((.*)\)$/)?.[1];
971
+ const staticLabelValue = (label) =>
972
+ ownProperties[label] ??
973
+ (pkgVersionLabelCandidates[label]?.length === 1
974
+ ? pkgVersionLabelCandidates[label][0]
975
+ : undefined);
976
+ let msbuildValues = {};
977
+ const unresolved = new Set();
978
+ for (const item of project.ItemGroup || []) {
979
+ for (const reference of item.PackageReference || []) {
980
+ const label = labelOf(
981
+ reference.$?.VersionOverride ||
982
+ reference.VersionOverride?.[0]._?.[0] ||
983
+ reference.$?.Version ||
984
+ reference.Version?.[0]._?.[0],
985
+ );
986
+ if (label && staticLabelValue(label) === undefined) {
987
+ unresolved.add(label);
988
+ }
989
+ }
990
+ }
991
+ if (msbuildInstalled) {
992
+ msbuildValues = evaluateMsbuildProperties(projFile, [...unresolved].sort());
993
+ }
532
994
  // SDK-style projects declare their version through the MSBuild properties
533
995
  // that `dotnet pack` reads: PackageVersion, which defaults to Version, which
534
996
  // in turn defaults to VersionPrefix plus an optional `-VersionSuffix`. A
@@ -726,53 +1188,30 @@ export function parseCsProjData(
726
1188
  if (!pref.Include || pref.Include.includes(".csproj")) {
727
1189
  continue;
728
1190
  }
729
- pkg.name = pref.Include;
1191
+ pkg.name = restoredName(pref.Include);
730
1192
  // Under central package management a PackageReference carries no Version at
731
1193
  // all, and VersionOverride is the documented way for a project to opt out of
732
- // the central version, so it wins over everything else. The central version
733
- // is consulted only after pkgNameVersions, which holds versions already
734
- // resolved from project.assets.json / packages.lock.json and is therefore
735
- // more precise than a central declaration that may be a floating range.
1194
+ // the central version, so it wins over everything else. Otherwise the version
1195
+ // this project restored is more precise than a central declaration that may
1196
+ // be a floating range.
736
1197
  pkg.version =
737
1198
  pref.VersionOverride ||
738
1199
  item.PackageReference[j].VersionOverride?.[0]._?.[0] ||
739
1200
  pref.Version ||
740
1201
  item.PackageReference[j].Version?.[0]._?.[0] ||
741
- pkgNameVersions[pkg.name] ||
742
- centralVersions[pkg.name.toLowerCase()];
743
- if (pkg.version) {
744
- const version_label_match = pkg.version.match(/^\$\((.*)\)$/)?.[1];
745
- if (version_label_match) {
746
- let version_resolved = false;
747
- if (msbuildInstalled) {
748
- const result = safeSpawnSync(
749
- DOTNET_CMD,
750
- [
751
- `msbuild ${projFile} -nologo -getProperty:${version_label_match}`,
752
- ],
753
- { shell: isWin },
754
- );
755
- if (
756
- result.status === 0 &&
757
- !result.error &&
758
- result.stdout.trim()
759
- ) {
760
- pkg.version = result.stdout.trim();
761
- version_resolved = true;
762
- }
763
- }
764
- if (!version_resolved) {
765
- if (
766
- pkgVersionLabelCandidates[version_label_match]?.length === 1
767
- ) {
768
- // Prioritizing correctness over completeness: reject resolved package version label if it has more than one possible values.
769
- pkg.version = pkgVersionLabelCandidates[version_label_match][0];
770
- } else if (DEBUG_MODE) {
771
- console.log(
772
- `Could not resolve package version label ${version_label_match} for ${pkg.name}`,
773
- );
774
- }
775
- }
1202
+ resolvedVersion(pkg.name);
1203
+ const versionLabel = labelOf(pkg.version);
1204
+ if (versionLabel) {
1205
+ // Prioritizing correctness over completeness: a label with more than one
1206
+ // candidate value across the applicable props files stays unresolved.
1207
+ const labelValue =
1208
+ msbuildValues[versionLabel] ?? staticLabelValue(versionLabel);
1209
+ if (labelValue !== undefined) {
1210
+ pkg.version = labelValue;
1211
+ } else if (DEBUG_MODE) {
1212
+ console.log(
1213
+ `Could not resolve package version label ${versionLabel} for ${pkg.name}`,
1214
+ );
776
1215
  }
777
1216
  }
778
1217
  // A `Version` attribute holds whatever the project declares, which may
@@ -789,9 +1228,7 @@ export function parseCsProjData(
789
1228
  pkg.version = exactPin[1];
790
1229
  } else if (!concreteVersion(pkg.version)) {
791
1230
  declaredRange = pkg.version;
792
- pkg.version =
793
- pkgNameVersions[pkg.name] ||
794
- centralVersions[pkg.name.toLowerCase()];
1231
+ pkg.version = resolvedVersion(pkg.name);
795
1232
  }
796
1233
  }
797
1234
  // A version that could not be resolved from any source must not be
@@ -866,21 +1303,20 @@ export function parseCsProjData(
866
1303
  }
867
1304
  }
868
1305
  }
1306
+ pkg.name = restoredName(pkg.name);
869
1307
  let declaredAssemblyVersion;
870
1308
  if (incParts.length > 1 && incParts[1].includes("Version")) {
871
1309
  const declared = incParts[1].replace("Version=", "").trim();
872
- // An assembly reference carries the version inline, and that value can
873
- // be an MSBuild property such as `Version=$(TargetFSharpCoreVersion)`
874
- // which only a build evaluates. Anything that is not a concrete
875
- // version is resolved from the manifests that pin one, keeping the
876
- // declaration as a property.
1310
+ // An assembly reference carries the assembly's version inline, which is
1311
+ // not the version of the package that ships it (Newtonsoft.Json 13.0.3
1312
+ // ships assembly 13.0.0.0), and can be an MSBuild property such as
1313
+ // `Version=$(TargetFSharpCoreVersion)` that only a build evaluates. A
1314
+ // concrete one is kept as the assembly version; a property as a
1315
+ // declaration.
877
1316
  if (declared && !concreteVersion(declared)) {
878
1317
  declaredAssemblyVersion = declared;
879
- assemblyVersion =
880
- pkgNameVersions[pkg.name] ||
881
- centralVersions[pkg.name.toLowerCase()];
882
- } else {
883
- assemblyVersion = declared || pkgNameVersions[pkg.name];
1318
+ } else if (declared) {
1319
+ assemblyVersion = declared;
884
1320
  }
885
1321
  }
886
1322
  if (declaredAssemblyVersion) {
@@ -889,7 +1325,11 @@ export function parseCsProjData(
889
1325
  value: declaredAssemblyVersion,
890
1326
  });
891
1327
  }
892
- const version = hintVersion ?? assemblyVersion;
1328
+ // The package version comes from the HintPath's package folder, or from
1329
+ // what this project restored; never from the assembly version, and not
1330
+ // from a central pin, which applies to PackageReference items only.
1331
+ const version =
1332
+ hintVersion ?? lookupNugetVersion(pkgNameVersions, pkg.name);
893
1333
  // An assembly reference that states no version anywhere - no HintPath to
894
1334
  // read one from and no `Version=` on the Include - leaves the component
895
1335
  // versionless in both fields. The registry's latest release is not a
@@ -917,7 +1357,7 @@ export function parseCsProjData(
917
1357
  value: assemblyName,
918
1358
  });
919
1359
  }
920
- if (hintVersion && assemblyVersion) {
1360
+ if (assemblyVersion) {
921
1361
  pkg.properties.push({
922
1362
  name: "cdx:dotnet:assembly_version",
923
1363
  value: assemblyVersion,
@@ -966,14 +1406,47 @@ export function parseCsProjData(
966
1406
  }
967
1407
  if (sdkVersion) {
968
1408
  parentComponent.version = sdkVersion;
1409
+ } else if (
1410
+ !parentComponent.version &&
1411
+ !Object.keys(sdkVersionProps).length &&
1412
+ (project.$?.Sdk || project.Sdk)
1413
+ ) {
1414
+ // An SDK-style project that sets no version builds as 1.0.0, the Version
1415
+ // its project.assets.json records too, so both describe one component. A
1416
+ // version set from a build-time property is unknown, not 1.0.0.
1417
+ parentComponent.version = "1.0.0";
1418
+ }
1419
+ // Only a build knows the value of a property that references another one
1420
+ const literal = (value) =>
1421
+ value && !value.includes("$(") ? value : undefined;
1422
+ // NuGet names a project's package by its PackageId, which defaults to the
1423
+ // assembly name, so a PackageId the project file states names the project.
1424
+ const declaredPackageId = literal(ownProperties.PackageId);
1425
+ if (declaredPackageId) {
1426
+ parentComponent.name = declaredPackageId;
1427
+ }
1428
+ // Restore evaluated the project with everything MSBuild imports and every
1429
+ // property it was given: a Version from Directory.Build.props, `-p:Version`
1430
+ // or `$(BuildNumber)`, a PackageId. Its project.assets.json roots the
1431
+ // restored dependency tree at that identity, so naming the project from its
1432
+ // file alone would make it two components.
1433
+ if (projectIdentity?.name) {
1434
+ parentComponent.name = projectIdentity.name;
1435
+ parentComponent.version = projectIdentity.version;
1436
+ }
1437
+ // The assembly the project builds keeps its name when the package differs
1438
+ const declaredAssemblyName = literal(ownProperties.AssemblyName);
1439
+ if (declaredAssemblyName && declaredAssemblyName !== parentComponent.name) {
1440
+ parentComponent.properties.push({
1441
+ name: "cdx:dotnet:assembly_name",
1442
+ value: declaredAssemblyName,
1443
+ });
969
1444
  }
970
1445
  // The purl is built once every PropertyGroup has been read, so a version
971
- // declared after OutputType is not lost.
1446
+ // declared after OutputType is not lost. A project that states no version
1447
+ // gets a versionless purl: "latest" is not a version.
972
1448
  if (parentComponent.name) {
973
- applyPurl(
974
- parentComponent,
975
- nugetPurl(parentComponent.name, parentComponent.version || "latest"),
976
- );
1449
+ applyNugetVersion(parentComponent, parentComponent.version);
977
1450
  }
978
1451
  let dependencies = [];
979
1452
  if (parentComponent?.["bom-ref"]) {
@@ -988,9 +1461,46 @@ export function parseCsProjData(
988
1461
  pkgList,
989
1462
  parentComponent,
990
1463
  dependencies,
1464
+ // Version labels no static source settled, which only msbuild could
1465
+ unresolvedVersionLabels: msbuildInstalled ? [] : [...unresolved].sort(),
991
1466
  };
992
1467
  }
993
1468
 
1469
+ // The package folders a consuming project compiles against or loads at run
1470
+ // time. A DLL elsewhere in the package (build/, buildTransitive/, tools/,
1471
+ // analyzers/) is a build-time helper of the package itself, such as the
1472
+ // System.Formats.Asn1.dll coverlet.collector carries for its test-platform
1473
+ // collector: the project's code never calls it, so its name must not credit
1474
+ // the package with the project's framework calls (issue 4441).
1475
+ const DOTNET_REFERENCED_ASSET_FOLDERS = new Set(["lib", "ref", "runtimes"]);
1476
+
1477
+ /**
1478
+ * Whether a file of a NuGet package is an assembly a consuming project can
1479
+ * reference: a .dll, .exe, or .so under lib/, ref/, or runtimes/.
1480
+ *
1481
+ * @param {string} packageFile Path of the file inside the package, as project.assets.json lists it
1482
+ * @returns {boolean} true for a referenced assembly
1483
+ */
1484
+ export function isReferencedDotnetPackageFile(packageFile) {
1485
+ if (typeof packageFile !== "string") {
1486
+ return false;
1487
+ }
1488
+ const lowerFile = packageFile.toLowerCase();
1489
+ if (
1490
+ !lowerFile.endsWith(".dll") &&
1491
+ !lowerFile.endsWith(".exe") &&
1492
+ !lowerFile.endsWith(".so")
1493
+ ) {
1494
+ return false;
1495
+ }
1496
+ const normalized = lowerFile.replaceAll("\\", "/");
1497
+ const slashIndex = normalized.indexOf("/");
1498
+ return (
1499
+ slashIndex > 0 &&
1500
+ DOTNET_REFERENCED_ASSET_FOLDERS.has(normalized.slice(0, slashIndex))
1501
+ );
1502
+ }
1503
+
994
1504
  /**
995
1505
  * Parse a .NET project.assets.json file and return the package list and dependency tree.
996
1506
  *
@@ -1000,7 +1510,10 @@ export function parseCsProjData(
1000
1510
  *
1001
1511
  * @param {string} csProjData Raw JSON string of the project.assets.json file
1002
1512
  * @param {string} assetsJsonFile Path to the project.assets.json file, used for evidence properties
1003
- * @returns {{ pkgList: Object[], dependenciesList: Object[] }}
1513
+ * @returns {{ pkgList: Object[], dependenciesList: Object[], projectPath: (string|undefined), outputPath: (string|undefined), projectIdentity: ({name: string, version: (string|undefined)}|undefined) }}
1514
+ * `projectPath` is the project file the restore output belongs to and `outputPath` the
1515
+ * directory restore wrote it to, both as restore recorded them. `projectIdentity` is the
1516
+ * package id and version restore evaluated for the project, which root the dependency tree.
1004
1517
  */
1005
1518
  export function parseCsProjAssetsData(csProjData, assetsJsonFile) {
1006
1519
  // extract name, operator, version from .NET package representation
@@ -1040,22 +1553,18 @@ export function parseCsProjAssetsData(csProjData, assetsJsonFile) {
1040
1553
  return { pkgList, dependenciesList };
1041
1554
  }
1042
1555
  csProjData = JSON.parse(csProjData);
1043
- let purlString;
1556
+ const projectPath = csProjData.project?.restore?.projectPath;
1557
+ let rootRef;
1044
1558
  if (csProjData.project?.restore?.projectName) {
1045
- purlString = build({
1046
- type: "nuget",
1047
- namespace: "" || null,
1048
- name: csProjData.project?.restore?.projectName,
1049
- version: csProjData.project.version || "latest" || null,
1050
- });
1559
+ // The project's own version as restore evaluated it; "latest" is not a
1560
+ // version, so a project without one gets a versionless purl.
1051
1561
  rootPkg = {
1052
1562
  group: "",
1053
1563
  name: csProjData.project.restore.projectName,
1054
- version: csProjData.project.version || "latest",
1055
1564
  type: "application",
1056
- purl: purlString,
1057
- "bom-ref": decodeURIComponent(purlString),
1058
1565
  };
1566
+ applyNugetVersion(rootPkg, csProjData.project.version);
1567
+ rootRef = rootPkg["bom-ref"];
1059
1568
  pkgList.push(rootPkg);
1060
1569
  }
1061
1570
  const rootPkgDeps = new Set();
@@ -1069,53 +1578,42 @@ export function parseCsProjAssetsData(csProjData, assetsJsonFile) {
1069
1578
  if (nameOperatorVersion == null) {
1070
1579
  continue;
1071
1580
  }
1072
- const targetNameVersion = `${nameOperatorVersion.name}/${nameOperatorVersion.version}`;
1073
- let nameToUse = nameOperatorVersion.name;
1074
- // Due to the difference in casing, we might arrive this case where a simple lookup doesn't succeed.
1075
- // Instead of skipping, let's work harder to find a match.
1076
- if (!csProjData.targets[frameworkTarget][targetNameVersion]) {
1077
- let matchFound = false;
1078
- for (const fkeys of Object.keys(
1079
- csProjData.targets[frameworkTarget],
1080
- )) {
1081
- const tmpParts = fkeys.split("/");
1082
- const tname = tmpParts[0];
1083
- const tversion = tmpParts[1];
1084
- if (
1085
- tname.toLowerCase() === nameOperatorVersion.name.toLowerCase() &&
1086
- tversion === nameOperatorVersion.version.replace("-*", "")
1087
- ) {
1088
- nameToUse = tname;
1089
- matchFound = true;
1090
- break;
1091
- }
1092
- }
1093
- if (!matchFound) {
1094
- if (DEBUG_MODE) {
1095
- console.log(
1096
- "Unable to match",
1097
- dependencyName,
1098
- "with a target name. The dependency tree will be imprecise.",
1099
- );
1100
- }
1101
- continue;
1581
+ // A direct dependency is declared as a constraint (`Moq >= 4.17.6`,
1582
+ // `X >= 7.0.0-*`, `[4.0.0, 5.0.0)`), and restore installs one version of
1583
+ // each package per target: the edge points at that installed version,
1584
+ // whatever the constraint's lower bound says.
1585
+ const resolvedTarget = Object.keys(
1586
+ csProjData.targets[frameworkTarget] || {},
1587
+ ).find(
1588
+ (targetKey) =>
1589
+ targetKey.split("/")[0].toLowerCase() ===
1590
+ nameOperatorVersion.name.toLowerCase(),
1591
+ );
1592
+ if (!resolvedTarget) {
1593
+ if (DEBUG_MODE) {
1594
+ console.log(
1595
+ "Unable to match",
1596
+ dependencyName,
1597
+ "with a target name. The dependency tree will be imprecise.",
1598
+ );
1102
1599
  }
1600
+ continue;
1103
1601
  }
1104
-
1602
+ const [nameToUse, resolvedVersion] = resolvedTarget.split("/");
1105
1603
  const dpurl = decodeURIComponent(
1106
1604
  build({
1107
1605
  type: "nuget",
1108
1606
  namespace: "" || null,
1109
1607
  name: nameToUse,
1110
- version: nameOperatorVersion.version || null,
1608
+ version: resolvedVersion || null,
1111
1609
  }),
1112
1610
  );
1113
1611
  rootPkgDeps.add(dpurl);
1114
1612
  }
1115
1613
  }
1116
- if (purlString && rootPkgDeps.size) {
1614
+ if (rootRef && rootPkgDeps.size) {
1117
1615
  dependenciesList.push({
1118
- ref: purlString,
1616
+ ref: rootRef,
1119
1617
  dependsOn: Array.from(rootPkgDeps).sort(),
1120
1618
  });
1121
1619
  }
@@ -1154,12 +1652,8 @@ export function parseCsProjAssetsData(csProjData, assetsJsonFile) {
1154
1652
  if (lib[rootDep].files && Array.isArray(lib[rootDep].files)) {
1155
1653
  const dllFiles = new Set();
1156
1654
  lib[rootDep].files.forEach((f) => {
1157
- if (
1158
- f.endsWith(".dll") ||
1159
- f.endsWith(".exe") ||
1160
- f.endsWith(".so")
1161
- ) {
1162
- dllFiles.add(basename(f));
1655
+ if (isReferencedDotnetPackageFile(f)) {
1656
+ dllFiles.add(basename(f.replaceAll("\\", "/")));
1163
1657
  }
1164
1658
  });
1165
1659
  pkg.properties = [
@@ -1246,6 +1740,11 @@ export function parseCsProjAssetsData(csProjData, assetsJsonFile) {
1246
1740
  return {
1247
1741
  pkgList,
1248
1742
  dependenciesList,
1743
+ projectPath,
1744
+ outputPath: csProjData.project?.restore?.outputPath,
1745
+ projectIdentity: rootRef
1746
+ ? { name: rootPkg.name, version: rootPkg.version }
1747
+ : undefined,
1249
1748
  };
1250
1749
  }
1251
1750
 
@@ -1321,39 +1820,55 @@ export function parseCsPkgLockData(csLockData, pkgLockFile) {
1321
1820
  }
1322
1821
  const dependsOn = new Set();
1323
1822
  if (libData.dependencies) {
1324
- for (let adep of Object.keys(libData.dependencies)) {
1325
- let adepResolvedVersion = libData.dependencies[adep];
1326
- const aversionNoRuntime = aversion.split("/")[0];
1327
- // Try to get the resolved version of the dependency. See #930 and #937
1328
- if (assetData.dependencies[aversion]?.[adep]?.resolved) {
1329
- adepResolvedVersion =
1330
- assetData.dependencies[aversion][adep].resolved;
1331
- } else if (
1332
- aversion.includes("/") &&
1333
- assetData.dependencies[aversionNoRuntime]?.[adep]?.resolved
1334
- ) {
1335
- adepResolvedVersion =
1336
- assetData.dependencies[aversionNoRuntime][adep].resolved;
1337
- } else if (
1338
- (assetData.dependencies[aversion]?.[adep.toLowerCase()] &&
1339
- assetData.dependencies[aversion][adep.toLowerCase()].type ===
1340
- "Project") ||
1341
- (assetData.dependencies[aversionNoRuntime]?.[adep.toLowerCase()] &&
1342
- assetData.dependencies[aversionNoRuntime][adep.toLowerCase()]
1343
- .type === "Project")
1344
- ) {
1345
- adepResolvedVersion = undefined;
1346
- adep = adep.toLowerCase();
1347
- } else if (DEBUG_MODE) {
1348
- console.warn(
1349
- `Unable to find the resolved version for ${adep} ${aversion}. Using ${adepResolvedVersion} which may be incorrect.`,
1823
+ const aversionNoRuntime = aversion.split("/")[0];
1824
+ // The entry for a dependency: in this target, the target without its
1825
+ // runtime identifier, or a runtime-specific target of the same
1826
+ // framework. Package ids are case-insensitive, and the lock file writes
1827
+ // a dependency the way the depending package's nuspec spells it. See
1828
+ // #930 and #937.
1829
+ const sections = [
1830
+ assetData.dependencies[aversion],
1831
+ assetData.dependencies[aversionNoRuntime],
1832
+ ...Object.keys(assetData.dependencies)
1833
+ .filter((key) => key.startsWith(`${aversionNoRuntime}/`))
1834
+ .map((key) => assetData.dependencies[key]),
1835
+ ].filter(Boolean);
1836
+ const entryFor = (name) => {
1837
+ for (const section of sections) {
1838
+ const key = Object.keys(section).find(
1839
+ (candidate) => candidate.toLowerCase() === name.toLowerCase(),
1350
1840
  );
1841
+ if (key) {
1842
+ return { name: key, entry: section[key] };
1843
+ }
1844
+ }
1845
+ return undefined;
1846
+ };
1847
+ for (const adep of Object.keys(libData.dependencies)) {
1848
+ const found = entryFor(adep);
1849
+ let depName;
1850
+ let depVersion;
1851
+ if (found?.entry?.type === "Project") {
1852
+ depName = found.name;
1853
+ } else if (found?.entry?.resolved) {
1854
+ depName = found.name;
1855
+ depVersion = found.entry.resolved;
1856
+ } else {
1857
+ // The dependency value is the declared range (`[6.0.0, )`), which
1858
+ // names no package version: without a resolved entry there is no
1859
+ // component to point at.
1860
+ if (DEBUG_MODE) {
1861
+ console.warn(
1862
+ `Unable to find the resolved version for ${adep} ${aversion}. The dependency tree will be imprecise.`,
1863
+ );
1864
+ }
1865
+ continue;
1351
1866
  }
1352
1867
  const adpurl = build({
1353
1868
  type: "nuget",
1354
1869
  namespace: "" || null,
1355
- name: adep,
1356
- version: adepResolvedVersion || null,
1870
+ name: depName,
1871
+ version: depVersion || null,
1357
1872
  });
1358
1873
  dependsOn.add(decodeURIComponent(adpurl));
1359
1874
  }