@cyclonedx/cdxgen 12.3.3 → 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 (157) hide show
  1. package/README.md +64 -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 +42 -18
  20. package/data/rules/container-risk.yaml +14 -7
  21. package/data/rules/dependency-sources.yaml +11 -0
  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 +14 -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 +506 -88
  38. package/lib/cli/index.poku.js +1352 -212
  39. package/lib/evinser/evinser.js +14 -9
  40. package/lib/helpers/analyzer.js +1406 -29
  41. package/lib/helpers/analyzer.poku.js +342 -0
  42. package/lib/helpers/analyzerScope.js +712 -0
  43. package/lib/helpers/asarutils.js +1556 -0
  44. package/lib/helpers/asarutils.poku.js +443 -0
  45. package/lib/helpers/auditCategories.js +12 -0
  46. package/lib/helpers/auditCategories.poku.js +32 -0
  47. package/lib/helpers/cbomutils.js +271 -1
  48. package/lib/helpers/cbomutils.poku.js +248 -5
  49. package/lib/helpers/display.js +291 -1
  50. package/lib/helpers/display.poku.js +149 -0
  51. package/lib/helpers/evidenceUtils.js +58 -0
  52. package/lib/helpers/evidenceUtils.poku.js +54 -0
  53. package/lib/helpers/exportUtils.js +9 -0
  54. package/lib/helpers/gtfobins.js +142 -8
  55. package/lib/helpers/gtfobins.poku.js +24 -1
  56. package/lib/helpers/hbom.js +710 -0
  57. package/lib/helpers/hbom.poku.js +496 -0
  58. package/lib/helpers/hbomAnalysis.js +268 -0
  59. package/lib/helpers/hbomAnalysis.poku.js +249 -0
  60. package/lib/helpers/hbomLoader.js +35 -0
  61. package/lib/helpers/hostTopology.js +803 -0
  62. package/lib/helpers/hostTopology.poku.js +363 -0
  63. package/lib/helpers/inventoryStats.js +69 -0
  64. package/lib/helpers/inventoryStats.poku.js +86 -0
  65. package/lib/helpers/lolbas.js +19 -1
  66. package/lib/helpers/lolbas.poku.js +23 -0
  67. package/lib/helpers/osqueryTransform.js +47 -0
  68. package/lib/helpers/osqueryTransform.poku.js +47 -0
  69. package/lib/helpers/plugins.js +349 -0
  70. package/lib/helpers/plugins.poku.js +57 -0
  71. package/lib/helpers/protobom.js +156 -45
  72. package/lib/helpers/protobom.poku.js +140 -5
  73. package/lib/helpers/remote/dependency-track.js +36 -3
  74. package/lib/helpers/remote/dependency-track.poku.js +44 -0
  75. package/lib/helpers/source.js +24 -0
  76. package/lib/helpers/source.poku.js +32 -0
  77. package/lib/helpers/utils.js +1438 -93
  78. package/lib/helpers/utils.poku.js +846 -4
  79. package/lib/managers/binary.e2e.poku.js +367 -0
  80. package/lib/managers/binary.js +2293 -353
  81. package/lib/managers/binary.poku.js +1699 -1
  82. package/lib/managers/docker.js +201 -79
  83. package/lib/managers/docker.poku.js +337 -12
  84. package/lib/server/server.js +2 -27
  85. package/lib/stages/postgen/annotator.js +38 -0
  86. package/lib/stages/postgen/annotator.poku.js +107 -1
  87. package/lib/stages/postgen/auditBom.js +121 -18
  88. package/lib/stages/postgen/auditBom.poku.js +1366 -31
  89. package/lib/stages/postgen/hostTopologyAudit.poku.js +186 -0
  90. package/lib/stages/postgen/postgen.js +192 -1
  91. package/lib/stages/postgen/postgen.poku.js +321 -0
  92. package/lib/stages/postgen/ruleEngine.js +116 -0
  93. package/lib/stages/pregen/envAudit.js +14 -3
  94. package/package.json +23 -21
  95. package/types/bin/hbom.d.ts +3 -0
  96. package/types/bin/hbom.d.ts.map +1 -0
  97. package/types/bin/repl.d.ts.map +1 -1
  98. package/types/lib/audit/index.d.ts +44 -0
  99. package/types/lib/audit/index.d.ts.map +1 -1
  100. package/types/lib/audit/reporters.d.ts +16 -0
  101. package/types/lib/audit/reporters.d.ts.map +1 -1
  102. package/types/lib/audit/targets.d.ts.map +1 -1
  103. package/types/lib/cli/index.d.ts +16 -0
  104. package/types/lib/cli/index.d.ts.map +1 -1
  105. package/types/lib/evinser/evinser.d.ts +4 -0
  106. package/types/lib/evinser/evinser.d.ts.map +1 -1
  107. package/types/lib/helpers/analyzer.d.ts +33 -0
  108. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  109. package/types/lib/helpers/analyzerScope.d.ts +11 -0
  110. package/types/lib/helpers/analyzerScope.d.ts.map +1 -0
  111. package/types/lib/helpers/asarutils.d.ts +34 -0
  112. package/types/lib/helpers/asarutils.d.ts.map +1 -0
  113. package/types/lib/helpers/auditCategories.d.ts +5 -0
  114. package/types/lib/helpers/auditCategories.d.ts.map +1 -1
  115. package/types/lib/helpers/cbomutils.d.ts +3 -2
  116. package/types/lib/helpers/cbomutils.d.ts.map +1 -1
  117. package/types/lib/helpers/display.d.ts.map +1 -1
  118. package/types/lib/helpers/evidenceUtils.d.ts +8 -0
  119. package/types/lib/helpers/evidenceUtils.d.ts.map +1 -0
  120. package/types/lib/helpers/exportUtils.d.ts.map +1 -1
  121. package/types/lib/helpers/gtfobins.d.ts +8 -0
  122. package/types/lib/helpers/gtfobins.d.ts.map +1 -1
  123. package/types/lib/helpers/hbom.d.ts +49 -0
  124. package/types/lib/helpers/hbom.d.ts.map +1 -0
  125. package/types/lib/helpers/hbomAnalysis.d.ts +62 -0
  126. package/types/lib/helpers/hbomAnalysis.d.ts.map +1 -0
  127. package/types/lib/helpers/hbomLoader.d.ts +7 -0
  128. package/types/lib/helpers/hbomLoader.d.ts.map +1 -0
  129. package/types/lib/helpers/hostTopology.d.ts +12 -0
  130. package/types/lib/helpers/hostTopology.d.ts.map +1 -0
  131. package/types/lib/helpers/inventoryStats.d.ts +11 -0
  132. package/types/lib/helpers/inventoryStats.d.ts.map +1 -0
  133. package/types/lib/helpers/lolbas.d.ts.map +1 -1
  134. package/types/lib/helpers/osqueryTransform.d.ts +3 -0
  135. package/types/lib/helpers/osqueryTransform.d.ts.map +1 -1
  136. package/types/lib/helpers/plugins.d.ts +58 -0
  137. package/types/lib/helpers/plugins.d.ts.map +1 -0
  138. package/types/lib/helpers/protobom.d.ts +3 -4
  139. package/types/lib/helpers/protobom.d.ts.map +1 -1
  140. package/types/lib/helpers/remote/dependency-track.d.ts +10 -3
  141. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
  142. package/types/lib/helpers/source.d.ts.map +1 -1
  143. package/types/lib/helpers/utils.d.ts +45 -8
  144. package/types/lib/helpers/utils.d.ts.map +1 -1
  145. package/types/lib/managers/binary.d.ts +5 -0
  146. package/types/lib/managers/binary.d.ts.map +1 -1
  147. package/types/lib/managers/docker.d.ts.map +1 -1
  148. package/types/lib/server/server.d.ts +2 -1
  149. package/types/lib/server/server.d.ts.map +1 -1
  150. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  151. package/types/lib/stages/postgen/auditBom.d.ts +26 -1
  152. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  153. package/types/lib/stages/postgen/postgen.d.ts +2 -1
  154. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  155. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  156. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  157. package/data/spdx-model-v3.0.1.jsonld +0 -15999
@@ -0,0 +1,328 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import process from "node:process";
6
+
7
+ import { assert, describe, it } from "poku";
8
+
9
+ import {
10
+ createAsarFixture,
11
+ writeElectronAsarIntegrityPlist,
12
+ } from "../../test/helpers/asar-fixture-builder.js";
13
+ import { readAsarArchiveHeaderSync } from "../helpers/asarutils.js";
14
+ import { auditBom } from "../stages/postgen/auditBom.js";
15
+ import { postProcess } from "../stages/postgen/postgen.js";
16
+ import { validateBom } from "../validator/bomValidator.js";
17
+ import { createAsarBom } from "./index.js";
18
+
19
+ function getProp(obj, name) {
20
+ return obj?.properties?.find((property) => property.name === name)?.value;
21
+ }
22
+
23
+ if (process.platform !== "win32") {
24
+ describe("createAsarBom()", () => {
25
+ it("catalogs ASAR archives, extracts nested npm metadata, and surfaces audit findings", async () => {
26
+ const fixtureRoot = mkdtempSync(join(tmpdir(), "cdxgen-asar-cli-"));
27
+ const archivePath = join(fixtureRoot, "app.asar");
28
+ createAsarFixture(archivePath, {
29
+ corruptIntegrityPaths: ["config/settings.json"],
30
+ executablePaths: ["scripts/postinstall.js"],
31
+ unpackedPaths: ["native/addon.node"],
32
+ });
33
+ try {
34
+ const bomData = await createAsarBom(archivePath, {
35
+ installDeps: false,
36
+ multiProject: false,
37
+ projectType: ["asar"],
38
+ specVersion: 1.7,
39
+ });
40
+ assert.ok(bomData?.bomJson?.components?.length);
41
+ assert.strictEqual(bomData.parentComponent.name, "Sample Electron App");
42
+ assert.strictEqual(
43
+ getProp(bomData.parentComponent, "cdx:asar:hasEval"),
44
+ "true",
45
+ );
46
+ assert.strictEqual(
47
+ getProp(bomData.parentComponent, "cdx:asar:hasDynamicFetch"),
48
+ "true",
49
+ );
50
+ assert.strictEqual(
51
+ getProp(bomData.parentComponent, "cdx:asar:hasNativeAddons"),
52
+ "true",
53
+ );
54
+ const mainFileComponent = bomData.bomJson.components.find(
55
+ (component) => getProp(component, "cdx:asar:path") === "src/main.js",
56
+ );
57
+ assert.ok(mainFileComponent, "expected src/main.js component");
58
+ assert.strictEqual(
59
+ getProp(mainFileComponent, "cdx:asar:js:capability:network"),
60
+ "true",
61
+ );
62
+ const sketchyAddon = bomData.bomJson.components.find(
63
+ (component) => component.name === "sketchy-addon",
64
+ );
65
+ assert.ok(sketchyAddon, "expected extracted npm component");
66
+ assert.ok(
67
+ String(getProp(sketchyAddon, "SrcFile") || "").includes(
68
+ `${archivePath}#/`,
69
+ ),
70
+ );
71
+
72
+ const postProcessed = postProcess(bomData, {
73
+ bomAudit: true,
74
+ bomAuditCategories: ["asar-archive"],
75
+ installDeps: false,
76
+ projectType: ["asar"],
77
+ specVersion: 1.7,
78
+ });
79
+ const findings = await auditBom(postProcessed.bomJson, {
80
+ bomAuditCategories: ["asar-archive"],
81
+ });
82
+ assert.ok(
83
+ findings.some((finding) => finding.ruleId === "ASAR-001"),
84
+ "expected ASAR eval/dynamic execution finding",
85
+ );
86
+ assert.ok(
87
+ findings.some((finding) => finding.ruleId === "ASAR-004"),
88
+ "expected embedded npm install-script finding",
89
+ );
90
+ } finally {
91
+ rmSync(fixtureRoot, { force: true, recursive: true });
92
+ }
93
+ });
94
+
95
+ it("scans directories containing multiple ASAR archives", async () => {
96
+ const fixtureRoot = mkdtempSync(join(tmpdir(), "cdxgen-asar-dir-"));
97
+ const firstArchivePath = join(fixtureRoot, "app-one.asar");
98
+ const secondArchivePath = join(fixtureRoot, "nested", "app-two.asar");
99
+ mkdirSync(join(fixtureRoot, "nested"), { recursive: true });
100
+ createAsarFixture(firstArchivePath, {
101
+ extraEntries: {
102
+ "src/one.js": { content: "export const one = 1;\n" },
103
+ },
104
+ });
105
+ createAsarFixture(secondArchivePath, {
106
+ extraEntries: {
107
+ "package.json": {
108
+ content: JSON.stringify({
109
+ name: "sample-electron-app-two",
110
+ version: "2.0.0",
111
+ main: "src/two.js",
112
+ }),
113
+ },
114
+ "src/two.js": { content: "export const two = 2;\n" },
115
+ },
116
+ });
117
+ try {
118
+ const bomData = await createAsarBom(fixtureRoot, {
119
+ installDeps: false,
120
+ multiProject: true,
121
+ projectType: ["asar"],
122
+ specVersion: 1.7,
123
+ });
124
+ const archiveComponents = (bomData.bomJson?.components || []).filter(
125
+ (component) => getProp(component, "cdx:file:kind") === "asar-archive",
126
+ );
127
+ assert.strictEqual(archiveComponents.length, 2);
128
+ } finally {
129
+ rmSync(fixtureRoot, { force: true, recursive: true });
130
+ }
131
+ });
132
+
133
+ it("keeps distinct nested ASAR archives with different virtual paths", async () => {
134
+ const fixtureRoot = mkdtempSync(join(tmpdir(), "cdxgen-asar-case-"));
135
+ const outerArchivePath = join(fixtureRoot, "outer.asar");
136
+ const firstNestedArchivePath = join(fixtureRoot, "first-nested.asar");
137
+ const secondNestedArchivePath = join(fixtureRoot, "second-nested.asar");
138
+ createAsarFixture(firstNestedArchivePath);
139
+ createAsarFixture(secondNestedArchivePath, {
140
+ extraEntries: {
141
+ "package.json": {
142
+ content: JSON.stringify({
143
+ name: "sample-electron-app-upper",
144
+ version: "4.5.6",
145
+ main: "src/main.js",
146
+ }),
147
+ },
148
+ },
149
+ });
150
+ createAsarFixture(outerArchivePath, {
151
+ extraEntries: {
152
+ "nested/first/core.asar": {
153
+ content: readFileSync(firstNestedArchivePath),
154
+ },
155
+ "nested/second/core.asar": {
156
+ content: readFileSync(secondNestedArchivePath),
157
+ },
158
+ },
159
+ });
160
+ try {
161
+ const bomData = await createAsarBom(outerArchivePath, {
162
+ installDeps: false,
163
+ multiProject: false,
164
+ projectType: ["asar"],
165
+ specVersion: 1.7,
166
+ });
167
+ const nestedArchiveComponents = (
168
+ bomData.bomJson?.components || []
169
+ ).filter(
170
+ (component) =>
171
+ getProp(component, "cdx:file:kind") === "asar-archive" &&
172
+ String(getProp(component, "SrcFile") || "").startsWith(
173
+ `${outerArchivePath}#/nested/`,
174
+ ),
175
+ );
176
+ assert.strictEqual(nestedArchiveComponents.length, 2);
177
+ assert.ok(
178
+ nestedArchiveComponents.some(
179
+ (component) =>
180
+ getProp(component, "SrcFile") ===
181
+ `${outerArchivePath}#/nested/first/core.asar`,
182
+ ),
183
+ );
184
+ assert.ok(
185
+ nestedArchiveComponents.some(
186
+ (component) =>
187
+ getProp(component, "SrcFile") ===
188
+ `${outerArchivePath}#/nested/second/core.asar`,
189
+ ),
190
+ );
191
+ } finally {
192
+ rmSync(fixtureRoot, { force: true, recursive: true });
193
+ }
194
+ });
195
+
196
+ it("recursively scans nested ASAR archives and rewrites nested evidence paths", async () => {
197
+ const fixtureRoot = mkdtempSync(join(tmpdir(), "cdxgen-asar-nested-"));
198
+ const archivePath = join(fixtureRoot, "outer.asar");
199
+ const nestedArchivePath = join(fixtureRoot, "inner.asar");
200
+ createAsarFixture(nestedArchivePath, {
201
+ extraEntries: {
202
+ "node_modules/inner-addon/package.json": {
203
+ content: JSON.stringify({
204
+ name: "inner-addon",
205
+ version: "1.0.0",
206
+ }),
207
+ },
208
+ "package-lock.json": {
209
+ content: JSON.stringify({
210
+ lockfileVersion: 3,
211
+ name: "inner-electron-app",
212
+ packages: {
213
+ "": {
214
+ dependencies: {
215
+ "inner-addon": "1.0.0",
216
+ },
217
+ name: "inner-electron-app",
218
+ version: "9.9.9",
219
+ },
220
+ "node_modules/inner-addon": {
221
+ name: "inner-addon",
222
+ version: "1.0.0",
223
+ },
224
+ },
225
+ }),
226
+ },
227
+ "package.json": {
228
+ content: JSON.stringify({
229
+ dependencies: {
230
+ "inner-addon": "1.0.0",
231
+ },
232
+ name: "inner-electron-app",
233
+ version: "9.9.9",
234
+ main: "src/main.js",
235
+ }),
236
+ },
237
+ },
238
+ });
239
+ createAsarFixture(archivePath, {
240
+ extraEntries: {
241
+ "nested/core.asar": {
242
+ content: readFileSync(nestedArchivePath),
243
+ },
244
+ },
245
+ });
246
+ try {
247
+ const bomData = await createAsarBom(archivePath, {
248
+ installDeps: false,
249
+ multiProject: false,
250
+ projectType: ["asar"],
251
+ specVersion: 1.7,
252
+ });
253
+ const nestedArchiveComponent = bomData.bomJson.components.find(
254
+ (component) =>
255
+ getProp(component, "cdx:file:kind") === "asar-archive" &&
256
+ getProp(component, "SrcFile") ===
257
+ `${archivePath}#/nested/core.asar`,
258
+ );
259
+ const nestedMainFileComponent = bomData.bomJson.components.find(
260
+ (component) =>
261
+ getProp(component, "cdx:asar:path") === "src/main.js" &&
262
+ component.evidence?.occurrences?.some(
263
+ (occurrence) =>
264
+ occurrence.location ===
265
+ `${archivePath}#/nested/core.asar#/src/main.js`,
266
+ ),
267
+ );
268
+ const nestedNpmComponent = bomData.bomJson.components.find(
269
+ (component) =>
270
+ component.name === "inner-addon" &&
271
+ String(getProp(component, "SrcFile") || "").startsWith(
272
+ `${archivePath}#/nested/core.asar#/`,
273
+ ),
274
+ );
275
+ assert.ok(nestedArchiveComponent, "expected nested archive component");
276
+ assert.ok(
277
+ nestedMainFileComponent,
278
+ "expected nested archive file inventory component",
279
+ );
280
+ assert.ok(nestedNpmComponent, "expected nested archive npm component");
281
+ } finally {
282
+ rmSync(fixtureRoot, { force: true, recursive: true });
283
+ }
284
+ });
285
+
286
+ it("produces a schema-valid BOM with ASAR signing crypto components", async () => {
287
+ const fixtureRoot = mkdtempSync(join(tmpdir(), "cdxgen-asar-signed-"));
288
+ const appDir = join(fixtureRoot, "Signed.app");
289
+ const archivePath = join(appDir, "Contents", "Resources", "app.asar");
290
+ mkdirSync(join(appDir, "Contents", "Resources"), { recursive: true });
291
+ createAsarFixture(archivePath);
292
+ const { headerString } = readAsarArchiveHeaderSync(archivePath);
293
+ const headerHash = createHash("sha256")
294
+ .update(headerString, "utf8")
295
+ .digest("hex");
296
+ writeElectronAsarIntegrityPlist(join(appDir, "Contents", "Info.plist"), {
297
+ "Resources/app.asar": {
298
+ algorithm: "SHA256",
299
+ hash: headerHash,
300
+ },
301
+ });
302
+ try {
303
+ const bomData = await createAsarBom(archivePath, {
304
+ installDeps: false,
305
+ multiProject: false,
306
+ projectType: ["asar"],
307
+ specVersion: 1.7,
308
+ });
309
+ const postProcessed = postProcess(bomData, {
310
+ installDeps: false,
311
+ projectType: ["asar"],
312
+ specVersion: 1.7,
313
+ });
314
+ assert.strictEqual(validateBom(postProcessed.bomJson), true);
315
+ assert.ok(
316
+ postProcessed.bomJson.components.some(
317
+ (component) =>
318
+ component.type === "cryptographic-asset" &&
319
+ getProp(component, "cdx:asar:signingVerified") === "true",
320
+ ),
321
+ "expected a verified ASAR signing crypto component",
322
+ );
323
+ } finally {
324
+ rmSync(fixtureRoot, { force: true, recursive: true });
325
+ }
326
+ });
327
+ });
328
+ }