@cyclonedx/cdxgen 12.3.3 → 12.4.1

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 (175) hide show
  1. package/README.md +69 -25
  2. package/bin/audit.js +21 -7
  3. package/bin/cdxgen.js +270 -127
  4. package/bin/convert.js +34 -15
  5. package/bin/hbom.js +495 -0
  6. package/bin/repl.js +592 -37
  7. package/bin/validate.js +31 -4
  8. package/bin/verify.js +18 -5
  9. package/data/README.md +298 -25
  10. package/data/component-tags.json +6 -0
  11. package/data/crypto-oid.json +16 -0
  12. package/data/cyclonedx-2.0-bundled.schema.json +7182 -0
  13. package/data/predictive-audit-allowlist.json +11 -0
  14. package/data/queries-darwin.json +12 -1
  15. package/data/queries-win.json +7 -1
  16. package/data/queries.json +39 -2
  17. package/data/rules/ai-agent-governance.yaml +16 -0
  18. package/data/rules/asar-archives.yaml +150 -0
  19. package/data/rules/chrome-extensions.yaml +8 -0
  20. package/data/rules/ci-permissions.yaml +42 -18
  21. package/data/rules/container-risk.yaml +14 -7
  22. package/data/rules/dependency-sources.yaml +11 -0
  23. package/data/rules/hbom-compliance.yaml +325 -0
  24. package/data/rules/hbom-performance.yaml +307 -0
  25. package/data/rules/hbom-security.yaml +248 -0
  26. package/data/rules/host-topology.yaml +165 -0
  27. package/data/rules/mcp-servers.yaml +18 -3
  28. package/data/rules/obom-runtime.yaml +907 -22
  29. package/data/rules/package-integrity.yaml +14 -0
  30. package/data/rules/rootfs-hardening.yaml +179 -0
  31. package/data/rules/vscode-extensions.yaml +9 -0
  32. package/lib/audit/index.js +210 -8
  33. package/lib/audit/index.poku.js +332 -0
  34. package/lib/audit/reporters.js +222 -0
  35. package/lib/audit/targets.js +146 -1
  36. package/lib/audit/targets.poku.js +186 -0
  37. package/lib/cli/asar.poku.js +328 -0
  38. package/lib/cli/index.js +527 -99
  39. package/lib/cli/index.poku.js +1469 -212
  40. package/lib/evinser/evinser.js +14 -9
  41. package/lib/helpers/analyzer.js +1406 -29
  42. package/lib/helpers/analyzer.poku.js +342 -0
  43. package/lib/helpers/analyzerScope.js +712 -0
  44. package/lib/helpers/asarutils.js +1556 -0
  45. package/lib/helpers/asarutils.poku.js +443 -0
  46. package/lib/helpers/auditCategories.js +12 -0
  47. package/lib/helpers/auditCategories.poku.js +32 -0
  48. package/lib/helpers/bomUtils.js +155 -1
  49. package/lib/helpers/bomUtils.poku.js +79 -1
  50. package/lib/helpers/cbomutils.js +271 -1
  51. package/lib/helpers/cbomutils.poku.js +248 -5
  52. package/lib/helpers/display.js +291 -1
  53. package/lib/helpers/display.poku.js +149 -0
  54. package/lib/helpers/evidenceUtils.js +58 -0
  55. package/lib/helpers/evidenceUtils.poku.js +54 -0
  56. package/lib/helpers/exportUtils.js +9 -0
  57. package/lib/helpers/gtfobins.js +142 -8
  58. package/lib/helpers/gtfobins.poku.js +24 -1
  59. package/lib/helpers/hbom.js +710 -0
  60. package/lib/helpers/hbom.poku.js +496 -0
  61. package/lib/helpers/hbomAnalysis.js +268 -0
  62. package/lib/helpers/hbomAnalysis.poku.js +249 -0
  63. package/lib/helpers/hbomLoader.js +35 -0
  64. package/lib/helpers/hostTopology.js +803 -0
  65. package/lib/helpers/hostTopology.poku.js +363 -0
  66. package/lib/helpers/inventoryStats.js +69 -0
  67. package/lib/helpers/inventoryStats.poku.js +86 -0
  68. package/lib/helpers/lolbas.js +19 -1
  69. package/lib/helpers/lolbas.poku.js +23 -0
  70. package/lib/helpers/osqueryTransform.js +47 -0
  71. package/lib/helpers/osqueryTransform.poku.js +47 -0
  72. package/lib/helpers/plugins.js +350 -0
  73. package/lib/helpers/plugins.poku.js +57 -0
  74. package/lib/helpers/protobom.js +209 -45
  75. package/lib/helpers/protobom.poku.js +183 -5
  76. package/lib/helpers/protobomLoader.js +43 -0
  77. package/lib/helpers/protobomLoader.poku.js +31 -0
  78. package/lib/helpers/remote/dependency-track.js +36 -3
  79. package/lib/helpers/remote/dependency-track.poku.js +44 -0
  80. package/lib/helpers/source.js +24 -0
  81. package/lib/helpers/source.poku.js +32 -0
  82. package/lib/helpers/utils.js +1438 -93
  83. package/lib/helpers/utils.poku.js +846 -4
  84. package/lib/managers/binary.e2e.poku.js +367 -0
  85. package/lib/managers/binary.js +2293 -353
  86. package/lib/managers/binary.poku.js +1699 -1
  87. package/lib/managers/docker.js +201 -79
  88. package/lib/managers/docker.poku.js +337 -12
  89. package/lib/server/server.js +4 -28
  90. package/lib/stages/postgen/annotator.js +38 -0
  91. package/lib/stages/postgen/annotator.poku.js +107 -1
  92. package/lib/stages/postgen/auditBom.js +121 -18
  93. package/lib/stages/postgen/auditBom.poku.js +1366 -31
  94. package/lib/stages/postgen/hostTopologyAudit.poku.js +186 -0
  95. package/lib/stages/postgen/postgen.js +406 -8
  96. package/lib/stages/postgen/postgen.poku.js +484 -0
  97. package/lib/stages/postgen/ruleEngine.js +116 -0
  98. package/lib/stages/pregen/envAudit.js +14 -3
  99. package/lib/validator/bomValidator.js +90 -38
  100. package/lib/validator/bomValidator.poku.js +90 -0
  101. package/lib/validator/complianceRules.js +4 -2
  102. package/lib/validator/index.poku.js +14 -0
  103. package/package.json +23 -21
  104. package/types/bin/hbom.d.ts +3 -0
  105. package/types/bin/hbom.d.ts.map +1 -0
  106. package/types/bin/repl.d.ts +1 -1
  107. package/types/bin/repl.d.ts.map +1 -1
  108. package/types/lib/audit/index.d.ts +44 -0
  109. package/types/lib/audit/index.d.ts.map +1 -1
  110. package/types/lib/audit/reporters.d.ts +16 -0
  111. package/types/lib/audit/reporters.d.ts.map +1 -1
  112. package/types/lib/audit/targets.d.ts.map +1 -1
  113. package/types/lib/cli/index.d.ts +16 -0
  114. package/types/lib/cli/index.d.ts.map +1 -1
  115. package/types/lib/evinser/evinser.d.ts +4 -0
  116. package/types/lib/evinser/evinser.d.ts.map +1 -1
  117. package/types/lib/helpers/analyzer.d.ts +33 -0
  118. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  119. package/types/lib/helpers/analyzerScope.d.ts +11 -0
  120. package/types/lib/helpers/analyzerScope.d.ts.map +1 -0
  121. package/types/lib/helpers/asarutils.d.ts +34 -0
  122. package/types/lib/helpers/asarutils.d.ts.map +1 -0
  123. package/types/lib/helpers/auditCategories.d.ts +5 -0
  124. package/types/lib/helpers/auditCategories.d.ts.map +1 -1
  125. package/types/lib/helpers/bomUtils.d.ts +10 -0
  126. package/types/lib/helpers/bomUtils.d.ts.map +1 -1
  127. package/types/lib/helpers/cbomutils.d.ts +3 -2
  128. package/types/lib/helpers/cbomutils.d.ts.map +1 -1
  129. package/types/lib/helpers/display.d.ts.map +1 -1
  130. package/types/lib/helpers/evidenceUtils.d.ts +8 -0
  131. package/types/lib/helpers/evidenceUtils.d.ts.map +1 -0
  132. package/types/lib/helpers/exportUtils.d.ts.map +1 -1
  133. package/types/lib/helpers/gtfobins.d.ts +8 -0
  134. package/types/lib/helpers/gtfobins.d.ts.map +1 -1
  135. package/types/lib/helpers/hbom.d.ts +49 -0
  136. package/types/lib/helpers/hbom.d.ts.map +1 -0
  137. package/types/lib/helpers/hbomAnalysis.d.ts +76 -0
  138. package/types/lib/helpers/hbomAnalysis.d.ts.map +1 -0
  139. package/types/lib/helpers/hbomLoader.d.ts +7 -0
  140. package/types/lib/helpers/hbomLoader.d.ts.map +1 -0
  141. package/types/lib/helpers/hostTopology.d.ts +12 -0
  142. package/types/lib/helpers/hostTopology.d.ts.map +1 -0
  143. package/types/lib/helpers/inventoryStats.d.ts +11 -0
  144. package/types/lib/helpers/inventoryStats.d.ts.map +1 -0
  145. package/types/lib/helpers/lolbas.d.ts.map +1 -1
  146. package/types/lib/helpers/osqueryTransform.d.ts +3 -0
  147. package/types/lib/helpers/osqueryTransform.d.ts.map +1 -1
  148. package/types/lib/helpers/plugins.d.ts +58 -0
  149. package/types/lib/helpers/plugins.d.ts.map +1 -0
  150. package/types/lib/helpers/protobom.d.ts +5 -4
  151. package/types/lib/helpers/protobom.d.ts.map +1 -1
  152. package/types/lib/helpers/protobomLoader.d.ts +17 -0
  153. package/types/lib/helpers/protobomLoader.d.ts.map +1 -0
  154. package/types/lib/helpers/remote/dependency-track.d.ts +10 -3
  155. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
  156. package/types/lib/helpers/source.d.ts.map +1 -1
  157. package/types/lib/helpers/utils.d.ts +45 -8
  158. package/types/lib/helpers/utils.d.ts.map +1 -1
  159. package/types/lib/managers/binary.d.ts +5 -0
  160. package/types/lib/managers/binary.d.ts.map +1 -1
  161. package/types/lib/managers/docker.d.ts.map +1 -1
  162. package/types/lib/server/server.d.ts +2 -1
  163. package/types/lib/server/server.d.ts.map +1 -1
  164. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  165. package/types/lib/stages/postgen/auditBom.d.ts +26 -1
  166. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  167. package/types/lib/stages/postgen/postgen.d.ts +2 -1
  168. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  169. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  170. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  171. package/types/lib/third-party/arborist/lib/node.d.ts +23 -0
  172. package/types/lib/third-party/arborist/lib/node.d.ts.map +1 -1
  173. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  174. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  175. package/data/spdx-model-v3.0.1.jsonld +0 -15999
package/bin/repl.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from "node:fs";
3
+ import { readFileSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import process from "node:process";
@@ -21,14 +21,35 @@ import {
21
21
  printTable,
22
22
  printVulnerabilities,
23
23
  } from "../lib/helpers/display.js";
24
- import { readBinary } from "../lib/helpers/protobom.js";
24
+ import {
25
+ formatHbomHardwareClassSummary,
26
+ getHbomSummary,
27
+ isHbomLikeBom,
28
+ } from "../lib/helpers/hbomAnalysis.js";
29
+ import {
30
+ getPropertyValue,
31
+ getSourceDerivedCryptoComponents,
32
+ getUnpackagedExecutableComponents,
33
+ getUnpackagedSharedLibraryComponents,
34
+ } from "../lib/helpers/inventoryStats.js";
35
+ import {
36
+ importProtobomModule,
37
+ isProtoBomPath,
38
+ } from "../lib/helpers/protobomLoader.js";
25
39
  import {
26
40
  getProvenanceComponents,
27
41
  getTrustedComponents,
28
42
  } from "../lib/helpers/provenanceUtils.js";
29
43
  import { toCycloneDxLikeBom } from "../lib/helpers/spdxUtils.js";
30
44
  import { table } from "../lib/helpers/table.js";
31
- import { getTmpDir } from "../lib/helpers/utils.js";
45
+ import {
46
+ getTmpDir,
47
+ isDryRun,
48
+ safeExistsSync,
49
+ safeMkdirSync,
50
+ safeMkdtempSync,
51
+ safeWriteSync,
52
+ } from "../lib/helpers/utils.js";
32
53
  import { getBomWithOras } from "../lib/managers/oci.js";
33
54
  import { validateBom } from "../lib/validator/bomValidator.js";
34
55
 
@@ -55,7 +76,7 @@ const cdxArt = `
55
76
 
56
77
  console.log(cdxArt);
57
78
 
58
- if (process.env?.CDXGEN_NODE_OPTIONS) {
79
+ if (process.env.CDXGEN_NODE_OPTIONS) {
59
80
  process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS || ""} ${process.env.CDXGEN_NODE_OPTIONS}`;
60
81
  }
61
82
 
@@ -63,6 +84,23 @@ if (process.env?.CDXGEN_NODE_OPTIONS) {
63
84
  let sbom;
64
85
  const getInteractiveBom = () => toCycloneDxLikeBom(sbom);
65
86
 
87
+ function getContainerRegistryHost(reference) {
88
+ const trimmedReference = `${reference || ""}`.trim().toLowerCase();
89
+ if (!trimmedReference) {
90
+ return undefined;
91
+ }
92
+ const slashIndex = trimmedReference.indexOf("/");
93
+ if (slashIndex <= 0) {
94
+ return undefined;
95
+ }
96
+ return trimmedReference.slice(0, slashIndex).replace(/:\d+$/, "");
97
+ }
98
+
99
+ function isSupportedSbomRegistryReference(reference) {
100
+ const registryHost = getContainerRegistryHost(reference);
101
+ return registryHost === "ghcr.io" || registryHost === "docker.io";
102
+ }
103
+
66
104
  function unescapeAnnotationText(value) {
67
105
  return String(value || "")
68
106
  .replace(/<br>/g, "\n")
@@ -143,11 +181,15 @@ function printAuditTable(title, rows) {
143
181
  );
144
182
  }
145
183
 
146
- function getPropertyValue(propertiesOrObject, propertyName) {
147
- const properties = Array.isArray(propertiesOrObject)
148
- ? propertiesOrObject
149
- : propertiesOrObject?.properties;
150
- return properties?.find((property) => property.name === propertyName)?.value;
184
+ function printKeyValueTable(title, entries) {
185
+ const rows = [["Field", "Value"]];
186
+ entries.forEach(([field, value]) => {
187
+ if (value === undefined || value === null || value === "") {
188
+ return;
189
+ }
190
+ rows.push([field, `${value}`]);
191
+ });
192
+ printAuditTable(title, rows);
151
193
  }
152
194
 
153
195
  function isLikelyObom(bom) {
@@ -158,6 +200,10 @@ function isLikelyObom(bom) {
158
200
  );
159
201
  }
160
202
 
203
+ function isLikelyHbom(bom) {
204
+ return isHbomLikeBom(bom);
205
+ }
206
+
161
207
  function isLikelyCargoBom(bom) {
162
208
  const formulation = Array.isArray(bom?.formulation)
163
209
  ? bom.formulation
@@ -221,11 +267,94 @@ function getCargoFormulationEntries(bom) {
221
267
  return matchingEntries;
222
268
  }
223
269
 
270
+ const HBOM_FIRMWARE_PROPERTIES = Object.freeze([
271
+ "cdx:hbom:protocol",
272
+ "cdx:hbom:flags",
273
+ "cdx:hbom:guids",
274
+ "cdx:hbom:instanceIds",
275
+ "cdx:hbom:createdEpoch",
276
+ "cdx:hbom:firmwareDate",
277
+ ]);
278
+
279
+ const HBOM_BUS_SECURITY_PROPERTIES = Object.freeze([
280
+ "cdx:hbom:securityLevel",
281
+ "cdx:hbom:iommuProtection",
282
+ "cdx:hbom:policy",
283
+ "cdx:hbom:authorized",
284
+ "cdx:hbom:usbVersion",
285
+ "cdx:hbom:usbClassName",
286
+ "cdx:hbom:usbInterfaceClasses",
287
+ "cdx:hbom:pciClass",
288
+ "cdx:hbom:pciClassCode",
289
+ "cdx:hbom:displayConnectorType",
290
+ "cdx:hbom:contentProtection",
291
+ "cdx:hbom:drmNode",
292
+ ]);
293
+
294
+ const HBOM_POWER_PROPERTIES = Object.freeze([
295
+ "cdx:hbom:designCapacityPercent",
296
+ "cdx:hbom:energyNow",
297
+ "cdx:hbom:energyFull",
298
+ "cdx:hbom:energyFullDesign",
299
+ "cdx:hbom:chargeNow",
300
+ "cdx:hbom:chargeFull",
301
+ "cdx:hbom:chargeFullDesign",
302
+ "cdx:hbom:powerNow",
303
+ "cdx:hbom:voltageNow",
304
+ "cdx:hbom:currentNow",
305
+ "cdx:hbom:warningLevel",
306
+ ]);
307
+
308
+ function getInteractiveHbomOrWarn(replContext) {
309
+ const interactiveBom = getInteractiveBom();
310
+ if (!interactiveBom) {
311
+ console.log(
312
+ "⚠ No BOM is loaded. Use .import command to import an existing BOM",
313
+ );
314
+ replContext.displayPrompt();
315
+ return undefined;
316
+ }
317
+ if (!isLikelyHbom(interactiveBom)) {
318
+ console.log(
319
+ "This BOM does not look like an HBOM. Import an HBOM generated with 'cdxgen -t hbom' to use this view.",
320
+ );
321
+ replContext.displayPrompt();
322
+ return undefined;
323
+ }
324
+ return interactiveBom;
325
+ }
326
+
327
+ function filterHbomComponentsByProperties(
328
+ bom,
329
+ propertyNames,
330
+ hardwareClasses = [],
331
+ ) {
332
+ return (bom?.components || []).filter((component) => {
333
+ const hardwareClass = getPropertyValue(component, "cdx:hbom:hardwareClass");
334
+ if (hardwareClasses.includes(hardwareClass)) {
335
+ return true;
336
+ }
337
+ return propertyNames.some((propertyName) =>
338
+ Boolean(getPropertyValue(component, propertyName)),
339
+ );
340
+ });
341
+ }
342
+
343
+ function getDiagnosticDisplayDetail(diagnostic) {
344
+ return (
345
+ diagnostic.installHint ||
346
+ diagnostic.privilegeHint ||
347
+ diagnostic.message ||
348
+ diagnostic.code ||
349
+ "-"
350
+ );
351
+ }
352
+
224
353
  let historyFile;
225
354
  const historyConfigDir = join(homedir(), ".config", ".cdxgen");
226
- if (!process.env.CDXGEN_REPL_HISTORY && !fs.existsSync(historyConfigDir)) {
355
+ if (!process.env.CDXGEN_REPL_HISTORY && !safeExistsSync(historyConfigDir)) {
227
356
  try {
228
- fs.mkdirSync(historyConfigDir, { recursive: true });
357
+ safeMkdirSync(historyConfigDir, { recursive: true });
229
358
  historyFile = join(historyConfigDir, ".repl_history");
230
359
  } catch (_e) {
231
360
  // ignore
@@ -234,10 +363,15 @@ if (!process.env.CDXGEN_REPL_HISTORY && !fs.existsSync(historyConfigDir)) {
234
363
  historyFile = join(historyConfigDir, ".repl_history");
235
364
  }
236
365
 
237
- export const importSbom = (sbomOrPath) => {
238
- if (sbomOrPath?.endsWith(".json") && fs.existsSync(sbomOrPath)) {
366
+ export const importSbom = async (sbomOrPath) => {
367
+ const importTarget = String(sbomOrPath || "").trim();
368
+ if (!importTarget) {
369
+ console.log("⚠ An SBOM path or image reference is required.");
370
+ return;
371
+ }
372
+ if (importTarget.endsWith(".json") && safeExistsSync(importTarget)) {
239
373
  try {
240
- sbom = JSON.parse(fs.readFileSync(sbomOrPath, "utf-8"));
374
+ sbom = JSON.parse(readFileSync(importTarget, "utf-8"));
241
375
  let bomType = "SBOM";
242
376
  if (isSpdxJsonLd(sbom)) {
243
377
  bomType = "SPDX";
@@ -245,8 +379,13 @@ export const importSbom = (sbomOrPath) => {
245
379
  if (sbom?.vulnerabilities && Array.isArray(sbom.vulnerabilities)) {
246
380
  bomType = "VDR";
247
381
  }
248
- console.log(`✅ ${bomType} imported successfully from ${sbomOrPath}`);
382
+ console.log(`✅ ${bomType} imported successfully from ${importTarget}`);
249
383
  printSummary(sbom);
384
+ if (isLikelyHbom(sbom)) {
385
+ console.log(
386
+ "💭 HBOM detected. Try .hbomsummary, .hbomevidence, .hbomdiagnostics, or .hbomtips",
387
+ );
388
+ }
250
389
  if (isLikelyObom(sbom)) {
251
390
  console.log(
252
391
  "💭 OBOM detected. Try .osinfocategories, .obomtips, .processes, or .services_snapshot",
@@ -263,56 +402,66 @@ export const importSbom = (sbomOrPath) => {
263
402
  );
264
403
  }
265
404
  } catch (e) {
266
- console.log(`⚠ Unable to import the BOM from ${sbomOrPath} due to ${e}`);
405
+ console.log(
406
+ `⚠ Unable to import the BOM from ${importTarget} due to ${e}`,
407
+ );
267
408
  }
268
- } else if (
269
- (sbomOrPath?.endsWith(".cdx") || sbomOrPath?.endsWith(".proto")) &&
270
- fs.existsSync(sbomOrPath)
271
- ) {
272
- sbom = readBinary(sbomOrPath, true);
409
+ } else if (isProtoBomPath(importTarget) && safeExistsSync(importTarget)) {
410
+ const { readBinary } = await importProtobomModule(
411
+ "cdxi",
412
+ "protobuf BOM input",
413
+ );
414
+ sbom = readBinary(importTarget, true);
273
415
  printSummary(sbom);
274
- } else if (
275
- sbomOrPath.startsWith("ghcr.io") ||
276
- sbomOrPath.startsWith("docker.io")
277
- ) {
416
+ } else if (isSupportedSbomRegistryReference(importTarget)) {
278
417
  try {
279
- sbom = getBomWithOras(sbomOrPath);
418
+ sbom = getBomWithOras(importTarget);
280
419
  if (sbom) {
281
420
  printSummary(sbom);
282
421
  } else {
283
422
  console.log(
284
- `cyclonedx sbom attachment was not found within ${sbomOrPath}`,
423
+ `cyclonedx sbom attachment was not found within ${importTarget}`,
285
424
  );
286
425
  }
287
426
  } catch (e) {
288
- console.log(`⚠ Unable to import the BOM from ${sbomOrPath} due to ${e}`);
427
+ console.log(
428
+ `⚠ Unable to import the BOM from ${importTarget} due to ${e}`,
429
+ );
289
430
  }
290
431
  } else {
291
- console.log(`⚠ ${sbomOrPath} is invalid.`);
432
+ console.log(`⚠ ${importTarget} is invalid.`);
292
433
  }
293
434
  };
294
435
  // Load any sbom passed from the command line
295
436
  if (process.argv.length > 2) {
296
- importSbom(process.argv[process.argv.length - 1]);
437
+ await importSbom(process.argv[process.argv.length - 1]);
297
438
  console.log("💭 Type .print to view the BOM as a table");
298
439
  console.log("💭 Type .trusted to list components with trusted publishing.");
299
440
  console.log(
300
441
  "💭 Type .provenance to list components with registry provenance evidence.",
301
442
  );
443
+ if (isLikelyHbom(sbom)) {
444
+ console.log(
445
+ "💭 Type .hbomsummary to review the host profile, evidence coverage, hardware-class mix, and collector diagnostics.",
446
+ );
447
+ }
302
448
  if (getAuditAnnotations().length) {
303
449
  console.log(
304
450
  "💭 Type .auditfindings to review cdx-audit and bom-audit annotations.",
305
451
  );
306
452
  }
307
- } else if (fs.existsSync("bom.json")) {
453
+ } else if (safeExistsSync("bom.json")) {
308
454
  // If the current directory has a bom.json load it
309
- importSbom("bom.json");
455
+ await importSbom("bom.json");
310
456
  } else {
311
457
  console.log("💭 Use .create <path> to create an SBOM for the given path.");
312
458
  console.log("💭 Use .import <json> to import an existing BOM.");
313
459
  console.log(
314
460
  "💭 For OBOM investigations, try .obomtips after importing an OBOM.",
315
461
  );
462
+ console.log(
463
+ "💭 For HBOM investigations, try .hbomtips after importing an HBOM.",
464
+ );
316
465
  console.log("💭 Type .exit or press ctrl+d to close.");
317
466
  }
318
467
 
@@ -333,7 +482,7 @@ cdxgenRepl.defineCommand("create", {
333
482
  help: "create an SBOM for the given path",
334
483
  async action(sbomOrPath) {
335
484
  this.clearBufferedCommand();
336
- const tempDir = fs.mkdtempSync(join(getTmpDir(), "cdxgen-repl-"));
485
+ const tempDir = safeMkdtempSync(join(getTmpDir(), "cdxgen-repl-"));
337
486
  const bomFile = join(tempDir, "bom.json");
338
487
  const bomNSData = await createBom(sbomOrPath, {
339
488
  multiProject: true,
@@ -350,6 +499,11 @@ cdxgenRepl.defineCommand("create", {
350
499
  console.log(
351
500
  "💭 Type .provenance to list components with registry provenance evidence.",
352
501
  );
502
+ if (isLikelyHbom(sbom)) {
503
+ console.log(
504
+ "💭 Type .hbomsummary or .hbomdiagnostics for focused hardware inventory and collector-diagnostic summaries.",
505
+ );
506
+ }
353
507
  if (getAuditAnnotations().length) {
354
508
  console.log(
355
509
  "💭 Type .auditfindings to review cdx-audit and bom-audit annotations.",
@@ -368,9 +522,9 @@ cdxgenRepl.defineCommand("create", {
368
522
  });
369
523
  cdxgenRepl.defineCommand("import", {
370
524
  help: "import an existing BOM",
371
- action(sbomOrPath) {
525
+ async action(sbomOrPath) {
372
526
  this.clearBufferedCommand();
373
- importSbom(sbomOrPath);
527
+ await importSbom(sbomOrPath);
374
528
  this.displayPrompt();
375
529
  },
376
530
  });
@@ -387,6 +541,293 @@ cdxgenRepl.defineCommand("summary", {
387
541
  this.displayPrompt();
388
542
  },
389
543
  });
544
+ cdxgenRepl.defineCommand("hbomsummary", {
545
+ help: "summarize HBOM host metadata, evidence coverage, and hardware-class mix",
546
+ action() {
547
+ const interactiveBom = getInteractiveBom();
548
+ if (!interactiveBom) {
549
+ console.log(
550
+ "⚠ No BOM is loaded. Use .import command to import an existing BOM",
551
+ );
552
+ this.displayPrompt();
553
+ return;
554
+ }
555
+ if (!isLikelyHbom(interactiveBom)) {
556
+ console.log(
557
+ "This BOM does not look like an HBOM. Import an HBOM generated with 'cdxgen -t hbom' to use this view.",
558
+ );
559
+ this.displayPrompt();
560
+ return;
561
+ }
562
+ const hbomSummary = getHbomSummary(interactiveBom);
563
+ printKeyValueTable("HBOM summary", [
564
+ ["Host", hbomSummary.metadataName],
565
+ ["Component type", hbomSummary.metadataType],
566
+ ["Manufacturer", hbomSummary.manufacturer],
567
+ ["Platform", hbomSummary.platform],
568
+ ["Architecture", hbomSummary.architecture],
569
+ ["Collector profile", hbomSummary.collectorProfile],
570
+ ["Identifier policy", hbomSummary.identifierPolicy],
571
+ ["Component count", hbomSummary.componentCount],
572
+ ["Hardware class count", hbomSummary.hardwareClassCount],
573
+ [
574
+ "Top hardware classes",
575
+ formatHbomHardwareClassSummary(hbomSummary.hardwareClassCounts),
576
+ ],
577
+ ["Command evidence count", hbomSummary.evidenceCommandCount],
578
+ ["Observed file count", hbomSummary.evidenceFileCount],
579
+ ]);
580
+ this.displayPrompt();
581
+ },
582
+ });
583
+ cdxgenRepl.defineCommand("hbomclasses", {
584
+ help: "show HBOM component counts by hardware class",
585
+ action() {
586
+ const interactiveBom = getInteractiveBom();
587
+ if (!interactiveBom) {
588
+ console.log(
589
+ "⚠ No BOM is loaded. Use .import command to import an existing BOM",
590
+ );
591
+ this.displayPrompt();
592
+ return;
593
+ }
594
+ if (!isLikelyHbom(interactiveBom)) {
595
+ console.log(
596
+ "This BOM does not look like an HBOM. Import an HBOM generated with 'cdxgen -t hbom' to use this view.",
597
+ );
598
+ this.displayPrompt();
599
+ return;
600
+ }
601
+ const hbomSummary = getHbomSummary(interactiveBom);
602
+ if (!hbomSummary.hardwareClassCounts.length) {
603
+ console.log(
604
+ "No HBOM hardware classes were found on the loaded BOM. Check whether the document includes cdx:hbom:hardwareClass properties.",
605
+ );
606
+ this.displayPrompt();
607
+ return;
608
+ }
609
+ printAuditTable("HBOM hardware classes", [
610
+ ["Hardware class", "Count"],
611
+ ...hbomSummary.hardwareClassCounts.map(({ hardwareClass, count }) => [
612
+ hardwareClass,
613
+ `${count}`,
614
+ ]),
615
+ ]);
616
+ this.displayPrompt();
617
+ },
618
+ });
619
+ cdxgenRepl.defineCommand("hbomevidence", {
620
+ help: "show HBOM collector profile plus command and observed-file evidence",
621
+ action() {
622
+ const interactiveBom = getInteractiveBom();
623
+ if (!interactiveBom) {
624
+ console.log(
625
+ "⚠ No BOM is loaded. Use .import command to import an existing BOM",
626
+ );
627
+ this.displayPrompt();
628
+ return;
629
+ }
630
+ if (!isLikelyHbom(interactiveBom)) {
631
+ console.log(
632
+ "This BOM does not look like an HBOM. Import an HBOM generated with 'cdxgen -t hbom' to use this view.",
633
+ );
634
+ this.displayPrompt();
635
+ return;
636
+ }
637
+ const hbomSummary = getHbomSummary(interactiveBom);
638
+ printKeyValueTable("HBOM evidence overview", [
639
+ ["Collector profile", hbomSummary.collectorProfile],
640
+ ["Command evidence count", hbomSummary.evidenceCommandCount],
641
+ ["Observed file count", hbomSummary.evidenceFileCount],
642
+ ]);
643
+ if (hbomSummary.evidenceCommands.length) {
644
+ printAuditTable("HBOM command evidence", [
645
+ ["Command #", "Evidence"],
646
+ ...hbomSummary.evidenceCommands.map((command, index) => [
647
+ `${index + 1}`,
648
+ command,
649
+ ]),
650
+ ]);
651
+ }
652
+ if (hbomSummary.evidenceFiles.length) {
653
+ printAuditTable("HBOM observed files", [
654
+ ["File #", "Path"],
655
+ ...hbomSummary.evidenceFiles.map((filePath, index) => [
656
+ `${index + 1}`,
657
+ filePath,
658
+ ]),
659
+ ]);
660
+ }
661
+ this.displayPrompt();
662
+ },
663
+ });
664
+ cdxgenRepl.defineCommand("hbomdiagnostics", {
665
+ help: "show parsed HBOM command diagnostics, issue counts, and install or privilege guidance",
666
+ action() {
667
+ const interactiveBom = getInteractiveHbomOrWarn(this);
668
+ if (!interactiveBom) {
669
+ return;
670
+ }
671
+ const hbomSummary = getHbomSummary(interactiveBom);
672
+ if (!hbomSummary.commandDiagnosticCount) {
673
+ console.log(
674
+ "No HBOM command diagnostics were found. This usually means the collector completed without recording missing-command or permission-denied enrichments.",
675
+ );
676
+ this.displayPrompt();
677
+ return;
678
+ }
679
+ printKeyValueTable("HBOM diagnostic overview", [
680
+ ["Diagnostic count", hbomSummary.commandDiagnosticCount],
681
+ ["Actionable diagnostics", hbomSummary.actionableDiagnosticCount],
682
+ ["Missing commands", hbomSummary.missingCommandCount],
683
+ ["Permission denied", hbomSummary.permissionDeniedCount],
684
+ ["Partial support", hbomSummary.partialSupportCount],
685
+ ["Timeouts", hbomSummary.timeoutCount],
686
+ ["Other command errors", hbomSummary.commandErrorCount],
687
+ ["Diagnostic issues", hbomSummary.diagnosticIssues.join(", ")],
688
+ ["Missing command IDs", hbomSummary.missingCommandIds.join(", ")],
689
+ ["Permission-denied IDs", hbomSummary.permissionDeniedIds.join(", ")],
690
+ ["Install hint count", hbomSummary.installHintCount],
691
+ ["Privilege hint count", hbomSummary.privilegeHintCount],
692
+ [
693
+ "Requires privileged rerun",
694
+ hbomSummary.requiresPrivilegedEnrichment ? "yes" : "no",
695
+ ],
696
+ ]);
697
+ printAuditTable("HBOM command diagnostics", [
698
+ ["Issue", "Diagnostic ID", "Command", "Hint / Message"],
699
+ ...hbomSummary.commandDiagnostics.map((diagnostic) => [
700
+ diagnostic.issue || "unknown",
701
+ diagnostic.id || "-",
702
+ diagnostic.command || "-",
703
+ getDiagnosticDisplayDetail(diagnostic),
704
+ ]),
705
+ ]);
706
+ this.displayPrompt();
707
+ },
708
+ });
709
+ cdxgenRepl.defineCommand("hbomfirmware", {
710
+ help: "show firmware, board, TPM, and update-managed HBOM components plus host firmware provenance",
711
+ action() {
712
+ const interactiveBom = getInteractiveHbomOrWarn(this);
713
+ if (!interactiveBom) {
714
+ return;
715
+ }
716
+ const metadataComponent = interactiveBom.metadata?.component;
717
+ const firmwareComponents = filterHbomComponentsByProperties(
718
+ interactiveBom,
719
+ HBOM_FIRMWARE_PROPERTIES,
720
+ ["firmware", "board", "tpm"],
721
+ );
722
+ const metadataEntries = [
723
+ [
724
+ "Board vendor",
725
+ getPropertyValue(metadataComponent, "cdx:hbom:boardVendor"),
726
+ ],
727
+ ["Board name", getPropertyValue(metadataComponent, "cdx:hbom:boardName")],
728
+ [
729
+ "BIOS vendor",
730
+ getPropertyValue(metadataComponent, "cdx:hbom:biosVendor"),
731
+ ],
732
+ [
733
+ "BIOS version",
734
+ getPropertyValue(metadataComponent, "cdx:hbom:biosVersion"),
735
+ ],
736
+ [
737
+ "Firmware date",
738
+ getPropertyValue(metadataComponent, "cdx:hbom:firmwareDate"),
739
+ ],
740
+ [
741
+ "Device-tree revision",
742
+ getPropertyValue(metadataComponent, "cdx:hbom:deviceTreeRevision"),
743
+ ],
744
+ ];
745
+ const hasMetadataProvenance = metadataEntries.some(([, value]) =>
746
+ Boolean(value),
747
+ );
748
+ if (!firmwareComponents.length && !hasMetadataProvenance) {
749
+ console.log(
750
+ "No focused firmware or board provenance pivots were found. Import an HBOM from a host that exposes board, TPM, or firmware-management metadata to use this view.",
751
+ );
752
+ this.displayPrompt();
753
+ return;
754
+ }
755
+ if (hasMetadataProvenance) {
756
+ printKeyValueTable("HBOM host firmware provenance", metadataEntries);
757
+ }
758
+ if (firmwareComponents.length) {
759
+ printTable(
760
+ { components: firmwareComponents, dependencies: [] },
761
+ undefined,
762
+ undefined,
763
+ `Found ${firmwareComponents.length} firmware, board, TPM, or update-managed component(s).`,
764
+ );
765
+ }
766
+ this.displayPrompt();
767
+ },
768
+ });
769
+ cdxgenRepl.defineCommand("hbombuses", {
770
+ help: "show bus, connector, USB, PCI, and external-expansion HBOM components with security or topology metadata",
771
+ action() {
772
+ const interactiveBom = getInteractiveHbomOrWarn(this);
773
+ if (!interactiveBom) {
774
+ return;
775
+ }
776
+ const busComponents = filterHbomComponentsByProperties(
777
+ interactiveBom,
778
+ HBOM_BUS_SECURITY_PROPERTIES,
779
+ [
780
+ "bus",
781
+ "usb-device",
782
+ "pci-device",
783
+ "display-adapter",
784
+ "display-connector",
785
+ ],
786
+ );
787
+ if (!busComponents.length) {
788
+ console.log(
789
+ "No bus or connector components with focused security or topology metadata were found.",
790
+ );
791
+ this.displayPrompt();
792
+ return;
793
+ }
794
+ printTable(
795
+ { components: busComponents, dependencies: [] },
796
+ undefined,
797
+ undefined,
798
+ `Found ${busComponents.length} bus, USB, PCI, or display-link component(s) with bus-security or topology pivots.`,
799
+ );
800
+ this.displayPrompt();
801
+ },
802
+ });
803
+ cdxgenRepl.defineCommand("hbompower", {
804
+ help: "show HBOM power and battery components with detailed design-capacity and runtime telemetry",
805
+ action() {
806
+ const interactiveBom = getInteractiveHbomOrWarn(this);
807
+ if (!interactiveBom) {
808
+ return;
809
+ }
810
+ const powerComponents = filterHbomComponentsByProperties(
811
+ interactiveBom,
812
+ HBOM_POWER_PROPERTIES,
813
+ ["power"],
814
+ );
815
+ if (!powerComponents.length) {
816
+ console.log(
817
+ "No focused power or battery telemetry components were found on the loaded HBOM.",
818
+ );
819
+ this.displayPrompt();
820
+ return;
821
+ }
822
+ printTable(
823
+ { components: powerComponents, dependencies: [] },
824
+ undefined,
825
+ undefined,
826
+ `Found ${powerComponents.length} power or battery component(s) with detailed runtime telemetry.`,
827
+ );
828
+ this.displayPrompt();
829
+ },
830
+ });
390
831
  cdxgenRepl.defineCommand("exit", {
391
832
  help: "exit",
392
833
  action() {
@@ -447,7 +888,7 @@ cdxgenRepl.defineCommand("search", {
447
888
  console.log(e);
448
889
  }
449
890
  } else {
450
- console.log("⚠ Specify the search string. Eg: .search <search string>");
891
+ console.log('⚠ Specify the search string. Eg: .search "search string"');
451
892
  }
452
893
  } else {
453
894
  console.log(
@@ -596,6 +1037,90 @@ cdxgenRepl.defineCommand("cryptos", {
596
1037
  this.displayPrompt();
597
1038
  },
598
1039
  });
1040
+ cdxgenRepl.defineCommand("sourcecryptos", {
1041
+ help: "show source-derived cryptographic assets detected from JS AST analysis",
1042
+ action() {
1043
+ const interactiveBom = getInteractiveBom();
1044
+ if (!interactiveBom?.components) {
1045
+ console.log("⚠ No BOM is loaded. Use .import command to import an SBOM");
1046
+ this.displayPrompt();
1047
+ return;
1048
+ }
1049
+ const sourceCryptoComponents = getSourceDerivedCryptoComponents(
1050
+ interactiveBom.components,
1051
+ );
1052
+ if (!sourceCryptoComponents.length) {
1053
+ console.log(
1054
+ "No source-derived crypto assets found. Generate a CBOM or SBOM with source crypto analysis to use this view.",
1055
+ );
1056
+ this.displayPrompt();
1057
+ return;
1058
+ }
1059
+ printTable(
1060
+ { components: sourceCryptoComponents, dependencies: [] },
1061
+ ["cryptographic-asset"],
1062
+ undefined,
1063
+ `Found ${sourceCryptoComponents.length} source-derived cryptographic asset component(s).`,
1064
+ );
1065
+ this.displayPrompt();
1066
+ },
1067
+ });
1068
+ cdxgenRepl.defineCommand("unpackagedbins", {
1069
+ help: "show executable file components that were not matched to OS package ownership",
1070
+ action() {
1071
+ const interactiveBom = getInteractiveBom();
1072
+ if (!interactiveBom?.components) {
1073
+ console.log("⚠ No BOM is loaded. Use .import command to import an SBOM");
1074
+ this.displayPrompt();
1075
+ return;
1076
+ }
1077
+ const unpackagedExecutables = getUnpackagedExecutableComponents(
1078
+ interactiveBom.components,
1079
+ );
1080
+ if (!unpackagedExecutables.length) {
1081
+ console.log(
1082
+ "No unpackaged executable file components found. Import a container or rootfs BOM with native file inventory to use this view.",
1083
+ );
1084
+ this.displayPrompt();
1085
+ return;
1086
+ }
1087
+ printTable(
1088
+ { components: unpackagedExecutables, dependencies: [] },
1089
+ ["file"],
1090
+ undefined,
1091
+ `Found ${unpackagedExecutables.length} executable file component(s) that were not traced to OS package ownership.`,
1092
+ );
1093
+ this.displayPrompt();
1094
+ },
1095
+ });
1096
+ cdxgenRepl.defineCommand("unpackagedlibs", {
1097
+ help: "show shared library file components that were not matched to OS package ownership",
1098
+ action() {
1099
+ const interactiveBom = getInteractiveBom();
1100
+ if (!interactiveBom?.components) {
1101
+ console.log("⚠ No BOM is loaded. Use .import command to import an SBOM");
1102
+ this.displayPrompt();
1103
+ return;
1104
+ }
1105
+ const unpackagedSharedLibraries = getUnpackagedSharedLibraryComponents(
1106
+ interactiveBom.components,
1107
+ );
1108
+ if (!unpackagedSharedLibraries.length) {
1109
+ console.log(
1110
+ "No unpackaged shared library file components found. Import a container or rootfs BOM with native file inventory to use this view.",
1111
+ );
1112
+ this.displayPrompt();
1113
+ return;
1114
+ }
1115
+ printTable(
1116
+ { components: unpackagedSharedLibraries, dependencies: [] },
1117
+ ["file"],
1118
+ undefined,
1119
+ `Found ${unpackagedSharedLibraries.length} shared library file component(s) that were not traced to OS package ownership.`,
1120
+ );
1121
+ this.displayPrompt();
1122
+ },
1123
+ });
599
1124
  cdxgenRepl.defineCommand("frameworks", {
600
1125
  help: "print the components of type framework as a table",
601
1126
  action() {
@@ -660,7 +1185,14 @@ cdxgenRepl.defineCommand("save", {
660
1185
  if (!saveToFile) {
661
1186
  saveToFile = "bom.json";
662
1187
  }
663
- fs.writeFileSync(saveToFile, JSON.stringify(sbom, null, null));
1188
+ if (isDryRun) {
1189
+ console.log(
1190
+ `⚠ Dry run mode blocks saving the BOM to ${saveToFile}. Disable --dry-run or CDXGEN_DRY_RUN to persist it.`,
1191
+ );
1192
+ this.displayPrompt();
1193
+ return;
1194
+ }
1195
+ safeWriteSync(saveToFile, JSON.stringify(sbom, null, 2));
664
1196
  console.log(`BOM saved successfully to ${saveToFile}`);
665
1197
  } else {
666
1198
  console.log(
@@ -1049,6 +1581,25 @@ cdxgenRepl.defineCommand("obomtips", {
1049
1581
  this.displayPrompt();
1050
1582
  },
1051
1583
  });
1584
+ cdxgenRepl.defineCommand("hbomtips", {
1585
+ help: "show analyst tips and useful commands for HBOM investigations",
1586
+ action() {
1587
+ console.log("HBOM analyst quick guide:");
1588
+ console.log("1. .hbomsummary");
1589
+ console.log("2. .hbomclasses");
1590
+ console.log("3. .hbomevidence");
1591
+ console.log("4. .hbomdiagnostics");
1592
+ console.log("5. .hbomfirmware / .hbombuses / .hbompower");
1593
+ console.log(
1594
+ "6. .auditfindings to review hbom-security, hbom-performance, and hbom-compliance findings",
1595
+ );
1596
+ console.log("7. .search <hardwareClass or device name>");
1597
+ console.log(
1598
+ 'Tip: .query components[properties[name="cdx:hbom:hardwareClass" and value="storage"]] filters directly by hardware class.',
1599
+ );
1600
+ this.displayPrompt();
1601
+ },
1602
+ });
1052
1603
  cdxgenRepl.defineCommand("licenses", {
1053
1604
  help: "visualize license distribution",
1054
1605
  async action() {
@@ -1249,6 +1800,7 @@ cdxgenRepl.defineCommand("tagcloud", {
1249
1800
  "docker_volumes",
1250
1801
  "etc_hosts",
1251
1802
  "firefox_addons",
1803
+ "gatekeeper",
1252
1804
  "vscode_extensions",
1253
1805
  "homebrew_packages",
1254
1806
  "installed_applications",
@@ -1265,8 +1817,10 @@ cdxgenRepl.defineCommand("tagcloud", {
1265
1817
  "portage_packages",
1266
1818
  "process_events",
1267
1819
  "processes",
1820
+ "secureboot_certificates",
1268
1821
  "privilege_transitions",
1269
1822
  "privileged_listening_ports",
1823
+ "npm_packages",
1270
1824
  "python_packages",
1271
1825
  "rpm_packages",
1272
1826
  "scheduled_tasks",
@@ -1291,6 +1845,7 @@ cdxgenRepl.defineCommand("tagcloud", {
1291
1845
  "npm_packages",
1292
1846
  "opera_extensions",
1293
1847
  "pipes_snapshot",
1848
+ "process_open_handles_snapshot",
1294
1849
  "process_open_sockets",
1295
1850
  "safari_extensions",
1296
1851
  "scheduled_tasks",