@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
@@ -0,0 +1,803 @@
1
+ import {
2
+ mergeDependencies,
3
+ mergeServices,
4
+ trimComponents,
5
+ } from "./depsUtils.js";
6
+ import { getHbomHardwareClass, isHbomLikeBom } from "./hbomAnalysis.js";
7
+ import { getPropertyValue } from "./inventoryStats.js";
8
+
9
+ const HOST_VIEW_PROPERTY_PREFIX = "cdx:hostview:";
10
+ const NETWORK_HARDWARE_CLASSES = new Set([
11
+ "network-interface",
12
+ "wireless-adapter",
13
+ ]);
14
+ const STORAGE_HARDWARE_CLASSES = new Set([
15
+ "storage",
16
+ "storage-device",
17
+ "storage-volume",
18
+ ]);
19
+ const OBOM_HOST_ANCHOR_TYPES = new Set(["device", "operating-system"]);
20
+ const HBOM_NETWORK_IDENTITY_PROPS = Object.freeze([
21
+ "cdx:hbom:interface",
22
+ "cdx:hbom:interfaceName",
23
+ ]);
24
+ const RUNTIME_STORAGE_IDENTITY_PROPS = Object.freeze([
25
+ "volume_uuid",
26
+ "persistent_volume_id",
27
+ "uuid",
28
+ "device_id",
29
+ ]);
30
+ const HBOM_STORAGE_IDENTITY_PROPS = Object.freeze([
31
+ "cdx:hbom:volumeUuid",
32
+ "cdx:hbom:uuid",
33
+ "cdx:hbom:deviceNode",
34
+ ]);
35
+ const HBOM_STORAGE_DEVICE_PROPS = Object.freeze([
36
+ "cdx:hbom:deviceNode",
37
+ "cdx:hbom:devicePath",
38
+ ]);
39
+ const HBOM_STORAGE_MOUNT_PATH_PROPS = Object.freeze([
40
+ "cdx:hbom:mountPoint",
41
+ "cdx:hbom:mountPath",
42
+ "cdx:hbom:path",
43
+ ]);
44
+ const SECURE_BOOT_HOST_IDENTITY_PROPS = Object.freeze([
45
+ [
46
+ "path",
47
+ Object.freeze([
48
+ "cdx:hbom:secureBootCertificatePath",
49
+ "cdx:hbom:secureBootDbPath",
50
+ "cdx:hbom:secureBootDbxPath",
51
+ ]),
52
+ ],
53
+ [
54
+ "serial",
55
+ Object.freeze([
56
+ "cdx:hbom:secureBootCertificateSerial",
57
+ "cdx:hbom:secureBootDbSerial",
58
+ "cdx:hbom:secureBootDbxSerial",
59
+ "cdx:hbom:secureBootSerial",
60
+ ]),
61
+ ],
62
+ [
63
+ "sha1",
64
+ Object.freeze([
65
+ "cdx:hbom:secureBootCertificateSha1",
66
+ "cdx:hbom:secureBootDbSha1",
67
+ "cdx:hbom:secureBootDbxSha1",
68
+ "cdx:hbom:secureBootSha1",
69
+ ]),
70
+ ],
71
+ [
72
+ "subject_key_id",
73
+ Object.freeze([
74
+ "cdx:hbom:secureBootCertificateSubjectKeyId",
75
+ "cdx:hbom:secureBootDbSubjectKeyId",
76
+ "cdx:hbom:secureBootDbxSubjectKeyId",
77
+ "cdx:hbom:secureBootSubjectKeyId",
78
+ ]),
79
+ ],
80
+ [
81
+ "authority_key_id",
82
+ Object.freeze([
83
+ "cdx:hbom:secureBootCertificateAuthorityKeyId",
84
+ "cdx:hbom:secureBootDbAuthorityKeyId",
85
+ "cdx:hbom:secureBootDbxAuthorityKeyId",
86
+ "cdx:hbom:secureBootAuthorityKeyId",
87
+ ]),
88
+ ],
89
+ ]);
90
+
91
+ function getPropertyValues(propertiesOrObject, propertyName) {
92
+ const properties = Array.isArray(propertiesOrObject)
93
+ ? propertiesOrObject
94
+ : Array.isArray(propertiesOrObject?.properties)
95
+ ? propertiesOrObject.properties
96
+ : [];
97
+ return properties
98
+ .filter((property) => property?.name === propertyName)
99
+ .map((property) => property.value)
100
+ .filter(
101
+ (value) =>
102
+ value !== undefined && value !== null && `${value}`.trim() !== "",
103
+ );
104
+ }
105
+
106
+ function removePropertiesByPrefix(subject, propertyPrefix) {
107
+ if (!Array.isArray(subject?.properties)) {
108
+ return;
109
+ }
110
+ subject.properties = subject.properties.filter(
111
+ (property) => !`${property?.name || ""}`.startsWith(propertyPrefix),
112
+ );
113
+ }
114
+
115
+ function addUniqueProperty(subject, name, value) {
116
+ if (
117
+ !subject ||
118
+ value === undefined ||
119
+ value === null ||
120
+ `${value}`.trim() === ""
121
+ ) {
122
+ return;
123
+ }
124
+ if (!Array.isArray(subject.properties)) {
125
+ subject.properties = [];
126
+ }
127
+ if (
128
+ subject.properties.some(
129
+ (property) => property?.name === name && property?.value === `${value}`,
130
+ )
131
+ ) {
132
+ return;
133
+ }
134
+ subject.properties.push({
135
+ name,
136
+ value: `${value}`,
137
+ });
138
+ }
139
+
140
+ function addHostViewSummaryProperty(bomJson, name, value) {
141
+ addUniqueProperty(bomJson, name, value);
142
+ addUniqueProperty(bomJson?.metadata?.component, name, value);
143
+ }
144
+
145
+ function sanitizeRefToken(value, fallback = "unknown") {
146
+ let normalizedValue = `${value || ""}`
147
+ .trim()
148
+ .replace(/[\r\n\t]+/gu, " ")
149
+ .replace(/\s+/gu, "-")
150
+ .replace(/[^a-zA-Z0-9._:-]+/gu, "-")
151
+ .replace(/-+/gu, "-");
152
+ while (normalizedValue.startsWith("-") || normalizedValue.startsWith(":")) {
153
+ normalizedValue = normalizedValue.slice(1);
154
+ }
155
+ while (normalizedValue.endsWith("-") || normalizedValue.endsWith(":")) {
156
+ normalizedValue = normalizedValue.slice(0, -1);
157
+ }
158
+ return normalizedValue || fallback;
159
+ }
160
+
161
+ function createSyntheticMetadataRef(metadataComponent) {
162
+ return [
163
+ "urn:cdxgen:host",
164
+ sanitizeRefToken(metadataComponent?.type || "device"),
165
+ sanitizeRefToken(metadataComponent?.name || "host"),
166
+ sanitizeRefToken(
167
+ getPropertyValue(metadataComponent, "cdx:hbom:platform") ||
168
+ getPropertyValue(metadataComponent, "cdx:hbom:architecture") ||
169
+ metadataComponent?.version ||
170
+ "live",
171
+ ),
172
+ ].join(":");
173
+ }
174
+
175
+ function createSyntheticComponentRef(component) {
176
+ const hbomClass = getHbomHardwareClass(component);
177
+ const runtimeCategory = getPropertyValue(component, "cdx:osquery:category");
178
+ const identityValue =
179
+ getPropertyValue(component, "cdx:hbom:busInfo") ||
180
+ getPropertyValue(component, "interface") ||
181
+ getPropertyValue(component, "path") ||
182
+ getPropertyValue(component, "uuid") ||
183
+ component?.version ||
184
+ "unknown";
185
+ return [
186
+ "urn:cdxgen:component",
187
+ sanitizeRefToken(component?.type || "component"),
188
+ sanitizeRefToken(hbomClass || runtimeCategory || "component"),
189
+ sanitizeRefToken(component?.name || "unnamed"),
190
+ sanitizeRefToken(identityValue),
191
+ ].join(":");
192
+ }
193
+
194
+ function assignSyntheticBomRef(subject, usedRefs, createRef) {
195
+ if (!subject || typeof subject !== "object") {
196
+ return;
197
+ }
198
+ const existingRef = subject["bom-ref"];
199
+ if (existingRef) {
200
+ usedRefs.add(existingRef.toLowerCase());
201
+ return;
202
+ }
203
+ const baseRef = createRef(subject);
204
+ let candidateRef = baseRef;
205
+ let collisionCounter = 1;
206
+ while (usedRefs.has(candidateRef.toLowerCase())) {
207
+ collisionCounter += 1;
208
+ candidateRef = `${baseRef}:${collisionCounter}`;
209
+ }
210
+ subject["bom-ref"] = candidateRef;
211
+ usedRefs.add(candidateRef.toLowerCase());
212
+ }
213
+
214
+ function ensureBomRefs(bomJson) {
215
+ const usedRefs = new Set();
216
+ if (bomJson?.metadata?.component?.["bom-ref"]) {
217
+ usedRefs.add(bomJson.metadata.component["bom-ref"].toLowerCase());
218
+ }
219
+ for (const component of bomJson?.components || []) {
220
+ if (component?.["bom-ref"]) {
221
+ usedRefs.add(component["bom-ref"].toLowerCase());
222
+ }
223
+ }
224
+ if (bomJson?.metadata?.component) {
225
+ assignSyntheticBomRef(
226
+ bomJson.metadata.component,
227
+ usedRefs,
228
+ createSyntheticMetadataRef,
229
+ );
230
+ }
231
+ for (const component of bomJson?.components || []) {
232
+ assignSyntheticBomRef(component, usedRefs, createSyntheticComponentRef);
233
+ }
234
+ return bomJson;
235
+ }
236
+
237
+ function appendMapEntry(map, key, value) {
238
+ if (!key || !value) {
239
+ return;
240
+ }
241
+ if (!map.has(key)) {
242
+ map.set(key, new Set());
243
+ }
244
+ map.get(key).add(value);
245
+ }
246
+
247
+ function toNormalizedIdentity(value) {
248
+ const normalizedValue = `${value || ""}`.trim().toLowerCase();
249
+ if (!normalizedValue || normalizedValue.startsWith("redacted:")) {
250
+ return undefined;
251
+ }
252
+ return normalizedValue;
253
+ }
254
+
255
+ function getNormalizedPropertyValues(subject, propertyName) {
256
+ return getPropertyValues(subject, propertyName)
257
+ .map((value) => toNormalizedIdentity(value))
258
+ .filter(Boolean);
259
+ }
260
+
261
+ function getNormalizedCandidateValues(values = []) {
262
+ return Array.from(
263
+ new Set(values.map((value) => toNormalizedIdentity(value)).filter(Boolean)),
264
+ );
265
+ }
266
+
267
+ function isFilesystemIdentity(value) {
268
+ const normalizedValue = `${value || ""}`.trim();
269
+ return (
270
+ normalizedValue.startsWith("/") ||
271
+ /^[a-z]:$/iu.test(normalizedValue) ||
272
+ /^[a-z]:[\\/]/iu.test(normalizedValue)
273
+ );
274
+ }
275
+
276
+ function createRuntimeIndexes(runtimeComponents) {
277
+ const interfaceAddressesByName = new Map();
278
+ const kernelModulesByName = new Map();
279
+ const mountPathsByDevice = new Map();
280
+ const mountPathsByPath = new Map();
281
+ const runtimeByStorageIdentity = new Map();
282
+ const secureBootCertificatesByField = {
283
+ authority_key_id: new Map(),
284
+ path: new Map(),
285
+ serial: new Map(),
286
+ sha1: new Map(),
287
+ subject_key_id: new Map(),
288
+ };
289
+
290
+ for (const component of runtimeComponents) {
291
+ const runtimeCategory = getPropertyValue(component, "cdx:osquery:category");
292
+ if (!runtimeCategory || !component?.["bom-ref"]) {
293
+ continue;
294
+ }
295
+ if (runtimeCategory === "interface_addresses") {
296
+ appendMapEntry(
297
+ interfaceAddressesByName,
298
+ toNormalizedIdentity(getPropertyValue(component, "interface")),
299
+ component["bom-ref"],
300
+ );
301
+ }
302
+ if (runtimeCategory === "kernel_modules") {
303
+ appendMapEntry(
304
+ kernelModulesByName,
305
+ toNormalizedIdentity(component.name),
306
+ component["bom-ref"],
307
+ );
308
+ }
309
+ if (runtimeCategory === "mount_hardening") {
310
+ appendMapEntry(
311
+ mountPathsByDevice,
312
+ toNormalizedIdentity(getPropertyValue(component, "device")),
313
+ component["bom-ref"],
314
+ );
315
+ appendMapEntry(
316
+ mountPathsByPath,
317
+ toNormalizedIdentity(getPropertyValue(component, "path")),
318
+ component["bom-ref"],
319
+ );
320
+ }
321
+ if (runtimeCategory === "secureboot_certificates") {
322
+ for (const fieldName of Object.keys(secureBootCertificatesByField)) {
323
+ appendMapEntry(
324
+ secureBootCertificatesByField[fieldName],
325
+ toNormalizedIdentity(getPropertyValue(component, fieldName)),
326
+ component["bom-ref"],
327
+ );
328
+ }
329
+ }
330
+ for (const propertyName of RUNTIME_STORAGE_IDENTITY_PROPS) {
331
+ appendMapEntry(
332
+ runtimeByStorageIdentity,
333
+ toNormalizedIdentity(getPropertyValue(component, propertyName)),
334
+ component["bom-ref"],
335
+ );
336
+ }
337
+ }
338
+
339
+ return {
340
+ interfaceAddressesByName,
341
+ kernelModulesByName,
342
+ mountPathsByDevice,
343
+ mountPathsByPath,
344
+ runtimeByStorageIdentity,
345
+ secureBootCertificatesByField,
346
+ };
347
+ }
348
+
349
+ function addCategoryRefs(linkedRefs, linkedCategoryRefs, category, refs = []) {
350
+ if (!category || !refs?.length) {
351
+ return;
352
+ }
353
+ if (!linkedCategoryRefs.has(category)) {
354
+ linkedCategoryRefs.set(category, new Set());
355
+ }
356
+ const categoryRefs = linkedCategoryRefs.get(category);
357
+ for (const ref of refs) {
358
+ if (!ref) {
359
+ continue;
360
+ }
361
+ linkedRefs.add(ref);
362
+ categoryRefs.add(ref);
363
+ }
364
+ }
365
+
366
+ function annotateLinkedComponent(component, linkedCategoryRefs, linkedRefs) {
367
+ removePropertiesByPrefix(component, HOST_VIEW_PROPERTY_PREFIX);
368
+ const sortedCategories = Array.from(linkedCategoryRefs.keys()).sort();
369
+ addUniqueProperty(
370
+ component,
371
+ "cdx:hostview:linkedRuntimeCategoryCount",
372
+ `${sortedCategories.length}`,
373
+ );
374
+ addUniqueProperty(
375
+ component,
376
+ "cdx:hostview:topologyLinkCount",
377
+ `${linkedRefs.size}`,
378
+ );
379
+ if (linkedCategoryRefs.has("interface_addresses")) {
380
+ addUniqueProperty(
381
+ component,
382
+ "cdx:hostview:runtimeAddressCount",
383
+ `${linkedCategoryRefs.get("interface_addresses").size}`,
384
+ );
385
+ }
386
+ for (const category of sortedCategories) {
387
+ addUniqueProperty(
388
+ component,
389
+ "cdx:hostview:linkedRuntimeCategory",
390
+ category,
391
+ );
392
+ addUniqueProperty(
393
+ component,
394
+ `cdx:hostview:${category}:count`,
395
+ `${linkedCategoryRefs.get(category).size}`,
396
+ );
397
+ }
398
+ }
399
+
400
+ function createHostTrustLinks(metadataComponent, indexes) {
401
+ const linkedRefs = new Set();
402
+ const linkedCategoryRefs = new Map();
403
+
404
+ for (const [fieldName, propertyNames] of SECURE_BOOT_HOST_IDENTITY_PROPS) {
405
+ const fieldIndex = indexes.secureBootCertificatesByField[fieldName];
406
+ if (!fieldIndex) {
407
+ continue;
408
+ }
409
+ for (const propertyName of propertyNames) {
410
+ const identityValues = getNormalizedPropertyValues(
411
+ metadataComponent,
412
+ propertyName,
413
+ );
414
+ for (const identityValue of identityValues) {
415
+ addCategoryRefs(
416
+ linkedRefs,
417
+ linkedCategoryRefs,
418
+ "secureboot_certificates",
419
+ Array.from(fieldIndex.get(identityValue) || []),
420
+ );
421
+ }
422
+ }
423
+ }
424
+
425
+ return {
426
+ linkedCategoryRefs,
427
+ linkedRefs,
428
+ };
429
+ }
430
+
431
+ function createDependencyEdgeList(
432
+ hostRef,
433
+ metadataComponent,
434
+ hbomComponents,
435
+ runtimeAnchorRefs,
436
+ indexes,
437
+ ) {
438
+ const newDependencies = [];
439
+ const linkedRuntimeCategories = new Set();
440
+ let linkedHardwareComponentCount = 0;
441
+ let topologyLinkCount = 0;
442
+
443
+ const hostTrustLinks = createHostTrustLinks(metadataComponent, indexes);
444
+ const hostDependsOn = [
445
+ ...hbomComponents.map((component) => component["bom-ref"]),
446
+ ...runtimeAnchorRefs,
447
+ ...hostTrustLinks.linkedRefs,
448
+ ].sort();
449
+
450
+ if (hostRef && hostDependsOn.length) {
451
+ newDependencies.push({
452
+ ref: hostRef,
453
+ dependsOn: hostDependsOn,
454
+ });
455
+ }
456
+ if (hostTrustLinks.linkedRefs.size) {
457
+ topologyLinkCount += hostTrustLinks.linkedRefs.size;
458
+ for (const runtimeCategory of hostTrustLinks.linkedCategoryRefs.keys()) {
459
+ linkedRuntimeCategories.add(runtimeCategory);
460
+ }
461
+ annotateLinkedComponent(
462
+ metadataComponent,
463
+ hostTrustLinks.linkedCategoryRefs,
464
+ hostTrustLinks.linkedRefs,
465
+ );
466
+ }
467
+
468
+ for (const component of hbomComponents) {
469
+ const linkedRefs = new Set();
470
+ const linkedCategoryRefs = new Map();
471
+ const hardwareClass = getHbomHardwareClass(component);
472
+
473
+ if (NETWORK_HARDWARE_CLASSES.has(hardwareClass)) {
474
+ const interfaceKeys = getNormalizedCandidateValues([
475
+ component.name,
476
+ component.version,
477
+ ...HBOM_NETWORK_IDENTITY_PROPS.flatMap((propertyName) =>
478
+ getPropertyValues(component, propertyName),
479
+ ),
480
+ ]);
481
+ for (const interfaceKey of interfaceKeys) {
482
+ addCategoryRefs(
483
+ linkedRefs,
484
+ linkedCategoryRefs,
485
+ "interface_addresses",
486
+ Array.from(indexes.interfaceAddressesByName.get(interfaceKey) || []),
487
+ );
488
+ }
489
+ const driverKey = toNormalizedIdentity(
490
+ getPropertyValue(component, "cdx:hbom:driver"),
491
+ );
492
+ addCategoryRefs(
493
+ linkedRefs,
494
+ linkedCategoryRefs,
495
+ "kernel_modules",
496
+ Array.from(indexes.kernelModulesByName.get(driverKey) || []),
497
+ );
498
+ }
499
+
500
+ if (STORAGE_HARDWARE_CLASSES.has(hardwareClass)) {
501
+ for (const propertyName of HBOM_STORAGE_IDENTITY_PROPS) {
502
+ const storageIdentity = toNormalizedIdentity(
503
+ getPropertyValue(component, propertyName),
504
+ );
505
+ addCategoryRefs(
506
+ linkedRefs,
507
+ linkedCategoryRefs,
508
+ "runtime-storage",
509
+ Array.from(
510
+ indexes.runtimeByStorageIdentity.get(storageIdentity) || [],
511
+ ),
512
+ );
513
+ }
514
+ for (const propertyName of HBOM_STORAGE_DEVICE_PROPS) {
515
+ const devicePath = toNormalizedIdentity(
516
+ getPropertyValue(component, propertyName),
517
+ );
518
+ addCategoryRefs(
519
+ linkedRefs,
520
+ linkedCategoryRefs,
521
+ "mount_hardening",
522
+ Array.from(indexes.mountPathsByDevice.get(devicePath) || []),
523
+ );
524
+ }
525
+ for (const propertyName of HBOM_STORAGE_MOUNT_PATH_PROPS) {
526
+ const mountPath = toNormalizedIdentity(
527
+ getPropertyValue(component, propertyName),
528
+ );
529
+ addCategoryRefs(
530
+ linkedRefs,
531
+ linkedCategoryRefs,
532
+ "mount_hardening",
533
+ Array.from(indexes.mountPathsByPath.get(mountPath) || []),
534
+ );
535
+ }
536
+ const explicitFilesystemIdentity = isFilesystemIdentity(component.version)
537
+ ? toNormalizedIdentity(component.version)
538
+ : undefined;
539
+ addCategoryRefs(
540
+ linkedRefs,
541
+ linkedCategoryRefs,
542
+ "mount_hardening",
543
+ Array.from(
544
+ indexes.mountPathsByDevice.get(explicitFilesystemIdentity) || [],
545
+ ),
546
+ );
547
+ addCategoryRefs(
548
+ linkedRefs,
549
+ linkedCategoryRefs,
550
+ "mount_hardening",
551
+ Array.from(
552
+ indexes.mountPathsByPath.get(explicitFilesystemIdentity) || [],
553
+ ),
554
+ );
555
+ }
556
+
557
+ if (!linkedRefs.size) {
558
+ removePropertiesByPrefix(component, HOST_VIEW_PROPERTY_PREFIX);
559
+ continue;
560
+ }
561
+
562
+ linkedHardwareComponentCount += 1;
563
+ topologyLinkCount += linkedRefs.size;
564
+ for (const runtimeCategory of linkedCategoryRefs.keys()) {
565
+ linkedRuntimeCategories.add(runtimeCategory);
566
+ }
567
+ annotateLinkedComponent(component, linkedCategoryRefs, linkedRefs);
568
+ newDependencies.push({
569
+ ref: component["bom-ref"],
570
+ dependsOn: Array.from(linkedRefs).sort(),
571
+ });
572
+ }
573
+
574
+ return {
575
+ linkedHardwareComponentCount,
576
+ linkedRuntimeCategories: Array.from(linkedRuntimeCategories).sort(),
577
+ newDependencies,
578
+ topologyLinkCount,
579
+ };
580
+ }
581
+
582
+ function mergeToolComponents(firstComponents = [], secondComponents = []) {
583
+ const mergedByKey = new Map();
584
+ for (const component of [...firstComponents, ...secondComponents]) {
585
+ if (!component) {
586
+ continue;
587
+ }
588
+ const key =
589
+ `${component["bom-ref"] || ""}:${component.group || ""}:${component.name || ""}:${component.version || ""}`.toLowerCase();
590
+ if (!mergedByKey.has(key)) {
591
+ mergedByKey.set(key, component);
592
+ }
593
+ }
594
+ return Array.from(mergedByKey.values());
595
+ }
596
+
597
+ function mergeMetadataProperties(firstProperties = [], secondProperties = []) {
598
+ const mergedProperties = [];
599
+ for (const property of [...firstProperties, ...secondProperties]) {
600
+ if (
601
+ !property?.name ||
602
+ property?.value === undefined ||
603
+ property?.value === null
604
+ ) {
605
+ continue;
606
+ }
607
+ if (
608
+ !mergedProperties.find(
609
+ (existingProperty) =>
610
+ existingProperty.name === property.name &&
611
+ existingProperty.value === property.value,
612
+ )
613
+ ) {
614
+ mergedProperties.push(property);
615
+ }
616
+ }
617
+ return mergedProperties;
618
+ }
619
+
620
+ export function isMergedHostViewBom(bomJson) {
621
+ return (
622
+ getPropertyValue(bomJson, "cdx:hostview:mode") === "hbom-obom-merged" ||
623
+ (isHbomLikeBom(bomJson) &&
624
+ (bomJson?.components || []).some((component) =>
625
+ getPropertyValue(component, "cdx:osquery:category"),
626
+ ))
627
+ );
628
+ }
629
+
630
+ export function getHostViewSummary(bomJson) {
631
+ return {
632
+ linkedHardwareComponentCount: Number.parseInt(
633
+ `${getPropertyValue(bomJson, "cdx:hostview:linkedHardwareComponentCount") || 0}`,
634
+ 10,
635
+ ),
636
+ linkedRuntimeCategories: getPropertyValues(
637
+ bomJson,
638
+ "cdx:hostview:linkedRuntimeCategory",
639
+ ),
640
+ mode: getPropertyValue(bomJson, "cdx:hostview:mode"),
641
+ runtimeAnchorCount: Number.parseInt(
642
+ `${getPropertyValue(bomJson, "cdx:hostview:runtimeAnchorCount") || 0}`,
643
+ 10,
644
+ ),
645
+ runtimeComponentCount: Number.parseInt(
646
+ `${getPropertyValue(bomJson, "cdx:hostview:runtimeComponentCount") || 0}`,
647
+ 10,
648
+ ),
649
+ topologyLinkCount: Number.parseInt(
650
+ `${getPropertyValue(bomJson, "cdx:hostview:topologyLinkCount") || 0}`,
651
+ 10,
652
+ ),
653
+ };
654
+ }
655
+
656
+ function ensureHostViewCategoryProperties(bomJson, linkedRuntimeCategories) {
657
+ for (const runtimeCategory of linkedRuntimeCategories || []) {
658
+ addHostViewSummaryProperty(
659
+ bomJson,
660
+ "cdx:hostview:linkedRuntimeCategory",
661
+ runtimeCategory,
662
+ );
663
+ }
664
+ }
665
+
666
+ export function applyHostInventoryTopology(bomJson) {
667
+ if (!bomJson || !isHbomLikeBom(bomJson)) {
668
+ return bomJson;
669
+ }
670
+
671
+ ensureBomRefs(bomJson);
672
+ removePropertiesByPrefix(bomJson, HOST_VIEW_PROPERTY_PREFIX);
673
+ removePropertiesByPrefix(
674
+ bomJson.metadata?.component,
675
+ HOST_VIEW_PROPERTY_PREFIX,
676
+ );
677
+
678
+ const runtimeComponents = (bomJson.components || []).filter((component) =>
679
+ getPropertyValue(component, "cdx:osquery:category"),
680
+ );
681
+ const hbomComponents = (bomJson.components || []).filter((component) =>
682
+ getHbomHardwareClass(component),
683
+ );
684
+ const runtimeAnchorRefs = (bomJson.components || [])
685
+ .filter(
686
+ (component) =>
687
+ component?.["bom-ref"] &&
688
+ OBOM_HOST_ANCHOR_TYPES.has(component.type) &&
689
+ getPropertyValue(component, "cdx:osquery:category") === undefined &&
690
+ getHbomHardwareClass(component) === undefined,
691
+ )
692
+ .map((component) => component["bom-ref"]);
693
+
694
+ const indexes = createRuntimeIndexes(runtimeComponents);
695
+ const hostRef = bomJson?.metadata?.component?.["bom-ref"];
696
+ const {
697
+ linkedHardwareComponentCount,
698
+ linkedRuntimeCategories,
699
+ newDependencies,
700
+ topologyLinkCount,
701
+ } = createDependencyEdgeList(
702
+ hostRef,
703
+ bomJson?.metadata?.component,
704
+ hbomComponents,
705
+ runtimeAnchorRefs,
706
+ indexes,
707
+ );
708
+
709
+ bomJson.dependencies = mergeDependencies(
710
+ bomJson.dependencies || [],
711
+ newDependencies,
712
+ );
713
+ addHostViewSummaryProperty(
714
+ bomJson,
715
+ "cdx:hostview:mode",
716
+ runtimeComponents.length ? "hbom-obom-merged" : "hbom-topology",
717
+ );
718
+ addHostViewSummaryProperty(
719
+ bomJson,
720
+ "cdx:hostview:hardwareComponentCount",
721
+ `${hbomComponents.length}`,
722
+ );
723
+ addHostViewSummaryProperty(
724
+ bomJson,
725
+ "cdx:hostview:runtimeComponentCount",
726
+ `${runtimeComponents.length}`,
727
+ );
728
+ addHostViewSummaryProperty(
729
+ bomJson,
730
+ "cdx:hostview:runtimeAnchorCount",
731
+ `${runtimeAnchorRefs.length}`,
732
+ );
733
+ addHostViewSummaryProperty(
734
+ bomJson,
735
+ "cdx:hostview:linkedHardwareComponentCount",
736
+ `${linkedHardwareComponentCount}`,
737
+ );
738
+ addHostViewSummaryProperty(
739
+ bomJson,
740
+ "cdx:hostview:topologyLinkCount",
741
+ `${topologyLinkCount}`,
742
+ );
743
+ ensureHostViewCategoryProperties(bomJson, linkedRuntimeCategories);
744
+ return bomJson;
745
+ }
746
+
747
+ export function mergeHostInventoryBoms(hbomJson, obomData) {
748
+ if (!hbomJson) {
749
+ return hbomJson;
750
+ }
751
+ if (!obomData?.bomJson) {
752
+ return applyHostInventoryTopology(hbomJson);
753
+ }
754
+
755
+ const runtimeComponents = [...(obomData.bomJson.components || [])];
756
+ if (obomData.parentComponent?.name && obomData.parentComponent?.type) {
757
+ runtimeComponents.unshift(obomData.parentComponent);
758
+ }
759
+ const mergedBomJson = {
760
+ ...hbomJson,
761
+ components: trimComponents([
762
+ ...(hbomJson.components || []),
763
+ ...runtimeComponents,
764
+ ]),
765
+ dependencies: mergeDependencies(
766
+ hbomJson.dependencies || [],
767
+ obomData.bomJson.dependencies || [],
768
+ ),
769
+ services: mergeServices(
770
+ hbomJson.services || [],
771
+ obomData.bomJson.services || [],
772
+ ),
773
+ metadata: {
774
+ ...hbomJson.metadata,
775
+ lifecycles: Array.from(
776
+ new Set([
777
+ ...(hbomJson.metadata?.lifecycles || []).map((entry) =>
778
+ JSON.stringify(entry),
779
+ ),
780
+ ...(obomData.bomJson.metadata?.lifecycles || []).map((entry) =>
781
+ JSON.stringify(entry),
782
+ ),
783
+ ]),
784
+ ).map((entry) => JSON.parse(entry)),
785
+ properties: mergeMetadataProperties(
786
+ hbomJson.metadata?.properties || [],
787
+ obomData.bomJson.metadata?.properties || [],
788
+ ),
789
+ tools: {
790
+ ...(hbomJson.metadata?.tools || {}),
791
+ components: mergeToolComponents(
792
+ hbomJson.metadata?.tools?.components || [],
793
+ obomData.bomJson.metadata?.tools?.components || [],
794
+ ),
795
+ },
796
+ },
797
+ properties: mergeMetadataProperties(
798
+ hbomJson.properties || [],
799
+ obomData.bomJson.properties || [],
800
+ ),
801
+ };
802
+ return applyHostInventoryTopology(mergedBomJson);
803
+ }