@cdxgen/cdxgen 13.0.0 → 13.1.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 (170) hide show
  1. package/README.md +26 -13
  2. package/bin/audit.js +6 -0
  3. package/bin/cdxgen.js +140 -8
  4. package/bin/convert.js +7 -0
  5. package/bin/evinse.js +1 -1
  6. package/bin/hbom.js +13 -4
  7. package/bin/tracebom.js +9 -1
  8. package/data/component-tags.json +8 -1
  9. package/data/frameworks-list.json +86 -1
  10. package/data/remediations.json +1144 -0
  11. package/data/rules/asar-archives.yaml +1 -1
  12. package/data/rules/build-fidelity.yaml +782 -0
  13. package/data/rules/package-integrity.yaml +13 -13
  14. package/lib/audit/index.js +48 -4
  15. package/lib/audit/scoring.js +2 -8
  16. package/lib/cli/bomAssembly.js +83 -15
  17. package/lib/cli/cliOptions.js +124 -7
  18. package/lib/cli/index.js +76 -48
  19. package/lib/cli/jsBom.js +32 -3
  20. package/lib/cli/jvmBom.js +1494 -1315
  21. package/lib/cli/managedBom.js +97 -28
  22. package/lib/cli/nativeBom.js +189 -10
  23. package/lib/core/buildLedger.js +799 -0
  24. package/lib/core/deferredExit.js +141 -0
  25. package/lib/core/env.js +69 -1
  26. package/lib/core/fs.js +60 -0
  27. package/lib/core/logger.js +76 -25
  28. package/lib/core/severity.js +16 -0
  29. package/lib/core/syncFileWriter.js +106 -0
  30. package/lib/core/ui.js +46 -2
  31. package/lib/ecosystems/ecosystems.js +9 -0
  32. package/lib/ecosystems/gradleutils.js +355 -15
  33. package/lib/ecosystems/parsers-js.js +26 -0
  34. package/lib/ecosystems/parsers-jvm.js +9 -0
  35. package/lib/ecosystems/parsers-misc.js +16 -2
  36. package/lib/ecosystems/parsers-python.js +171 -35
  37. package/lib/ecosystems/parsers-rust.js +12 -2
  38. package/lib/ecosystems/pypiClassifiers.js +326 -0
  39. package/lib/ecosystems/pythonutils.js +49 -1
  40. package/lib/ecosystems/rubyutils.js +13 -0
  41. package/lib/ecosystems/utils.js +9 -1
  42. package/lib/evinser/evinser.js +30 -12
  43. package/lib/helpers/remote/dependency-track.js +81 -14
  44. package/lib/inventory/auditCategories.js +9 -0
  45. package/lib/inventory/depsUtils.js +72 -0
  46. package/lib/inventory/dosai.js +431 -22
  47. package/lib/inventory/envcontext.js +211 -19
  48. package/lib/inventory/fetchBatch.js +57 -0
  49. package/lib/inventory/jvmToolEnv.js +593 -0
  50. package/lib/inventory/osinfo.js +123 -16
  51. package/lib/inventory/table.js +9 -5
  52. package/lib/inventory/toolRequirements.js +903 -0
  53. package/lib/managers/binary.js +216 -40
  54. package/lib/managers/docker.js +26 -5
  55. package/lib/server/server.js +1 -1
  56. package/lib/stages/postgen/annotationBuilder.js +97 -0
  57. package/lib/stages/postgen/auditBom.js +76 -79
  58. package/lib/stages/postgen/introspection/annotate.js +320 -0
  59. package/lib/stages/postgen/introspection/commandFacts.js +293 -0
  60. package/lib/stages/postgen/introspection/emit.js +192 -0
  61. package/lib/stages/postgen/introspection/formulationEvidence.js +427 -0
  62. package/lib/stages/postgen/introspection/markers.js +227 -0
  63. package/lib/stages/postgen/introspection/reflect.js +1214 -0
  64. package/lib/stages/postgen/introspection/report.js +1208 -0
  65. package/lib/stages/postgen/introspection/score.js +1067 -0
  66. package/lib/stages/postgen/introspection/shapeCommand.js +304 -0
  67. package/lib/stages/postgen/parentEdge.js +156 -0
  68. package/lib/stages/postgen/postgen.js +115 -2
  69. package/lib/stages/postgen/ruleEngine.js +17 -0
  70. package/lib/stages/pregen/envAudit.js +42 -2
  71. package/lib/stages/pregen/pregen.js +1171 -6
  72. package/lib/validator/bomValidator.js +11 -2
  73. package/package.json +14 -14
  74. package/types/lib/audit/index.d.ts.map +1 -1
  75. package/types/lib/audit/scoring.d.ts +2 -8
  76. package/types/lib/audit/scoring.d.ts.map +1 -1
  77. package/types/lib/cli/bomAssembly.d.ts +14 -0
  78. package/types/lib/cli/bomAssembly.d.ts.map +1 -1
  79. package/types/lib/cli/cliOptions.d.ts +2 -1
  80. package/types/lib/cli/cliOptions.d.ts.map +1 -1
  81. package/types/lib/cli/index.d.ts +8 -0
  82. package/types/lib/cli/index.d.ts.map +1 -1
  83. package/types/lib/cli/jsBom.d.ts.map +1 -1
  84. package/types/lib/cli/jvmBom.d.ts.map +1 -1
  85. package/types/lib/cli/managedBom.d.ts.map +1 -1
  86. package/types/lib/cli/nativeBom.d.ts.map +1 -1
  87. package/types/lib/core/buildLedger.d.ts +272 -0
  88. package/types/lib/core/buildLedger.d.ts.map +1 -0
  89. package/types/lib/core/deferredExit.d.ts +84 -0
  90. package/types/lib/core/deferredExit.d.ts.map +1 -0
  91. package/types/lib/core/env.d.ts +22 -1
  92. package/types/lib/core/env.d.ts.map +1 -1
  93. package/types/lib/core/fs.d.ts +21 -0
  94. package/types/lib/core/fs.d.ts.map +1 -1
  95. package/types/lib/core/logger.d.ts +14 -1
  96. package/types/lib/core/logger.d.ts.map +1 -1
  97. package/types/lib/core/severity.d.ts +11 -0
  98. package/types/lib/core/severity.d.ts.map +1 -0
  99. package/types/lib/core/syncFileWriter.d.ts +33 -0
  100. package/types/lib/core/syncFileWriter.d.ts.map +1 -0
  101. package/types/lib/core/ui.d.ts +1 -1
  102. package/types/lib/core/ui.d.ts.map +1 -1
  103. package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
  104. package/types/lib/ecosystems/gradleutils.d.ts +33 -0
  105. package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
  106. package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
  107. package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
  108. package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
  109. package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
  110. package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
  111. package/types/lib/ecosystems/pypiClassifiers.d.ts +80 -0
  112. package/types/lib/ecosystems/pypiClassifiers.d.ts.map +1 -0
  113. package/types/lib/ecosystems/pythonutils.d.ts +20 -0
  114. package/types/lib/ecosystems/pythonutils.d.ts.map +1 -1
  115. package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
  116. package/types/lib/ecosystems/utils.d.ts +4 -4
  117. package/types/lib/ecosystems/utils.d.ts.map +1 -1
  118. package/types/lib/evinser/evinser.d.ts +5 -1
  119. package/types/lib/evinser/evinser.d.ts.map +1 -1
  120. package/types/lib/helpers/remote/dependency-track.d.ts +45 -4
  121. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
  122. package/types/lib/inventory/auditCategories.d.ts +8 -0
  123. package/types/lib/inventory/auditCategories.d.ts.map +1 -1
  124. package/types/lib/inventory/depsUtils.d.ts.map +1 -1
  125. package/types/lib/inventory/dosai.d.ts +20 -0
  126. package/types/lib/inventory/dosai.d.ts.map +1 -1
  127. package/types/lib/inventory/envcontext.d.ts.map +1 -1
  128. package/types/lib/inventory/fetchBatch.d.ts +9 -0
  129. package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
  130. package/types/lib/inventory/jvmToolEnv.d.ts +189 -0
  131. package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -0
  132. package/types/lib/inventory/osinfo.d.ts +83 -2
  133. package/types/lib/inventory/osinfo.d.ts.map +1 -1
  134. package/types/lib/inventory/table.d.ts +4 -1
  135. package/types/lib/inventory/table.d.ts.map +1 -1
  136. package/types/lib/inventory/toolRequirements.d.ts +216 -0
  137. package/types/lib/inventory/toolRequirements.d.ts.map +1 -0
  138. package/types/lib/managers/binary.d.ts +71 -0
  139. package/types/lib/managers/binary.d.ts.map +1 -1
  140. package/types/lib/managers/docker.d.ts.map +1 -1
  141. package/types/lib/stages/postgen/annotationBuilder.d.ts +53 -0
  142. package/types/lib/stages/postgen/annotationBuilder.d.ts.map +1 -0
  143. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  144. package/types/lib/stages/postgen/introspection/annotate.d.ts +81 -0
  145. package/types/lib/stages/postgen/introspection/annotate.d.ts.map +1 -0
  146. package/types/lib/stages/postgen/introspection/commandFacts.d.ts +32 -0
  147. package/types/lib/stages/postgen/introspection/commandFacts.d.ts.map +1 -0
  148. package/types/lib/stages/postgen/introspection/emit.d.ts +71 -0
  149. package/types/lib/stages/postgen/introspection/emit.d.ts.map +1 -0
  150. package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts +154 -0
  151. package/types/lib/stages/postgen/introspection/formulationEvidence.d.ts.map +1 -0
  152. package/types/lib/stages/postgen/introspection/markers.d.ts +57 -0
  153. package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -0
  154. package/types/lib/stages/postgen/introspection/reflect.d.ts +77 -0
  155. package/types/lib/stages/postgen/introspection/reflect.d.ts.map +1 -0
  156. package/types/lib/stages/postgen/introspection/report.d.ts +85 -0
  157. package/types/lib/stages/postgen/introspection/report.d.ts.map +1 -0
  158. package/types/lib/stages/postgen/introspection/score.d.ts +149 -0
  159. package/types/lib/stages/postgen/introspection/score.d.ts.map +1 -0
  160. package/types/lib/stages/postgen/introspection/shapeCommand.d.ts +79 -0
  161. package/types/lib/stages/postgen/introspection/shapeCommand.d.ts.map +1 -0
  162. package/types/lib/stages/postgen/parentEdge.d.ts +35 -0
  163. package/types/lib/stages/postgen/parentEdge.d.ts.map +1 -0
  164. package/types/lib/stages/postgen/postgen.d.ts +2 -2
  165. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  166. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  167. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  168. package/types/lib/stages/pregen/pregen.d.ts +131 -0
  169. package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
  170. package/types/lib/validator/bomValidator.d.ts.map +1 -1
@@ -86,6 +86,112 @@ export const OS_DISTRO_ALIAS = {
86
86
  "red hat enterprise linux 10": "rhel-10",
87
87
  };
88
88
 
89
+ /**
90
+ * Canonical purl namespace for distro vendors that are published under more
91
+ * than one spelling. Both the os-release path (`getDistroInfo`) and the trivy
92
+ * passthrough in `lib/managers/binary.js` route through this single map so the
93
+ * two paths cannot drift apart again.
94
+ *
95
+ * Azure Linux 2.0 is CBL-Mariner 2.0 — Microsoft renamed the product — and the
96
+ * OSV feed publishes `pkg:rpm/azure-linux/<name>` for every generation
97
+ * (ecosystems "Azure Linux:2" and "Azure Linux:3"), so the historical
98
+ * spellings azurelinux, cbl-mariner and mariner all canonicalise to
99
+ * "azure-linux". rhel/ol/amzn/opensuse-* are the cases previously inlined in
100
+ * getDistroInfo(); alma/rocky-linux were previously inlined in binary.js.
101
+ *
102
+ * Amazon Linux canonicalises to "amazon", the spelling the vulnerability feeds
103
+ * publish (AppThreat vuln-list `amazon`): the os-release ID is `amzn` and
104
+ * trivy emits `amazon`, and the previous `amazonlinux` mapping here matched
105
+ * neither, so live-host Amazon Linux purls matched nothing at all.
106
+ */
107
+ export const OS_NAMESPACE_ALIAS = {
108
+ alma: "almalinux",
109
+ amazonlinux: "amazon",
110
+ amzn: "amazon",
111
+ azurelinux: "azure-linux",
112
+ "cbl-mariner": "azure-linux",
113
+ mariner: "azure-linux",
114
+ ol: "oracle",
115
+ "opensuse-leap": "opensuse",
116
+ "opensuse-tumbleweed": "opensuse",
117
+ rhel: "redhat",
118
+ "rocky-linux": "rocky",
119
+ };
120
+
121
+ /**
122
+ * Vendor prefixes that `distro` qualifier values are canonicalised for, so one
123
+ * release has exactly one qualifier value no matter which side produced it.
124
+ *
125
+ * trivy stamps the qualifier from its own distro table while cdxgen stamps it
126
+ * from os-release, and the two disagree: an Oracle Linux 9.8 image came out
127
+ * with `oracle-9.8` on 99 components and `ol-9.8` on 43, and a RHEL 9.8 image
128
+ * with namespace `redhat` but qualifier `rhel-9.8`. Canonicalising both sides
129
+ * through this map collapses each release onto the vendor its purl namespace
130
+ * already uses.
131
+ *
132
+ * SUSE is deliberately absent. `sles-15.6`, `opensuse-leap-15.6` and
133
+ * `opensuse-tumbleweed-<snapshot>` are the values consumers derive the release
134
+ * channel from (vulnerability-db maps them to per-release channels), and both
135
+ * sides already agree on them; folding Leap and Tumbleweed onto a bare
136
+ * `opensuse-` vendor would destroy exactly the distinction that matters.
137
+ *
138
+ * Keys are matched longest-first so `cbl-mariner-2.0` resolves against the
139
+ * full vendor segment rather than a shorter prefix.
140
+ */
141
+ export const OS_DISTRO_QUALIFIER_ALIAS = {
142
+ alma: "almalinux",
143
+ amazonlinux: "amazon",
144
+ amzn: "amazon",
145
+ azurelinux: "azure-linux",
146
+ "cbl-mariner": "azure-linux",
147
+ mariner: "azure-linux",
148
+ ol: "oracle",
149
+ rhel: "redhat",
150
+ "rocky-linux": "rocky",
151
+ };
152
+
153
+ // A distro qualifier reaches a purl verbatim, so anything outside the purl
154
+ // qualifier charset makes the whole purl — and with it the whole BOM —
155
+ // invalid. trivy emits Amazon Linux as
156
+ // `amazon-2023.12.20260727+(Amazon Linux)`, which failed cdxgen's own schema
157
+ // validation and cost the entire SBOM.
158
+ const SAFE_DISTRO_QUALIFIER = /^[A-Za-z0-9._-]+$/;
159
+
160
+ /**
161
+ * Whether a `distro` qualifier value is safe to place in a purl unencoded.
162
+ *
163
+ * @param {string} qualifier - distro qualifier value
164
+ * @returns {boolean} true when the value contains only purl-safe characters
165
+ */
166
+ export function isCleanDistroQualifier(qualifier) {
167
+ return !!qualifier && SAFE_DISTRO_QUALIFIER.test(qualifier);
168
+ }
169
+
170
+ const OS_DISTRO_QUALIFIER_ALIAS_KEYS = Object.keys(
171
+ OS_DISTRO_QUALIFIER_ALIAS,
172
+ ).sort((a, b) => b.length - a.length);
173
+
174
+ /**
175
+ * Canonicalise the vendor prefix of a `distro` qualifier value
176
+ * (e.g. "cbl-mariner-2.0" or "mariner-2.0" -> "azure-linux-2.0") so a release
177
+ * has exactly one qualifier spelling regardless of which tool emitted it.
178
+ * Values without a known vendor prefix are returned unchanged.
179
+ *
180
+ * @param {string} qualifier - distro qualifier value such as "azurelinux-3.0"
181
+ * @returns {string} canonical qualifier value
182
+ */
183
+ export function canonicalDistroQualifier(qualifier) {
184
+ if (!qualifier) {
185
+ return qualifier;
186
+ }
187
+ for (const key of OS_DISTRO_QUALIFIER_ALIAS_KEYS) {
188
+ if (qualifier.startsWith(`${key}-`)) {
189
+ return `${OS_DISTRO_QUALIFIER_ALIAS[key]}${qualifier.slice(key.length)}`;
190
+ }
191
+ }
192
+ return qualifier;
193
+ }
194
+
89
195
  // ---------------------------------------------------------------------------
90
196
  // Raw os-release cache — keyed by the osRelease file path so the live-host
91
197
  // cache (/etc/os-release) and any container rootfs path are kept separate.
@@ -151,8 +257,10 @@ export function _resetOsReleaseCache() {
151
257
  *
152
258
  * Returns an object with:
153
259
  * - purlType {string} "deb" | "apk" | "rpm"
154
- * - namespace {string} purl namespace (e.g. "ubuntu", "alpine", "fedora")
155
- * - distroId {string} ID + "-" + VERSION_ID (e.g. "ubuntu-22.04")
260
+ * - namespace {string} canonical purl namespace (e.g. "ubuntu", "alpine",
261
+ * "azure-linux" for both Azure Linux and CBL-Mariner)
262
+ * - distroId {string} ID + "-" + VERSION_ID with a canonical vendor
263
+ * prefix (e.g. "ubuntu-22.04", "azure-linux-3.0")
156
264
  * - distroName {string} codename/alias (e.g. "jammy")
157
265
  *
158
266
  * Mirrors the logic in lib/managers/binary.js getOSPackages() so that both
@@ -180,6 +288,7 @@ export function getDistroInfo(root = "/") {
180
288
  purlType = "deb";
181
289
  break;
182
290
  case "alpine":
291
+ case "alpaquita":
183
292
  case "openwrt":
184
293
  purlType = "apk";
185
294
  break;
@@ -193,6 +302,9 @@ export function getDistroInfo(root = "/") {
193
302
  case "centos":
194
303
  case "rocky":
195
304
  case "almalinux":
305
+ case "azurelinux":
306
+ case "cbl-mariner":
307
+ case "mariner":
196
308
  case "oracle":
197
309
  case "ol":
198
310
  case "amzn":
@@ -216,19 +328,10 @@ export function getDistroInfo(root = "/") {
216
328
  break;
217
329
  }
218
330
 
219
- // Determine the purl namespace (vendor)
220
- let namespace = rawId;
221
- if (rawId === "rhel") {
222
- namespace = "redhat";
223
- } else if (rawId === "ol") {
224
- namespace = "oracle";
225
- } else if (rawId === "amzn") {
226
- namespace = "amazonlinux";
227
- } else if (rawId === "opensuse-leap" || rawId === "opensuse-tumbleweed") {
228
- namespace = "opensuse";
229
- } else if (rawId === "almalinux") {
230
- namespace = "almalinux";
231
- } else if (!rawId) {
331
+ // Determine the purl namespace (vendor), canonicalising vendors that are
332
+ // published under several spellings (e.g. mariner -> azure-linux)
333
+ let namespace = OS_NAMESPACE_ALIAS[rawId] || rawId;
334
+ if (!rawId) {
232
335
  // Fallback via ID_LIKE
233
336
  if (idLike.includes("rhel") || idLike.includes("centos")) {
234
337
  namespace = "redhat";
@@ -245,8 +348,12 @@ export function getDistroInfo(root = "/") {
245
348
  }
246
349
  }
247
350
 
248
- // Build distroId = ID-VERSION_ID (e.g. "fedora-25", "alpine-3.17")
351
+ // Build distroId = ID-VERSION_ID (e.g. "fedora-25", "alpine-3.17"),
352
+ // canonicalising the vendor prefix so one release has a single qualifier
353
+ // value (e.g. "mariner-2.0" and "azurelinux-3.0" -> "azure-linux-2.0"/
354
+ // "azure-linux-3.0")
249
355
  let distroId = versionId ? `${rawId}-${versionId}` : rawId;
356
+ distroId = canonicalDistroQualifier(distroId);
250
357
 
251
358
  // Special-case Alpine: truncate to major.minor
252
359
  if (purlType === "apk" && versionId) {
@@ -2,6 +2,7 @@ import process from "node:process";
2
2
 
3
3
  import { readEnvironmentVariable } from "../core/activity.js";
4
4
  import { TABLE_BORDER_STYLE } from "../core/env.js";
5
+ import { diagnosticStream } from "../core/logger.js";
5
6
  import {
6
7
  alignText,
7
8
  stringWidth,
@@ -218,7 +219,10 @@ export function table(rows, config = {}) {
218
219
  }
219
220
 
220
221
  /**
221
- * Create a streaming table writer that renders rows to stdout incrementally.
222
+ * Create a streaming table writer that renders rows incrementally.
223
+ *
224
+ * Rows go to the diagnostic stream: a rendered table is human-readable output,
225
+ * and writing it to stdout corrupted the BOM document under `-o -`.
222
226
  *
223
227
  * @param {Object} [config={}] Table configuration (header, columns, border style)
224
228
  * @returns {{write(row: *): void, end(): void}} Writer whose `end()` emits the bottom border
@@ -242,21 +246,21 @@ export function createStream(config = {}) {
242
246
  const topBorder = drawBorder(columns, borderChars, "top");
243
247
  middleBorder = drawBorder(columns, borderChars, "mid");
244
248
  bottomBorder = drawBorder(columns, borderChars, "bottom");
245
- process.stdout.write(`${topBorder}\n`);
249
+ diagnosticStream.write(`${topBorder}\n`);
246
250
  }
247
251
  if (hasRows) {
248
- process.stdout.write(`${middleBorder}\n`);
252
+ diagnosticStream.write(`${middleBorder}\n`);
249
253
  }
250
254
  const safeRow = Array.isArray(row) ? row : [row];
251
255
  const rendered = renderRow(safeRow, columns, borderChars);
252
- process.stdout.write(`${rendered.join("\n")}\n`);
256
+ diagnosticStream.write(`${rendered.join("\n")}\n`);
253
257
  hasRows = true;
254
258
  },
255
259
  end() {
256
260
  if (!columns || closed) {
257
261
  return;
258
262
  }
259
- process.stdout.write(`${bottomBorder}\n`);
263
+ diagnosticStream.write(`${bottomBorder}\n`);
260
264
  closed = true;
261
265
  },
262
266
  };