@cyclonedx/cdxgen 12.3.2 → 12.4.0

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