@cdxgen/cdxgen 13.6.0 → 13.7.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 (264) hide show
  1. package/README.md +15 -8
  2. package/bin/audit.js +7 -4
  3. package/bin/cdxgen.js +256 -37
  4. package/bin/convert.js +146 -22
  5. package/bin/evinse.js +25 -3
  6. package/bin/repl.js +47 -3
  7. package/bin/sign.js +211 -34
  8. package/bin/tracebom.js +18 -6
  9. package/bin/validate.js +18 -3
  10. package/bin/verify.js +113 -5
  11. package/data/behavior-taxonomy.schema.json +1491 -0
  12. package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
  13. package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
  14. package/data/pqc-migration.json +239 -0
  15. package/data/remediations.json +52 -0
  16. package/data/rules/cbom-security.yaml +4 -3
  17. package/data/rules/package-integrity.yaml +28 -0
  18. package/lib/audit/index.js +25 -3
  19. package/lib/cli/bomAssembly.js +80 -1
  20. package/lib/cli/cliOptions.js +121 -17
  21. package/lib/cli/index.js +609 -898
  22. package/lib/cli/jsBom.js +3 -0
  23. package/lib/cli/jvmBom.js +365 -40
  24. package/lib/cli/managedBom.js +19 -3
  25. package/lib/cli/nativeBom.js +10 -0
  26. package/lib/core/env.js +26 -0
  27. package/lib/core/fs.js +126 -3
  28. package/lib/core/globPatterns.js +218 -0
  29. package/lib/core/processTree.js +99 -12
  30. package/lib/core/projectConfig.js +332 -25
  31. package/lib/core/toolRegistry.js +15 -1
  32. package/lib/ecosystems/denoutils.js +2 -2
  33. package/lib/ecosystems/golem.js +3 -8
  34. package/lib/ecosystems/gradleutils.js +1082 -6
  35. package/lib/ecosystems/jsEvidence.js +90 -38
  36. package/lib/ecosystems/kosi.js +3 -5
  37. package/lib/ecosystems/npmVersionResolution.js +376 -0
  38. package/lib/ecosystems/parsers-python.js +89 -28
  39. package/lib/ecosystems/parsers-rust.js +117 -15
  40. package/lib/ecosystems/remote/huggingface.js +22 -0
  41. package/lib/ecosystems/rusi.js +2 -4
  42. package/lib/ecosystems/scalasem.js +2 -1
  43. package/lib/evinser/evinser.js +196 -34
  44. package/lib/evinser/scalasem.js +5 -1
  45. package/lib/helpers/agenticToolutils.js +112 -1
  46. package/lib/helpers/bomSigner.js +43 -9
  47. package/lib/helpers/jssSigner.js +1276 -0
  48. package/lib/inventory/agentBlueprint.js +193 -0
  49. package/lib/inventory/agentToolNames.js +145 -0
  50. package/lib/inventory/aiCollector.js +153 -7
  51. package/lib/inventory/aiInventory.js +44 -8
  52. package/lib/inventory/aiProviders.js +324 -0
  53. package/lib/inventory/bomUtils.js +8 -1
  54. package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
  55. package/lib/inventory/citations.js +31 -2
  56. package/lib/inventory/communityAiConfigParser.js +978 -80
  57. package/lib/inventory/cyclonedx20.js +152 -0
  58. package/lib/inventory/cyclonedx20Common.js +286 -0
  59. package/lib/inventory/cyclonedx20Derived.js +159 -0
  60. package/lib/inventory/cyclonedx20Parties.js +552 -0
  61. package/lib/inventory/cyclonedx20Read.js +768 -0
  62. package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
  63. package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
  64. package/lib/inventory/cyclonedx20ReadContext.js +380 -0
  65. package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
  66. package/lib/inventory/cyclonedx20ReadSections.js +599 -0
  67. package/lib/inventory/cyclonedx20Render.js +766 -0
  68. package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
  69. package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
  70. package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
  71. package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
  72. package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
  73. package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
  74. package/lib/inventory/cyclonedx20RenderSections.js +499 -0
  75. package/lib/inventory/cyclonedx20Tables.js +594 -0
  76. package/lib/inventory/deps.js +106 -13
  77. package/lib/inventory/dosai.js +236 -1
  78. package/lib/inventory/dosaiParsers.js +3 -6
  79. package/lib/inventory/dotnetRuntime.js +164 -14
  80. package/lib/inventory/ecosystemFormulationParsers.js +979 -0
  81. package/lib/inventory/envcontext.js +132 -38
  82. package/lib/inventory/evidenceUtils.js +2 -1
  83. package/lib/inventory/formulationParsers.js +95 -13
  84. package/lib/inventory/hbom.js +45 -6
  85. package/lib/inventory/hbomLoader.js +166 -0
  86. package/lib/inventory/mcpConfigParser.js +5 -0
  87. package/lib/inventory/occurrenceKey.js +90 -0
  88. package/lib/inventory/pqcMigration.js +426 -0
  89. package/lib/inventory/rustFormulationParser.js +21 -10
  90. package/lib/inventory/source.js +65 -6
  91. package/lib/parsers/huggingfaceManifest.js +9 -1
  92. package/lib/parsers/huggingfaceUtils.js +18 -0
  93. package/lib/parsers/kiroPermissions.js +166 -0
  94. package/lib/server/server.js +36 -34
  95. package/lib/stages/postgen/postgen.js +41 -1
  96. package/lib/stages/postgen/specVersionCompat.js +76 -0
  97. package/lib/validator/bomValidator.js +8 -1
  98. package/lib/validator/complianceEngine.js +30 -3
  99. package/lib/validator/complianceRules.js +1416 -4
  100. package/lib/validator/generated/validate-2.0.mjs +57554 -13851
  101. package/lib/validator/index.js +48 -7
  102. package/lib/validator/reporters/json.js +6 -0
  103. package/package.json +9 -9
  104. package/types/bin/repl.d.ts.map +1 -1
  105. package/types/lib/audit/index.d.ts +6 -2
  106. package/types/lib/audit/index.d.ts.map +1 -1
  107. package/types/lib/cli/bomAssembly.d.ts +16 -0
  108. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  109. package/types/lib/cli/cliOptions.d.ts +6 -0
  110. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  111. package/types/lib/cli/index.d.ts.map +1 -1
  112. package/types/lib/cli/jsBom.d.ts +2 -1
  113. package/types/lib/cli/jsBom.d.ts.map +1 -1
  114. package/types/lib/cli/jvmBom.d.ts +15 -0
  115. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  116. package/types/lib/cli/managedBom.d.ts.map +1 -1
  117. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  118. package/types/lib/core/boundedFile.d.ts +30 -0
  119. package/types/lib/core/boundedFile.d.ts.map +1 -1
  120. package/types/lib/core/env.d.ts +10 -0
  121. package/types/lib/core/env.d.ts.map +1 -1
  122. package/types/lib/core/fs.d.ts +4 -0
  123. package/types/lib/core/fs.d.ts.map +1 -1
  124. package/types/lib/core/globPatterns.d.ts +70 -0
  125. package/types/lib/core/globPatterns.d.ts.map +1 -0
  126. package/types/lib/core/paths.d.ts +9 -0
  127. package/types/lib/core/paths.d.ts.map +1 -1
  128. package/types/lib/core/processTree.d.ts +16 -5
  129. package/types/lib/core/processTree.d.ts.map +1 -1
  130. package/types/lib/core/projectConfig.d.ts +47 -8
  131. package/types/lib/core/projectConfig.d.ts.map +1 -1
  132. package/types/lib/core/toolRegistry.d.ts.map +1 -1
  133. package/types/lib/ecosystems/golem.d.ts.map +1 -1
  134. package/types/lib/ecosystems/gradleutils.d.ts +248 -8
  135. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  136. package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
  137. package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
  138. package/types/lib/ecosystems/kosi.d.ts.map +1 -1
  139. package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
  140. package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
  141. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  142. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  143. package/types/lib/ecosystems/pythonutils.d.ts +1 -1
  144. package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
  145. package/types/lib/ecosystems/rusi.d.ts.map +1 -1
  146. package/types/lib/ecosystems/scalasem.d.ts +58 -16
  147. package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
  148. package/types/lib/evinser/evinser.d.ts +61 -9
  149. package/types/lib/evinser/evinser.d.ts.map +1 -1
  150. package/types/lib/evinser/scalasem.d.ts.map +1 -1
  151. package/types/lib/helpers/agenticToolutils.d.ts +53 -19
  152. package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
  153. package/types/lib/helpers/bomSigner.d.ts +20 -3
  154. package/types/lib/helpers/bomSigner.d.ts.map +1 -1
  155. package/types/lib/helpers/jssSigner.d.ts +288 -0
  156. package/types/lib/helpers/jssSigner.d.ts.map +1 -0
  157. package/types/lib/helpers/vsixutils.d.ts +303 -19
  158. package/types/lib/helpers/vsixutils.d.ts.map +1 -1
  159. package/types/lib/inventory/agentBlueprint.d.ts +70 -0
  160. package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
  161. package/types/lib/inventory/agentToolNames.d.ts +51 -0
  162. package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
  163. package/types/lib/inventory/aiCollector.d.ts.map +1 -1
  164. package/types/lib/inventory/aiInventory.d.ts.map +1 -1
  165. package/types/lib/inventory/aiProviders.d.ts +85 -0
  166. package/types/lib/inventory/aiProviders.d.ts.map +1 -0
  167. package/types/lib/inventory/bomUtils.d.ts.map +1 -1
  168. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
  169. package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
  170. package/types/lib/inventory/citations.d.ts +5 -0
  171. package/types/lib/inventory/citations.d.ts.map +1 -1
  172. package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
  173. package/types/lib/inventory/cyclonedx20.d.ts +99 -0
  174. package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
  175. package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
  176. package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
  177. package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
  178. package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
  179. package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
  180. package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
  181. package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
  182. package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
  183. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
  184. package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
  185. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
  186. package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
  187. package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
  188. package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
  189. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
  190. package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
  191. package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
  192. package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
  193. package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
  194. package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
  195. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
  196. package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
  197. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
  198. package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
  199. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
  200. package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
  201. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
  202. package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
  203. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
  204. package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
  205. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
  206. package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
  207. package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
  208. package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
  209. package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
  210. package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
  211. package/types/lib/inventory/deps.d.ts +35 -2
  212. package/types/lib/inventory/deps.d.ts.map +1 -1
  213. package/types/lib/inventory/dosai.d.ts +19 -0
  214. package/types/lib/inventory/dosai.d.ts.map +1 -1
  215. package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
  216. package/types/lib/inventory/dotnetRuntime.d.ts +51 -4
  217. package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -1
  218. package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
  219. package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
  220. package/types/lib/inventory/envcontext.d.ts +22 -6
  221. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  222. package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
  223. package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
  224. package/types/lib/inventory/hbom.d.ts +11 -0
  225. package/types/lib/inventory/hbom.d.ts.map +1 -1
  226. package/types/lib/inventory/hbomLoader.d.ts +54 -0
  227. package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
  228. package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
  229. package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
  230. package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
  231. package/types/lib/inventory/occurrenceKey.d.ts +52 -0
  232. package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
  233. package/types/lib/inventory/pqcMigration.d.ts +84 -0
  234. package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
  235. package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
  236. package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
  237. package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
  238. package/types/lib/inventory/scalaDedupe.d.ts +43 -0
  239. package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
  240. package/types/lib/inventory/source.d.ts +15 -0
  241. package/types/lib/inventory/source.d.ts.map +1 -1
  242. package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
  243. package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
  244. package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
  245. package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
  246. package/types/lib/parsers/kiroPermissions.d.ts +62 -0
  247. package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
  248. package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
  249. package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
  250. package/types/lib/server/server.d.ts.map +1 -1
  251. package/types/lib/stages/postgen/postgen.d.ts +7 -0
  252. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  253. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  254. package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
  255. package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
  256. package/types/lib/validator/bomValidator.d.ts +5 -1
  257. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  258. package/types/lib/validator/complianceEngine.d.ts +4 -4
  259. package/types/lib/validator/complianceEngine.d.ts.map +1 -1
  260. package/types/lib/validator/complianceRules.d.ts +14 -2
  261. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  262. package/types/lib/validator/index.d.ts +10 -0
  263. package/types/lib/validator/index.d.ts.map +1 -1
  264. package/types/lib/validator/reporters/json.d.ts.map +1 -1
@@ -11,7 +11,7 @@
11
11
  // https://learn.microsoft.com/dotnet/core/dependency-loading/default-probing#how-are-the-properties-populated
12
12
 
13
13
  import { closeSync, readSync } from "node:fs";
14
- import { join } from "node:path";
14
+ import { isAbsolute, join } from "node:path";
15
15
 
16
16
  import { globSync } from "glob";
17
17
 
@@ -38,6 +38,12 @@ const DOTNET_ROOTS = [
38
38
  ];
39
39
  const RUNTIME_PACK_RE =
40
40
  /^(Microsoft\.[A-Za-z]+(?:\.[A-Za-z]+)*\.App\.Runtime\.[A-Za-z0-9.-]+)\/(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/;
41
+ // The version shape a runtime pack names, anchored for the versions a line of
42
+ // `dotnet --list-runtimes` reports.
43
+ const RUNTIME_VERSION_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
44
+ // The pre-release label a CPE can name: `rc` or `preview` with its number,
45
+ // written with or without a dot between the label and the number.
46
+ const PRERELEASE_LABEL_RE = /^(rc|preview)\.?(\d+)(?:[.-]|$)/;
41
47
 
42
48
  function readJsonBounded(absPath, rootDir) {
43
49
  const opened = openRegularFile(absPath, rootDir);
@@ -93,27 +99,79 @@ export function runtimePackFromDepsJson(depsJson, framework) {
93
99
  }
94
100
 
95
101
  /**
96
- * The CPE NVD uses for a shared framework version: `.net` from 5.0 on,
97
- * `.net_core` before it, and `asp.net_core` for ASP.NET Core.
102
+ * The .NET majors whose previews NVD names `preview.<N>`. NVD names the
103
+ * previews of every other .NET major, of ASP.NET Core and of .NET Core
104
+ * `preview<N>`.
105
+ */
106
+ const DOTTED_PREVIEW_MAJORS = new Set([7, 8, 10]);
107
+
108
+ /**
109
+ * Read the pre-release tag of a runtime version.
110
+ *
111
+ * @param {string} version runtime version
112
+ * @returns {{label: string, number: string}|null|undefined} the `rc` or
113
+ * `preview` tag and its number; undefined for a release; null for a
114
+ * pre-release of any other kind
115
+ */
116
+ function dotnetPrerelease(version) {
117
+ const dashAt = version.indexOf("-");
118
+ if (dashAt < 0) {
119
+ return undefined;
120
+ }
121
+ const tag = PRERELEASE_LABEL_RE.exec(version.slice(dashAt + 1));
122
+ return tag ? { label: tag[1], number: tag[2] } : null;
123
+ }
124
+
125
+ /**
126
+ * The CPE NVD uses for a shared framework version, with the version and update
127
+ * fields spelled the way the NVD CPE dictionary spells them.
128
+ *
129
+ * The product is `.net` from 5.0 on, `.net_core` before it, and `asp.net_core`
130
+ * for ASP.NET Core. NVD names a .NET Core x.y.0 release and its pre-releases
131
+ * with two version parts (`.net_core:3.1`, `.net_core:3.0:preview9`), and the
132
+ * .NET 5.0 pre-releases the same way (`.net:5.0:rc1`); every other version
133
+ * keeps three. A release candidate is `rc<N>`, and a preview is
134
+ * `preview.<N>` for .NET 7, 8 and 10 and `preview<N>` otherwise. A release
135
+ * keeps the `*` update, which is the only one NVD lists for some releases. A
136
+ * pre-release of any other kind names no version NVD has, so it gets no CPE.
98
137
  *
99
138
  * @param {string} framework framework directory name
100
139
  * @param {string} version runtime version
101
140
  * @returns {string|undefined} CPE 2.3 string
102
141
  */
103
142
  export function dotnetCpe(framework, version) {
104
- const base = /^(\d+)\.\d+\.\d+/.exec(version || "");
143
+ const base = /^(\d+)\.(\d+)\.(\d+)/.exec(version || "");
105
144
  if (!base) {
106
145
  return undefined;
107
146
  }
108
- const plain = base[0];
147
+ const major = Number(base[1]);
148
+ let product;
109
149
  if (framework === "Microsoft.AspNetCore.App") {
110
- return `cpe:2.3:a:microsoft:asp.net_core:${plain}:*:*:*:*:*:*:*`;
150
+ product = "asp.net_core";
151
+ } else if (framework === "Microsoft.NETCore.App") {
152
+ product = major >= 5 ? ".net" : ".net_core";
153
+ } else {
154
+ return undefined;
111
155
  }
112
- if (framework === "Microsoft.NETCore.App") {
113
- const product = Number(base[1]) >= 5 ? ".net" : ".net_core";
114
- return `cpe:2.3:a:microsoft:${product}:${plain}:*:*:*:*:*:*:*`;
156
+ const prerelease = dotnetPrerelease(version);
157
+ if (prerelease === null) {
158
+ return undefined;
115
159
  }
116
- return undefined;
160
+ let update = "*";
161
+ if (prerelease?.label === "rc") {
162
+ update = `rc${prerelease.number}`;
163
+ } else if (prerelease) {
164
+ update =
165
+ product === ".net" && DOTTED_PREVIEW_MAJORS.has(major)
166
+ ? `preview.${prerelease.number}`
167
+ : `preview${prerelease.number}`;
168
+ }
169
+ const twoPart =
170
+ base[3] === "0" &&
171
+ (product === ".net_core" ||
172
+ (product === ".net" && major === 5 && prerelease !== undefined));
173
+ const cpeVersion = twoPart ? `${base[1]}.${base[2]}` : base[0];
174
+ return `cpe:2.3:a:microsoft:${product}:${cpeVersion}:${update}:*:*:*:*:*:*`;
117
175
  }
118
176
 
119
177
  /**
@@ -121,7 +179,9 @@ export function dotnetCpe(framework, version) {
121
179
  *
122
180
  * @param {string} framework framework directory name
123
181
  * @param {{pack: string, version: string, rid: string}} runtime runtime pack
124
- * @param {string} location root-relative path of the deps.json
182
+ * @param {string} [location] root-relative path of the deps.json. A runtime
183
+ * found on the scanning host gets none, since host paths can name the user's
184
+ * home directory.
125
185
  * @returns {Object|undefined} component, or undefined when no purl can be built
126
186
  */
127
187
  export function createDotnetRuntimeComponent(framework, runtime, location) {
@@ -133,10 +193,12 @@ export function createDotnetRuntimeComponent(framework, runtime, location) {
133
193
  { name: "cdx:dotnet:sharedFramework", value: framework },
134
194
  { name: "cdx:dotnet:runtimeIdentifier", value: runtime.rid },
135
195
  ],
136
- evidence: {
137
- occurrences: [{ location: `/${location.replace(/^\/+/, "")}` }],
138
- },
139
196
  };
197
+ if (location) {
198
+ component.evidence = {
199
+ occurrences: [{ location: `/${location.replace(/^\/+/, "")}` }],
200
+ };
201
+ }
140
202
  const cpe = dotnetCpe(framework, runtime.version);
141
203
  if (cpe) {
142
204
  component.cpe = cpe;
@@ -152,6 +214,94 @@ export function createDotnetRuntimeComponent(framework, runtime, location) {
152
214
  return applyPurl(component, purl);
153
215
  }
154
216
 
217
+ /**
218
+ * Parse the output of `dotnet --list-runtimes`, one `<framework> <version>
219
+ * [<directory>]` line per installed runtime. Warnings, update notices, blank
220
+ * lines, unknown frameworks and versions of a shape no runtime pack names are
221
+ * left out, and no more than MAX_FRAMEWORKS runtimes are kept.
222
+ *
223
+ * https://learn.microsoft.com/dotnet/core/install/how-to-detect-installed-versions
224
+ *
225
+ * @param {string} output output of `dotnet --list-runtimes`
226
+ * @returns {{framework: string, version: string, frameworkDir: string}[]} one
227
+ * entry per installed runtime
228
+ */
229
+ export function parseDotnetListRuntimes(output) {
230
+ const entries = [];
231
+ if (!output) {
232
+ return entries;
233
+ }
234
+ for (const rawLine of `${output}`.split(/\r?\n/)) {
235
+ if (entries.length >= MAX_FRAMEWORKS) {
236
+ break;
237
+ }
238
+ const line = rawLine.trim();
239
+ // The directory is the text between ` [` and the trailing `]`, and can
240
+ // contain spaces, as in `C:\Program Files\dotnet\shared\Microsoft.NETCore.App`.
241
+ const bracketAt = line.indexOf(" [");
242
+ if (bracketAt <= 0 || !line.endsWith("]")) {
243
+ continue;
244
+ }
245
+ const head = line.slice(0, bracketAt);
246
+ const spaceAt = head.indexOf(" ");
247
+ if (spaceAt <= 0) {
248
+ continue;
249
+ }
250
+ const framework = head.slice(0, spaceAt);
251
+ const version = head.slice(spaceAt + 1);
252
+ if (!FRAMEWORKS.has(framework) || !RUNTIME_VERSION_RE.test(version)) {
253
+ continue;
254
+ }
255
+ const frameworkDir = line.slice(bracketAt + 2, line.length - 1);
256
+ entries.push({ framework, version, frameworkDir });
257
+ }
258
+ return entries;
259
+ }
260
+
261
+ /**
262
+ * Create the platform component for one runtime the host install reports.
263
+ *
264
+ * The identity comes from the deps.json in the runtime's own directory, read
265
+ * with the directory as the root so the read cannot leave it and a symlink is
266
+ * not followed. Only an absolute directory is read, which is what the dotnet
267
+ * host prints. A runtime whose deps.json is missing, unreadable or names
268
+ * another version keeps its name, its version and its CPE, without a purl:
269
+ * without the runtime identifier of the pack there is no correct nuget purl.
270
+ *
271
+ * @param {{framework: string, version: string, frameworkDir: string}} entry
272
+ * one entry of parseDotnetListRuntimes
273
+ * @returns {Object} platform component
274
+ */
275
+ export function createHostDotnetRuntimeComponent(entry) {
276
+ const depsJson = isAbsolute(entry.frameworkDir)
277
+ ? readJsonBounded(
278
+ join(entry.frameworkDir, entry.version, `${entry.framework}.deps.json`),
279
+ entry.frameworkDir,
280
+ )
281
+ : undefined;
282
+ const runtime = runtimePackFromDepsJson(depsJson, entry.framework);
283
+ // The directory a runtime lives in is named after its version.
284
+ if (runtime && runtime.version === entry.version) {
285
+ const component = createDotnetRuntimeComponent(entry.framework, runtime);
286
+ if (component) {
287
+ return component;
288
+ }
289
+ }
290
+ const component = {
291
+ type: "platform",
292
+ name: entry.framework,
293
+ version: entry.version,
294
+ properties: [
295
+ { name: "cdx:dotnet:sharedFramework", value: entry.framework },
296
+ ],
297
+ };
298
+ const cpe = dotnetCpe(entry.framework, entry.version);
299
+ if (cpe) {
300
+ component.cpe = cpe;
301
+ }
302
+ return component;
303
+ }
304
+
155
305
  /**
156
306
  * Find the .NET shared frameworks installed under a root that no package
157
307
  * manager owns, and return one platform component per runtime pack.