@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.
- package/README.md +70 -22
- package/bin/audit.js +21 -7
- package/bin/cdxgen.js +238 -116
- package/bin/convert.js +28 -13
- package/bin/hbom.js +490 -0
- package/bin/repl.js +580 -29
- package/bin/validate.js +34 -4
- package/bin/verify.js +40 -5
- package/data/README.md +298 -25
- package/data/component-tags.json +6 -0
- package/data/crypto-oid.json +16 -0
- package/data/predictive-audit-allowlist.json +11 -0
- package/data/queries-darwin.json +12 -1
- package/data/queries-win.json +7 -1
- package/data/queries.json +39 -2
- package/data/rules/ai-agent-governance.yaml +16 -0
- package/data/rules/asar-archives.yaml +150 -0
- package/data/rules/chrome-extensions.yaml +8 -0
- package/data/rules/ci-permissions.yaml +171 -15
- package/data/rules/container-risk.yaml +14 -7
- package/data/rules/dependency-sources.yaml +76 -5
- package/data/rules/hbom-compliance.yaml +325 -0
- package/data/rules/hbom-performance.yaml +307 -0
- package/data/rules/hbom-security.yaml +248 -0
- package/data/rules/host-topology.yaml +165 -0
- package/data/rules/mcp-servers.yaml +18 -3
- package/data/rules/obom-runtime.yaml +907 -22
- package/data/rules/package-integrity.yaml +36 -0
- package/data/rules/rootfs-hardening.yaml +179 -0
- package/data/rules/vscode-extensions.yaml +9 -0
- package/lib/audit/index.js +209 -8
- package/lib/audit/index.poku.js +332 -0
- package/lib/audit/reporters.js +222 -0
- package/lib/audit/targets.js +146 -1
- package/lib/audit/targets.poku.js +186 -0
- package/lib/cli/asar.poku.js +328 -0
- package/lib/cli/index.js +647 -127
- package/lib/cli/index.poku.js +1905 -187
- package/lib/evinser/evinser.js +14 -9
- package/lib/helpers/agentFormulationParser.js +6 -2
- package/lib/helpers/agentFormulationParser.poku.js +42 -0
- package/lib/helpers/analyzer.js +1444 -38
- package/lib/helpers/analyzer.poku.js +409 -0
- package/lib/helpers/analyzerScope.js +712 -0
- package/lib/helpers/asarutils.js +1556 -0
- package/lib/helpers/asarutils.poku.js +443 -0
- package/lib/helpers/auditCategories.js +12 -0
- package/lib/helpers/auditCategories.poku.js +32 -0
- package/lib/helpers/cbomutils.js +271 -1
- package/lib/helpers/cbomutils.poku.js +248 -5
- package/lib/helpers/chromextutils.js +25 -3
- package/lib/helpers/chromextutils.poku.js +68 -0
- package/lib/helpers/ciParsers/githubActions.js +79 -0
- package/lib/helpers/ciParsers/githubActions.poku.js +103 -0
- package/lib/helpers/communityAiConfigParser.js +15 -5
- package/lib/helpers/communityAiConfigParser.poku.js +71 -0
- package/lib/helpers/depsUtils.js +5 -0
- package/lib/helpers/depsUtils.poku.js +55 -0
- package/lib/helpers/display.js +336 -23
- package/lib/helpers/display.poku.js +179 -43
- package/lib/helpers/evidenceUtils.js +58 -0
- package/lib/helpers/evidenceUtils.poku.js +54 -0
- package/lib/helpers/exportUtils.js +9 -0
- package/lib/helpers/gtfobins.js +142 -8
- package/lib/helpers/gtfobins.poku.js +24 -1
- package/lib/helpers/hbom.js +710 -0
- package/lib/helpers/hbom.poku.js +496 -0
- package/lib/helpers/hbomAnalysis.js +268 -0
- package/lib/helpers/hbomAnalysis.poku.js +249 -0
- package/lib/helpers/hbomLoader.js +35 -0
- package/lib/helpers/hostTopology.js +803 -0
- package/lib/helpers/hostTopology.poku.js +363 -0
- package/lib/helpers/inventoryStats.js +69 -0
- package/lib/helpers/inventoryStats.poku.js +86 -0
- package/lib/helpers/lolbas.js +19 -1
- package/lib/helpers/lolbas.poku.js +23 -0
- package/lib/helpers/mcpConfigParser.js +21 -5
- package/lib/helpers/mcpConfigParser.poku.js +39 -2
- package/lib/helpers/osqueryTransform.js +47 -0
- package/lib/helpers/osqueryTransform.poku.js +47 -0
- package/lib/helpers/plugins.js +349 -0
- package/lib/helpers/plugins.poku.js +57 -0
- package/lib/helpers/propertySanitizer.js +121 -0
- package/lib/helpers/protobom.js +156 -45
- package/lib/helpers/protobom.poku.js +140 -5
- package/lib/helpers/remote/dependency-track.js +36 -3
- package/lib/helpers/remote/dependency-track.poku.js +44 -0
- package/lib/helpers/source.js +24 -0
- package/lib/helpers/source.poku.js +32 -0
- package/lib/helpers/utils.js +2454 -198
- package/lib/helpers/utils.poku.js +1798 -74
- package/lib/managers/binary.e2e.poku.js +367 -0
- package/lib/managers/binary.js +2306 -350
- package/lib/managers/binary.poku.js +1700 -1
- package/lib/managers/docker.js +441 -95
- package/lib/managers/docker.poku.js +1479 -14
- package/lib/server/server.js +2 -24
- package/lib/server/server.poku.js +36 -1
- package/lib/stages/postgen/annotator.js +38 -0
- package/lib/stages/postgen/annotator.poku.js +107 -1
- package/lib/stages/postgen/auditBom.js +121 -18
- package/lib/stages/postgen/auditBom.poku.js +2967 -990
- package/lib/stages/postgen/hostTopologyAudit.poku.js +186 -0
- package/lib/stages/postgen/postgen.js +192 -1
- package/lib/stages/postgen/postgen.poku.js +321 -0
- package/lib/stages/postgen/ruleEngine.js +116 -0
- package/lib/stages/pregen/envAudit.js +14 -3
- package/package.json +24 -21
- package/types/bin/hbom.d.ts +3 -0
- package/types/bin/hbom.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +44 -0
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/audit/reporters.d.ts +16 -0
- package/types/lib/audit/reporters.d.ts.map +1 -1
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +16 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +4 -0
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/agentFormulationParser.d.ts.map +1 -1
- package/types/lib/helpers/analyzer.d.ts +33 -0
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/analyzerScope.d.ts +11 -0
- package/types/lib/helpers/analyzerScope.d.ts.map +1 -0
- package/types/lib/helpers/asarutils.d.ts +34 -0
- package/types/lib/helpers/asarutils.d.ts.map +1 -0
- package/types/lib/helpers/auditCategories.d.ts +5 -0
- package/types/lib/helpers/auditCategories.d.ts.map +1 -1
- package/types/lib/helpers/cbomutils.d.ts +3 -2
- package/types/lib/helpers/cbomutils.d.ts.map +1 -1
- package/types/lib/helpers/chromextutils.d.ts.map +1 -1
- package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
- package/types/lib/helpers/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/helpers/depsUtils.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts +1 -0
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/evidenceUtils.d.ts +8 -0
- package/types/lib/helpers/evidenceUtils.d.ts.map +1 -0
- package/types/lib/helpers/exportUtils.d.ts.map +1 -1
- package/types/lib/helpers/gtfobins.d.ts +8 -0
- package/types/lib/helpers/gtfobins.d.ts.map +1 -1
- package/types/lib/helpers/hbom.d.ts +49 -0
- package/types/lib/helpers/hbom.d.ts.map +1 -0
- package/types/lib/helpers/hbomAnalysis.d.ts +62 -0
- package/types/lib/helpers/hbomAnalysis.d.ts.map +1 -0
- package/types/lib/helpers/hbomLoader.d.ts +7 -0
- package/types/lib/helpers/hbomLoader.d.ts.map +1 -0
- package/types/lib/helpers/hostTopology.d.ts +12 -0
- package/types/lib/helpers/hostTopology.d.ts.map +1 -0
- package/types/lib/helpers/inventoryStats.d.ts +11 -0
- package/types/lib/helpers/inventoryStats.d.ts.map +1 -0
- package/types/lib/helpers/lolbas.d.ts.map +1 -1
- package/types/lib/helpers/mcpConfigParser.d.ts +1 -1
- package/types/lib/helpers/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/helpers/osqueryTransform.d.ts +3 -0
- package/types/lib/helpers/osqueryTransform.d.ts.map +1 -1
- package/types/lib/helpers/plugins.d.ts +58 -0
- package/types/lib/helpers/plugins.d.ts.map +1 -0
- package/types/lib/helpers/propertySanitizer.d.ts +3 -0
- package/types/lib/helpers/propertySanitizer.d.ts.map +1 -0
- package/types/lib/helpers/protobom.d.ts +3 -4
- package/types/lib/helpers/protobom.d.ts.map +1 -1
- package/types/lib/helpers/remote/dependency-track.d.ts +10 -3
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
- package/types/lib/helpers/source.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts +74 -8
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +5 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +3 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +2 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/auditBom.d.ts +26 -1
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +2 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
- package/data/spdx-model-v3.0.1.jsonld +0 -15999
package/lib/helpers/display.js
CHANGED
|
@@ -2,6 +2,14 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
|
|
5
|
+
import { formatOccurrenceEvidence } from "./evidenceUtils.js";
|
|
6
|
+
import {
|
|
7
|
+
formatHbomHardwareClassSummary,
|
|
8
|
+
getHbomSummary,
|
|
9
|
+
isHbomLikeBom,
|
|
10
|
+
} from "./hbomAnalysis.js";
|
|
11
|
+
import { getHostViewSummary, isMergedHostViewBom } from "./hostTopology.js";
|
|
12
|
+
import { getPropertyValue } from "./inventoryStats.js";
|
|
5
13
|
import {
|
|
6
14
|
hasComponentRegistryProvenance,
|
|
7
15
|
REGISTRY_PROVENANCE_ICON,
|
|
@@ -65,12 +73,314 @@ const formatComponentName = (component, highlight) => {
|
|
|
65
73
|
return displayName;
|
|
66
74
|
};
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Builds the summary and provenance lines printed after the component table.
|
|
78
|
+
*
|
|
79
|
+
* @param {Object} bomJson CycloneDX BOM JSON object
|
|
80
|
+
* @param {string[]|undefined} filterTypes Optional list of component types to include
|
|
81
|
+
* @param {string|undefined} summaryText Optional summary message to print after the table
|
|
82
|
+
* @param {number} displayedProvenanceCount Number of displayed components with registry provenance
|
|
83
|
+
* @returns {string[]} Summary lines to print
|
|
84
|
+
*/
|
|
85
|
+
export const buildTableSummaryLines = (
|
|
86
|
+
bomJson,
|
|
87
|
+
filterTypes,
|
|
88
|
+
summaryText,
|
|
89
|
+
displayedProvenanceCount = 0,
|
|
90
|
+
) => {
|
|
91
|
+
const summaryLines = [];
|
|
92
|
+
if (summaryText) {
|
|
93
|
+
summaryLines.push(summaryText);
|
|
94
|
+
} else if (!filterTypes && isHbomLikeBom(bomJson)) {
|
|
95
|
+
const hbomSummary = getHbomSummary(bomJson);
|
|
96
|
+
summaryLines.push(
|
|
97
|
+
`HBOM includes ${hbomSummary.componentCount} hardware component(s) across ${hbomSummary.hardwareClassCount} hardware class(es)`,
|
|
98
|
+
);
|
|
99
|
+
if (hbomSummary.hardwareClassCounts.length) {
|
|
100
|
+
summaryLines.push(
|
|
101
|
+
`Top hardware classes: ${formatHbomHardwareClassSummary(hbomSummary.hardwareClassCounts)}`,
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (hbomSummary.collectorProfile) {
|
|
105
|
+
summaryLines.push(
|
|
106
|
+
`Collector profile: ${hbomSummary.collectorProfile}; command evidence: ${hbomSummary.evidenceCommandCount}; observed files: ${hbomSummary.evidenceFileCount}`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (hbomSummary.commandDiagnosticCount) {
|
|
110
|
+
const diagnosticDetails = [];
|
|
111
|
+
if (hbomSummary.missingCommandCount) {
|
|
112
|
+
diagnosticDetails.push(
|
|
113
|
+
`missing commands: ${hbomSummary.missingCommandCount}`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
if (hbomSummary.permissionDeniedCount) {
|
|
117
|
+
diagnosticDetails.push(
|
|
118
|
+
`permission denied: ${hbomSummary.permissionDeniedCount}`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
if (hbomSummary.partialSupportCount) {
|
|
122
|
+
diagnosticDetails.push(
|
|
123
|
+
`partial support: ${hbomSummary.partialSupportCount}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (hbomSummary.timeoutCount) {
|
|
127
|
+
diagnosticDetails.push(`timeouts: ${hbomSummary.timeoutCount}`);
|
|
128
|
+
}
|
|
129
|
+
if (hbomSummary.commandErrorCount) {
|
|
130
|
+
diagnosticDetails.push(
|
|
131
|
+
`other command errors: ${hbomSummary.commandErrorCount}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
summaryLines.push(
|
|
135
|
+
`Collector diagnostics: ${hbomSummary.commandDiagnosticCount} issue(s)${diagnosticDetails.length ? `; ${diagnosticDetails.join(", ")}` : ""}`,
|
|
136
|
+
);
|
|
137
|
+
if (hbomSummary.requiresPrivilegedEnrichment) {
|
|
138
|
+
summaryLines.push(
|
|
139
|
+
"Permission-sensitive enrichments were skipped or blocked. Re-run with --privileged where policy allows.",
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (isMergedHostViewBom(bomJson)) {
|
|
144
|
+
const hostViewSummary = getHostViewSummary(bomJson);
|
|
145
|
+
summaryLines.push(
|
|
146
|
+
`Host topology view: ${hostViewSummary.runtimeComponentCount} runtime component(s), ${hostViewSummary.topologyLinkCount} strict host/runtime topology link(s), ${hostViewSummary.linkedHardwareComponentCount} linked hardware component(s)`,
|
|
147
|
+
);
|
|
148
|
+
if (hostViewSummary.linkedRuntimeCategories.length) {
|
|
149
|
+
summaryLines.push(
|
|
150
|
+
`Linked runtime categories: ${hostViewSummary.linkedRuntimeCategories.join(", ")}`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
} else if (!filterTypes) {
|
|
155
|
+
summaryLines.push(
|
|
156
|
+
`BOM includes ${bomJson?.components?.length || 0} components and ${
|
|
157
|
+
bomJson?.dependencies?.length || 0
|
|
158
|
+
} dependencies`,
|
|
159
|
+
);
|
|
160
|
+
} else {
|
|
161
|
+
summaryLines.push(
|
|
162
|
+
`Components filtered based on type: ${filterTypes.join(", ")}`,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (displayedProvenanceCount > 0) {
|
|
166
|
+
summaryLines.push(
|
|
167
|
+
`Legend: ${REGISTRY_PROVENANCE_ICON} = registry provenance or trusted publishing evidence`,
|
|
168
|
+
);
|
|
169
|
+
summaryLines.push(
|
|
170
|
+
`${REGISTRY_PROVENANCE_ICON} ${displayedProvenanceCount} component(s) include registry provenance or trusted publishing metadata.`,
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
return summaryLines;
|
|
72
174
|
};
|
|
73
175
|
|
|
176
|
+
const HBOM_COLUMN_PRIORITY = Object.freeze([
|
|
177
|
+
["cdx:hbom:status", "status"],
|
|
178
|
+
["cdx:hbom:connected", "connected"],
|
|
179
|
+
["cdx:hbom:connectionState", "connectionState"],
|
|
180
|
+
["cdx:hbom:securityMode", "securityMode"],
|
|
181
|
+
["cdx:hbom:health", "health"],
|
|
182
|
+
["cdx:hbom:smartStatus", "smartStatus"],
|
|
183
|
+
["cdx:hbom:powerSource", "powerSource"],
|
|
184
|
+
["cdx:hbom:maximumCapacity", "maximumCapacity"],
|
|
185
|
+
["cdx:hbom:chargePercent", "chargePercent"],
|
|
186
|
+
["cdx:hbom:capacity", "capacity"],
|
|
187
|
+
["cdx:hbom:size", "size"],
|
|
188
|
+
["cdx:hbom:sizeBytes", "sizeBytes"],
|
|
189
|
+
["cdx:hbom:linkRateMbps", "linkRateMbps"],
|
|
190
|
+
["cdx:hbom:speedMbps", "speedMbps"],
|
|
191
|
+
["cdx:hbom:resolution", "resolution"],
|
|
192
|
+
["cdx:hbom:transport", "transport"],
|
|
193
|
+
["cdx:hbom:connectionType", "connectionType"],
|
|
194
|
+
["cdx:hbom:firmwareVersion", "firmwareVersion"],
|
|
195
|
+
["cdx:hbom:driver", "driver"],
|
|
196
|
+
["cdx:hbom:channel", "channel"],
|
|
197
|
+
["cdx:hbom:phyMode", "phyMode"],
|
|
198
|
+
["cdx:hbom:temperatureCelsius", "temperatureCelsius"],
|
|
199
|
+
["cdx:hostview:runtimeAddressCount", "runtimeAddrs"],
|
|
200
|
+
["cdx:hostview:kernel_modules:count", "kernelMods"],
|
|
201
|
+
["cdx:hostview:mount_hardening:count", "runtimeMounts"],
|
|
202
|
+
["cdx:hostview:runtime-storage:count", "runtimeStorage"],
|
|
203
|
+
["cdx:hostview:linkedRuntimeCategoryCount", "runtimeLinks"],
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
const HBOM_CLASS_PROPERTY_PRIORITY = Object.freeze({
|
|
207
|
+
"audio-device": Object.freeze([
|
|
208
|
+
["cdx:hbom:transport", "transport"],
|
|
209
|
+
["cdx:hbom:defaultOutput", "defaultOutput"],
|
|
210
|
+
["cdx:hbom:defaultInput", "defaultInput"],
|
|
211
|
+
["cdx:hbom:sampleRate", "sampleRate"],
|
|
212
|
+
]),
|
|
213
|
+
bus: Object.freeze([
|
|
214
|
+
["cdx:hbom:speed", "speed"],
|
|
215
|
+
["cdx:hbom:linkStatus", "linkStatus"],
|
|
216
|
+
["cdx:hbom:receptacleStatus", "receptacleStatus"],
|
|
217
|
+
]),
|
|
218
|
+
camera: Object.freeze([
|
|
219
|
+
["cdx:hbom:isVirtual", "virtual"],
|
|
220
|
+
["cdx:hbom:cameraModelId", "modelId"],
|
|
221
|
+
]),
|
|
222
|
+
"bluetooth-controller": Object.freeze([
|
|
223
|
+
["cdx:hbom:state", "state"],
|
|
224
|
+
["cdx:hbom:transport", "transport"],
|
|
225
|
+
["cdx:hbom:firmwareVersion", "firmware"],
|
|
226
|
+
]),
|
|
227
|
+
"bluetooth-device": Object.freeze([
|
|
228
|
+
["cdx:hbom:connectionState", "connection"],
|
|
229
|
+
["cdx:hbom:rssi", "rssi"],
|
|
230
|
+
["cdx:hbom:firmwareVersion", "firmware"],
|
|
231
|
+
["cdx:hbom:minorType", "minorType"],
|
|
232
|
+
]),
|
|
233
|
+
display: Object.freeze([
|
|
234
|
+
["cdx:hbom:resolution", "resolution"],
|
|
235
|
+
["cdx:hbom:connectionType", "connection"],
|
|
236
|
+
["cdx:hbom:vendorId", "vendorId"],
|
|
237
|
+
["cdx:hbom:productId", "productId"],
|
|
238
|
+
]),
|
|
239
|
+
memory: Object.freeze([
|
|
240
|
+
["cdx:hbom:size", "size"],
|
|
241
|
+
["cdx:hbom:sizeBytes", "sizeBytes"],
|
|
242
|
+
["cdx:hbom:memoryOnlineSize", "onlineSize"],
|
|
243
|
+
["cdx:hbom:addressSizes", "addressSizes"],
|
|
244
|
+
]),
|
|
245
|
+
"network-interface": Object.freeze([
|
|
246
|
+
["cdx:hbom:status", "status"],
|
|
247
|
+
["cdx:hbom:speedMbps", "speedMbps"],
|
|
248
|
+
["cdx:hbom:duplex", "duplex"],
|
|
249
|
+
["cdx:hbom:driver", "driver"],
|
|
250
|
+
["cdx:hostview:runtimeAddressCount", "runtimeAddrs"],
|
|
251
|
+
["cdx:hostview:kernel_modules:count", "kernelMods"],
|
|
252
|
+
]),
|
|
253
|
+
power: Object.freeze([
|
|
254
|
+
["cdx:hbom:health", "health"],
|
|
255
|
+
["cdx:hbom:chargePercent", "charge%"],
|
|
256
|
+
["cdx:hbom:maximumCapacity", "maxCapacity"],
|
|
257
|
+
["cdx:hbom:cycleCount", "cycles"],
|
|
258
|
+
["cdx:hbom:powerSource", "source"],
|
|
259
|
+
]),
|
|
260
|
+
"power-adapter": Object.freeze([
|
|
261
|
+
["cdx:hbom:connected", "connected"],
|
|
262
|
+
["cdx:hbom:watts", "watts"],
|
|
263
|
+
["cdx:hbom:isCharging", "charging"],
|
|
264
|
+
]),
|
|
265
|
+
processor: Object.freeze([
|
|
266
|
+
["cdx:hbom:coreCount", "cores"],
|
|
267
|
+
["cdx:hbom:logicalCpuCount", "logical"],
|
|
268
|
+
["cdx:hbom:physicalCpuCount", "physical"],
|
|
269
|
+
]),
|
|
270
|
+
storage: Object.freeze([
|
|
271
|
+
["cdx:hbom:capacity", "capacity"],
|
|
272
|
+
["cdx:hbom:smartStatus", "smart"],
|
|
273
|
+
["cdx:hbom:wearPercentageUsed", "wearUsed"],
|
|
274
|
+
["cdx:hbom:transport", "transport"],
|
|
275
|
+
["cdx:hbom:firmwareVersion", "firmware"],
|
|
276
|
+
["cdx:hostview:mount_hardening:count", "runtimeMounts"],
|
|
277
|
+
["cdx:hostview:runtime-storage:count", "runtimeStorage"],
|
|
278
|
+
]),
|
|
279
|
+
"storage-volume": Object.freeze([
|
|
280
|
+
["cdx:hbom:size", "size"],
|
|
281
|
+
["cdx:hbom:capacity", "capacity"],
|
|
282
|
+
["cdx:hbom:fileVault", "fileVault"],
|
|
283
|
+
["cdx:hbom:isEncrypted", "encrypted"],
|
|
284
|
+
["cdx:hbom:isRemovable", "removable"],
|
|
285
|
+
["cdx:hostview:mount_hardening:count", "runtimeMounts"],
|
|
286
|
+
["cdx:hostview:runtime-storage:count", "runtimeStorage"],
|
|
287
|
+
]),
|
|
288
|
+
sensor: Object.freeze([
|
|
289
|
+
["cdx:hbom:temperatureCelsius", "tempC"],
|
|
290
|
+
["cdx:hbom:fanCount", "fanCount"],
|
|
291
|
+
]),
|
|
292
|
+
"thermal-zone": Object.freeze([
|
|
293
|
+
["cdx:hbom:temperatureCelsius", "tempC"],
|
|
294
|
+
["cdx:hbom:fanCount", "fanCount"],
|
|
295
|
+
]),
|
|
296
|
+
"wireless-adapter": Object.freeze([
|
|
297
|
+
["cdx:hbom:connected", "connected"],
|
|
298
|
+
["cdx:hbom:securityMode", "security"],
|
|
299
|
+
["cdx:hbom:linkRateMbps", "linkMbps"],
|
|
300
|
+
["cdx:hbom:channel", "channel"],
|
|
301
|
+
["cdx:hbom:phyMode", "phy"],
|
|
302
|
+
["cdx:hostview:runtimeAddressCount", "runtimeAddrs"],
|
|
303
|
+
]),
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
function formatHbomKeyProperties(component) {
|
|
307
|
+
const hardwareClass = getPropertyValue(component, "cdx:hbom:hardwareClass");
|
|
308
|
+
const classSpecificPriority =
|
|
309
|
+
HBOM_CLASS_PROPERTY_PRIORITY[hardwareClass] || [];
|
|
310
|
+
const details = [];
|
|
311
|
+
const seenPropertyNames = new Set();
|
|
312
|
+
for (const [propertyName, label] of [
|
|
313
|
+
...classSpecificPriority,
|
|
314
|
+
...HBOM_COLUMN_PRIORITY,
|
|
315
|
+
]) {
|
|
316
|
+
if (seenPropertyNames.has(propertyName)) {
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
seenPropertyNames.add(propertyName);
|
|
320
|
+
const value = getPropertyValue(component, propertyName);
|
|
321
|
+
if (value === undefined || value === null || value === "") {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
details.push(`${label}=${value}`);
|
|
325
|
+
if (details.length >= 3) {
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return details.join(", ");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function printHBOMTable(bomJson, filterTypes, highlight, summaryText) {
|
|
333
|
+
const config = {
|
|
334
|
+
columnDefault: {
|
|
335
|
+
width: 28,
|
|
336
|
+
},
|
|
337
|
+
columnCount: 5,
|
|
338
|
+
columns: [
|
|
339
|
+
{ width: 22 },
|
|
340
|
+
{ width: 32 },
|
|
341
|
+
{ width: 24 },
|
|
342
|
+
{ width: 52 },
|
|
343
|
+
{ width: 24 },
|
|
344
|
+
],
|
|
345
|
+
};
|
|
346
|
+
const stream = createStream(config);
|
|
347
|
+
stream.write([
|
|
348
|
+
"Hardware Class",
|
|
349
|
+
"Name",
|
|
350
|
+
"Manufacturer / Version",
|
|
351
|
+
"Key Properties",
|
|
352
|
+
"Tags",
|
|
353
|
+
]);
|
|
354
|
+
for (const comp of bomJson.components) {
|
|
355
|
+
if (filterTypes && !filterTypes.includes(comp.type)) {
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
const manufacturerOrVersion = [comp.manufacturer?.name, comp.version]
|
|
359
|
+
.filter(Boolean)
|
|
360
|
+
.join(" / ");
|
|
361
|
+
stream.write([
|
|
362
|
+
highlightStr(
|
|
363
|
+
getPropertyValue(comp, "cdx:hbom:hardwareClass") || comp.type || "",
|
|
364
|
+
highlight,
|
|
365
|
+
),
|
|
366
|
+
formatComponentName(comp, highlight),
|
|
367
|
+
highlightStr(manufacturerOrVersion, highlight),
|
|
368
|
+
highlightStr(formatHbomKeyProperties(comp), highlight),
|
|
369
|
+
(comp.tags || []).join(", "),
|
|
370
|
+
]);
|
|
371
|
+
}
|
|
372
|
+
stream.end();
|
|
373
|
+
console.log();
|
|
374
|
+
for (const line of buildTableSummaryLines(
|
|
375
|
+
bomJson,
|
|
376
|
+
filterTypes,
|
|
377
|
+
summaryText,
|
|
378
|
+
0,
|
|
379
|
+
)) {
|
|
380
|
+
console.log(line);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
74
384
|
/**
|
|
75
385
|
* Builds legend lines for dependency tree marker icons.
|
|
76
386
|
*
|
|
@@ -196,6 +506,9 @@ export function printTable(
|
|
|
196
506
|
) {
|
|
197
507
|
return printOSTable(bomJson);
|
|
198
508
|
}
|
|
509
|
+
if (isHbomLikeBom(bomJson) && !filterTypes?.includes("cryptographic-asset")) {
|
|
510
|
+
return printHBOMTable(bomJson, filterTypes, highlight, summaryText);
|
|
511
|
+
}
|
|
199
512
|
const config = {
|
|
200
513
|
columnDefault: {
|
|
201
514
|
width: 30,
|
|
@@ -247,24 +560,13 @@ export function printTable(
|
|
|
247
560
|
}
|
|
248
561
|
stream.end();
|
|
249
562
|
console.log();
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
bomJson?.dependencies?.length || 0,
|
|
258
|
-
"dependencies",
|
|
259
|
-
);
|
|
260
|
-
} else {
|
|
261
|
-
console.log(`Components filtered based on type: ${filterTypes.join(", ")}`);
|
|
262
|
-
}
|
|
263
|
-
if (displayedProvenanceCount > 0) {
|
|
264
|
-
printProvenanceLegend();
|
|
265
|
-
console.log(
|
|
266
|
-
`${REGISTRY_PROVENANCE_ICON} ${displayedProvenanceCount} component(s) include registry provenance or trusted publishing metadata.`,
|
|
267
|
-
);
|
|
563
|
+
for (const line of buildTableSummaryLines(
|
|
564
|
+
bomJson,
|
|
565
|
+
filterTypes,
|
|
566
|
+
summaryText,
|
|
567
|
+
displayedProvenanceCount,
|
|
568
|
+
)) {
|
|
569
|
+
console.log(line);
|
|
268
570
|
}
|
|
269
571
|
}
|
|
270
572
|
const formatProps = (props) => {
|
|
@@ -371,6 +673,17 @@ const locationComparator = (a, b) => {
|
|
|
371
673
|
}
|
|
372
674
|
}
|
|
373
675
|
}
|
|
676
|
+
if (a && b) {
|
|
677
|
+
const tmpA = a.match(/^(.*):(\d+)(?::(\d+))?$/);
|
|
678
|
+
const tmpB = b.match(/^(.*):(\d+)(?::(\d+))?$/);
|
|
679
|
+
if (tmpA && tmpB && tmpA[1] === tmpB[1]) {
|
|
680
|
+
const lineComparison = Number(tmpA[2]) - Number(tmpB[2]);
|
|
681
|
+
if (lineComparison !== 0) {
|
|
682
|
+
return lineComparison;
|
|
683
|
+
}
|
|
684
|
+
return Number(tmpA[3] || 0) - Number(tmpB[3] || 0);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
374
687
|
return a.localeCompare(b);
|
|
375
688
|
};
|
|
376
689
|
|
|
@@ -410,7 +723,7 @@ export function printOccurrences(bomJson) {
|
|
|
410
723
|
comp.name,
|
|
411
724
|
comp.version || "",
|
|
412
725
|
comp.evidence.occurrences
|
|
413
|
-
.map((
|
|
726
|
+
.map((occurrence) => formatOccurrenceEvidence(occurrence))
|
|
414
727
|
.sort(locationComparator)
|
|
415
728
|
.join("\n"),
|
|
416
729
|
];
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
buildActivitySummaryPayload,
|
|
9
9
|
buildDependencyTreeLegendLines,
|
|
10
10
|
buildDependencyTreeLines,
|
|
11
|
+
buildTableSummaryLines,
|
|
11
12
|
printDependencyTree,
|
|
12
13
|
serializeActivitySummary,
|
|
13
14
|
} from "./display.js";
|
|
@@ -21,9 +22,156 @@ it("print tree test", () => {
|
|
|
21
22
|
});
|
|
22
23
|
|
|
23
24
|
it("prints a provenance icon for registry-backed components", async () => {
|
|
25
|
+
const rows = [];
|
|
26
|
+
const bomJson = {
|
|
27
|
+
components: [
|
|
28
|
+
{
|
|
29
|
+
group: "",
|
|
30
|
+
name: "left-pad",
|
|
31
|
+
properties: [
|
|
32
|
+
{
|
|
33
|
+
name: "cdx:npm:provenanceUrl",
|
|
34
|
+
value: "https://registry.npmjs.org/-/npm/v1/attestations/left-pad",
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
type: "library",
|
|
38
|
+
version: "1.3.0",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
group: "",
|
|
42
|
+
name: "lodash",
|
|
43
|
+
properties: [],
|
|
44
|
+
type: "library",
|
|
45
|
+
version: "4.17.21",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
dependencies: [],
|
|
49
|
+
};
|
|
50
|
+
const { printTable } = await esmock("./display.js", {
|
|
51
|
+
"./table.js": {
|
|
52
|
+
createStream: () => ({
|
|
53
|
+
end() {
|
|
54
|
+
// intentional no-op for stream stub
|
|
55
|
+
},
|
|
56
|
+
write(row) {
|
|
57
|
+
rows.push(row);
|
|
58
|
+
},
|
|
59
|
+
}),
|
|
60
|
+
table: sinon.stub().returns(""),
|
|
61
|
+
},
|
|
62
|
+
"./utils.js": {
|
|
63
|
+
isSecureMode: false,
|
|
64
|
+
safeExistsSync: sinon.stub(),
|
|
65
|
+
toCamel: sinon.stub(),
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
printTable(bomJson, undefined, undefined, "Found 1 trusted component.");
|
|
70
|
+
|
|
71
|
+
assert.strictEqual(rows[1][1], `${REGISTRY_PROVENANCE_ICON} left-pad`);
|
|
72
|
+
assert.strictEqual(rows[2][1], "lodash");
|
|
73
|
+
assert.deepStrictEqual(
|
|
74
|
+
buildTableSummaryLines(bomJson, undefined, "Found 1 trusted component.", 1),
|
|
75
|
+
[
|
|
76
|
+
"Found 1 trusted component.",
|
|
77
|
+
`Legend: ${REGISTRY_PROVENANCE_ICON} = registry provenance or trusted publishing evidence`,
|
|
78
|
+
`${REGISTRY_PROVENANCE_ICON} 1 component(s) include registry provenance or trusted publishing metadata.`,
|
|
79
|
+
],
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("renders HBOM tables with hardware-centric columns and summary lines", async () => {
|
|
24
84
|
const rows = [];
|
|
25
85
|
const consoleLogStub = sinon.stub(console, "log");
|
|
26
86
|
try {
|
|
87
|
+
const bomJson = {
|
|
88
|
+
metadata: {
|
|
89
|
+
component: {
|
|
90
|
+
name: "demo-host",
|
|
91
|
+
type: "device",
|
|
92
|
+
manufacturer: { name: "Example Corp" },
|
|
93
|
+
properties: [
|
|
94
|
+
{ name: "cdx:hbom:platform", value: "linux" },
|
|
95
|
+
{ name: "cdx:hbom:architecture", value: "amd64" },
|
|
96
|
+
{
|
|
97
|
+
name: "cdx:hbom:identifierPolicy",
|
|
98
|
+
value: "redacted-by-default",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
components: [
|
|
104
|
+
{
|
|
105
|
+
type: "device",
|
|
106
|
+
name: "eth0",
|
|
107
|
+
manufacturer: { name: "Intel" },
|
|
108
|
+
version: "en0",
|
|
109
|
+
properties: [
|
|
110
|
+
{ name: "cdx:hbom:hardwareClass", value: "network-interface" },
|
|
111
|
+
{ name: "cdx:hbom:status", value: "active" },
|
|
112
|
+
{ name: "cdx:hbom:speedMbps", value: "2500" },
|
|
113
|
+
{ name: "cdx:hbom:driver", value: "igc" },
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
type: "device",
|
|
118
|
+
name: "wifi0",
|
|
119
|
+
properties: [
|
|
120
|
+
{ name: "cdx:hbom:hardwareClass", value: "wireless-adapter" },
|
|
121
|
+
{ name: "cdx:hbom:connected", value: "true" },
|
|
122
|
+
{ name: "cdx:hbom:securityMode", value: "wpa3-personal" },
|
|
123
|
+
{ name: "cdx:hbom:linkRateMbps", value: "1200" },
|
|
124
|
+
{ name: "cdx:hbom:channel", value: "36" },
|
|
125
|
+
],
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
type: "device",
|
|
129
|
+
name: "nvme0",
|
|
130
|
+
manufacturer: { name: "Samsung" },
|
|
131
|
+
properties: [
|
|
132
|
+
{ name: "cdx:hbom:hardwareClass", value: "storage" },
|
|
133
|
+
{ name: "cdx:hbom:capacity", value: "1 TB" },
|
|
134
|
+
{ name: "cdx:hbom:smartStatus", value: "Verified" },
|
|
135
|
+
{ name: "cdx:hbom:wearPercentageUsed", value: "2" },
|
|
136
|
+
{ name: "cdx:hbom:transport", value: "nvme" },
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
type: "device",
|
|
141
|
+
name: "Internal Battery",
|
|
142
|
+
properties: [
|
|
143
|
+
{ name: "cdx:hbom:hardwareClass", value: "power" },
|
|
144
|
+
{ name: "cdx:hbom:health", value: "Good" },
|
|
145
|
+
{ name: "cdx:hbom:chargePercent", value: "80" },
|
|
146
|
+
{ name: "cdx:hbom:maximumCapacity", value: "91%" },
|
|
147
|
+
{ name: "cdx:hbom:cycleCount", value: "120" },
|
|
148
|
+
],
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
properties: [
|
|
152
|
+
{ name: "cdx:hbom:collectorProfile", value: "linux-amd64-v1" },
|
|
153
|
+
{ name: "cdx:hbom:evidence:commandCount", value: "2" },
|
|
154
|
+
{ name: "cdx:hbom:evidence:commandDiagnosticCount", value: "2" },
|
|
155
|
+
{
|
|
156
|
+
name: "cdx:hbom:evidence:commandDiagnostic",
|
|
157
|
+
value: JSON.stringify({
|
|
158
|
+
command: "lsusb",
|
|
159
|
+
installHint:
|
|
160
|
+
"Command not found: install the Linux package providing lsusb (commonly `usbutils`).",
|
|
161
|
+
issue: "missing-command",
|
|
162
|
+
}),
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "cdx:hbom:evidence:commandDiagnostic",
|
|
166
|
+
value: JSON.stringify({
|
|
167
|
+
command: "drm_info",
|
|
168
|
+
issue: "permission-denied",
|
|
169
|
+
privilegeHint:
|
|
170
|
+
"Retry with --privileged to allow a non-interactive sudo attempt for permission-sensitive Linux commands.",
|
|
171
|
+
}),
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
};
|
|
27
175
|
const { printTable } = await esmock("./display.js", {
|
|
28
176
|
"./table.js": {
|
|
29
177
|
createStream: () => ({
|
|
@@ -37,57 +185,45 @@ it("prints a provenance icon for registry-backed components", async () => {
|
|
|
37
185
|
table: sinon.stub().returns(""),
|
|
38
186
|
},
|
|
39
187
|
"./utils.js": {
|
|
188
|
+
getRecordedActivities: sinon.stub().returns([]),
|
|
189
|
+
isDryRun: false,
|
|
40
190
|
isSecureMode: false,
|
|
41
191
|
safeExistsSync: sinon.stub(),
|
|
42
192
|
toCamel: sinon.stub(),
|
|
43
193
|
},
|
|
44
194
|
});
|
|
45
195
|
|
|
46
|
-
printTable(
|
|
47
|
-
{
|
|
48
|
-
components: [
|
|
49
|
-
{
|
|
50
|
-
group: "",
|
|
51
|
-
name: "left-pad",
|
|
52
|
-
properties: [
|
|
53
|
-
{
|
|
54
|
-
name: "cdx:npm:provenanceUrl",
|
|
55
|
-
value:
|
|
56
|
-
"https://registry.npmjs.org/-/npm/v1/attestations/left-pad",
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
type: "library",
|
|
60
|
-
version: "1.3.0",
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
group: "",
|
|
64
|
-
name: "lodash",
|
|
65
|
-
properties: [],
|
|
66
|
-
type: "library",
|
|
67
|
-
version: "4.17.21",
|
|
68
|
-
},
|
|
69
|
-
],
|
|
70
|
-
dependencies: [],
|
|
71
|
-
},
|
|
72
|
-
undefined,
|
|
73
|
-
undefined,
|
|
74
|
-
"Found 1 trusted component.",
|
|
75
|
-
);
|
|
196
|
+
printTable(bomJson);
|
|
76
197
|
|
|
77
|
-
assert.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
198
|
+
assert.deepStrictEqual(rows[0], [
|
|
199
|
+
"Hardware Class",
|
|
200
|
+
"Name",
|
|
201
|
+
"Manufacturer / Version",
|
|
202
|
+
"Key Properties",
|
|
203
|
+
"Tags",
|
|
204
|
+
]);
|
|
205
|
+
assert.strictEqual(rows[1][0], "network-interface");
|
|
206
|
+
assert.strictEqual(rows[1][1], "eth0");
|
|
207
|
+
assert.strictEqual(rows[1][2], "Intel / en0");
|
|
208
|
+
assert.match(rows[1][3], /status=active/u);
|
|
209
|
+
assert.match(rows[1][3], /speedMbps=2500/u);
|
|
210
|
+
assert.match(rows[1][3], /driver=igc/u);
|
|
211
|
+
assert.strictEqual(rows[2][0], "wireless-adapter");
|
|
212
|
+
assert.match(
|
|
213
|
+
rows[2][3],
|
|
214
|
+
/^connected=true, security=wpa3-personal, linkMbps=1200$/u,
|
|
90
215
|
);
|
|
216
|
+
assert.strictEqual(rows[3][0], "storage");
|
|
217
|
+
assert.match(rows[3][3], /^capacity=1 TB, smart=Verified, wearUsed=2$/u);
|
|
218
|
+
assert.strictEqual(rows[4][0], "power");
|
|
219
|
+
assert.match(rows[4][3], /^health=Good, charge%=80, maxCapacity=91%$/u);
|
|
220
|
+
assert.deepStrictEqual(buildTableSummaryLines(bomJson), [
|
|
221
|
+
"HBOM includes 4 hardware component(s) across 4 hardware class(es)",
|
|
222
|
+
"Top hardware classes: network-interface (1), power (1), storage (1), wireless-adapter (1)",
|
|
223
|
+
"Collector profile: linux-amd64-v1; command evidence: 2; observed files: 0",
|
|
224
|
+
"Collector diagnostics: 2 issue(s); missing commands: 1, permission denied: 1",
|
|
225
|
+
"Permission-sensitive enrichments were skipped or blocked. Re-run with --privileged where policy allows.",
|
|
226
|
+
]);
|
|
91
227
|
} finally {
|
|
92
228
|
consoleLogStub.restore();
|
|
93
229
|
}
|