fad-checker 2.0.0 → 2.0.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.
- package/CHANGELOG.md +15 -0
- package/README.md +58 -10
- package/data/eol-mapping.json +5 -5
- package/fad-checker.js +289 -203
- package/lib/codecs/npm/collect.js +26 -17
- package/lib/codecs/npm/parse.js +114 -10
- package/lib/codecs/npm/registry.js +4 -3
- package/lib/codecs/npm.codec.js +1 -1
- package/lib/codecs/nuget.codec.js +4 -2
- package/lib/codecs/pypi/parse.js +104 -12
- package/lib/codecs/pypi.codec.js +27 -6
- package/lib/core.js +35 -1
- package/lib/cve-match.js +15 -5
- package/lib/deps-descriptor.js +110 -0
- package/lib/nvd.js +4 -3
- package/lib/osv.js +7 -4
- package/lib/outdated.js +7 -2
- package/lib/retire.js +77 -13
- package/lib/transitive.js +3 -3
- package/lib/ui.js +87 -0
- package/package.json +3 -2
- package/CLAUDE.md +0 -147
- package/docs/ARCHITECTURE.md +0 -182
- package/docs/USAGE.md +0 -187
- package/docs/superpowers/plans/2026-05-29-codec-composer.md +0 -556
- package/docs/superpowers/plans/2026-05-29-codec-foundation.md +0 -851
- package/docs/superpowers/plans/2026-05-29-codec-nuget.md +0 -432
- package/docs/superpowers/plans/2026-05-29-codec-pypi.md +0 -450
- package/docs/superpowers/specs/2026-05-29-codecs-multi-ecosystem-design.md +0 -251
- package/test/cli-ecosystem.test.js +0 -30
- package/test/codec-edge-cases.test.js +0 -131
- package/test/codec-integration.test.js +0 -119
- package/test/codecs.test.js +0 -92
- package/test/composer.test.js +0 -71
- package/test/core.test.js +0 -153
- package/test/cpe.test.js +0 -166
- package/test/cve-download.test.js +0 -39
- package/test/cve-match.test.js +0 -217
- package/test/cve-report.test.js +0 -180
- package/test/dep-record.test.js +0 -31
- package/test/fixtures/complex-enterprise/api/pom.xml +0 -32
- package/test/fixtures/complex-enterprise/build/pom.xml +0 -46
- package/test/fixtures/complex-enterprise/dao/pom.xml +0 -37
- package/test/fixtures/complex-enterprise/pom.xml +0 -66
- package/test/fixtures/complex-enterprise/web/pom.xml +0 -77
- package/test/fixtures/csharp-config/packages.config +0 -4
- package/test/fixtures/csharp-csproj/Directory.Packages.props +0 -5
- package/test/fixtures/csharp-csproj/app.csproj +0 -7
- package/test/fixtures/csharp-lock/packages.lock.json +0 -6
- package/test/fixtures/cve-samples/cve-non-java.json +0 -19
- package/test/fixtures/cve-samples/cve-product-only.json +0 -31
- package/test/fixtures/cve-samples/cve-with-packagename.json +0 -37
- package/test/fixtures/cve-samples/nvd-log4shell.json +0 -40
- package/test/fixtures/cve-samples/nvd-npm-lodash.json +0 -22
- package/test/fixtures/monorepo-mixed/libs/common-bom/pom.xml +0 -26
- package/test/fixtures/monorepo-mixed/packages/cli/package.json +0 -14
- package/test/fixtures/monorepo-mixed/packages/cli/yarn.lock +0 -41
- package/test/fixtures/monorepo-mixed/packages/no-lock/package.json +0 -10
- package/test/fixtures/monorepo-mixed/packages/web-app/package-lock.json +0 -71
- package/test/fixtures/monorepo-mixed/packages/web-app/package.json +0 -17
- package/test/fixtures/monorepo-mixed/pom.xml +0 -29
- package/test/fixtures/monorepo-mixed/services/api/pom.xml +0 -27
- package/test/fixtures/monorepo-mixed/services/worker/pom.xml +0 -28
- package/test/fixtures/php-app/composer.json +0 -5
- package/test/fixtures/php-app/composer.lock +0 -10
- package/test/fixtures/polyglot/cs/packages.lock.json +0 -1
- package/test/fixtures/polyglot/js/package-lock.json +0 -1
- package/test/fixtures/polyglot/js/package.json +0 -1
- package/test/fixtures/polyglot/mvn/pom.xml +0 -7
- package/test/fixtures/polyglot/php/composer.lock +0 -1
- package/test/fixtures/polyglot/py/poetry.lock +0 -3
- package/test/fixtures/private-lib-detection/core/pom.xml +0 -26
- package/test/fixtures/private-lib-detection/plugin/pom.xml +0 -23
- package/test/fixtures/private-lib-detection/pom.xml +0 -35
- package/test/fixtures/python-pipenv/Pipfile.lock +0 -1
- package/test/fixtures/python-poetry/poetry.lock +0 -7
- package/test/fixtures/python-reqs/requirements.txt +0 -5
- package/test/fixtures/python-uv/uv.lock +0 -3
- package/test/fixtures/simple/app/pom.xml +0 -28
- package/test/fixtures/simple/lib/pom.xml +0 -18
- package/test/fixtures/simple/pom.xml +0 -24
- package/test/maven-repo.test.js +0 -111
- package/test/maven-version.test.js +0 -57
- package/test/monorepo.test.js +0 -132
- package/test/npm-registry.test.js +0 -64
- package/test/npm.test.js +0 -150
- package/test/nuget.test.js +0 -66
- package/test/osv.test.js +0 -62
- package/test/outdated.test.js +0 -101
- package/test/pypi.test.js +0 -72
- package/test/snyk.test.js +0 -64
- package/test/transitive.test.js +0 -305
- package/test/webjar.test.js +0 -33
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
const { test } = require("node:test");
|
|
2
|
-
const assert = require("node:assert/strict");
|
|
3
|
-
const { parseMavenVersion, compareMavenVersions, isVersionAffected, parseRange } = require("../lib/maven-version");
|
|
4
|
-
|
|
5
|
-
test("parseMavenVersion returns segments", () => {
|
|
6
|
-
const v = parseMavenVersion("2.14.0");
|
|
7
|
-
assert.equal(v.original, "2.14.0");
|
|
8
|
-
assert.deepEqual(v.segments.map(s => s.value), [2, 14, 0]);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
test("compareMavenVersions basic numeric ordering", () => {
|
|
12
|
-
assert.equal(compareMavenVersions("1.0.0", "1.0.0"), 0);
|
|
13
|
-
assert.equal(compareMavenVersions("1.0.0", "1.0.1"), -1);
|
|
14
|
-
assert.equal(compareMavenVersions("2.0.0", "1.9.9"), 1);
|
|
15
|
-
assert.equal(compareMavenVersions("1.10.0", "1.9.0"), 1);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test("compareMavenVersions handles qualifiers", () => {
|
|
19
|
-
assert.equal(compareMavenVersions("1.0.0-SNAPSHOT", "1.0.0"), -1);
|
|
20
|
-
assert.equal(compareMavenVersions("1.0.0-rc1", "1.0.0"), -1);
|
|
21
|
-
assert.equal(compareMavenVersions("1.0.0-alpha", "1.0.0-beta"), -1);
|
|
22
|
-
assert.equal(compareMavenVersions("5.3.20.Final", "5.3.20"), 0);
|
|
23
|
-
assert.equal(compareMavenVersions("5.3.20.RELEASE", "5.3.20.Final"), 0);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("isVersionAffected respects [version, lessThan)", () => {
|
|
27
|
-
const spec = { version: "2.0", status: "affected", lessThan: "2.15.0" };
|
|
28
|
-
assert.equal(isVersionAffected("2.14.0", spec), true);
|
|
29
|
-
assert.equal(isVersionAffected("2.15.0", spec), false, "lessThan is exclusive");
|
|
30
|
-
assert.equal(isVersionAffected("1.5.0", spec), false, "below lower bound");
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test("isVersionAffected with lessThanOrEqual is inclusive", () => {
|
|
34
|
-
const spec = { version: "1.0", status: "affected", lessThanOrEqual: "1.5.0" };
|
|
35
|
-
assert.equal(isVersionAffected("1.5.0", spec), true);
|
|
36
|
-
assert.equal(isVersionAffected("1.5.1", spec), false);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
test("isVersionAffected returns false when status != affected", () => {
|
|
40
|
-
const spec = { version: "1.0", lessThan: "2.0", status: "unaffected" };
|
|
41
|
-
assert.equal(isVersionAffected("1.5", spec), false);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("isVersionAffected fail-closed when spec has no version bounds (H1)", () => {
|
|
45
|
-
// CVEProject sometimes emits {status:"affected"} stubs with no version
|
|
46
|
-
// fields. The matcher must NOT fall through to `return true` — that was
|
|
47
|
-
// the H1 cascade.
|
|
48
|
-
assert.equal(isVersionAffected("2.14.0", { status: "affected" }), false);
|
|
49
|
-
assert.equal(isVersionAffected("2.14.0", {}), false);
|
|
50
|
-
assert.equal(isVersionAffected("0.0.1", { status: "affected" }), false);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test("parseRange handles Maven range syntax", () => {
|
|
54
|
-
assert.deepEqual(parseRange("1.2.3"), { exact: "1.2.3" });
|
|
55
|
-
assert.deepEqual(parseRange("[1.0,2.0)"), { lower: "1.0", lowerInclusive: true, upper: "2.0", upperInclusive: false });
|
|
56
|
-
assert.deepEqual(parseRange("(,1.5]"), { lower: null, lowerInclusive: false, upper: "1.5", upperInclusive: true });
|
|
57
|
-
});
|
package/test/monorepo.test.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* End-to-end test against fixtures/monorepo-mixed:
|
|
3
|
-
* - 1 parent pom + 1 BOM module + 2 Maven submodules
|
|
4
|
-
* - 1 npm package (package-lock v3) with prod/dev/peer deps
|
|
5
|
-
* - 1 yarn-v1 package with prod/dev deps + a private @acme/* dep
|
|
6
|
-
*
|
|
7
|
-
* Drives the same code paths as `fad-checker --report` minus the network calls.
|
|
8
|
-
*/
|
|
9
|
-
const { test } = require("node:test");
|
|
10
|
-
const assert = require("node:assert/strict");
|
|
11
|
-
const path = require("path");
|
|
12
|
-
const core = require("../lib/core");
|
|
13
|
-
const { collectResolvedDeps } = require("../lib/cve-match");
|
|
14
|
-
const { collectNpmDeps, hasJsManifests } = require("../lib/codecs/npm/collect");
|
|
15
|
-
|
|
16
|
-
const FIX = path.join(__dirname, "fixtures", "monorepo-mixed");
|
|
17
|
-
|
|
18
|
-
async function loadMavenTree(src) {
|
|
19
|
-
const pomFiles = core.findPomFiles(src);
|
|
20
|
-
const meta = core.newMetadataStore();
|
|
21
|
-
const propsByPom = {};
|
|
22
|
-
for (const pom of pomFiles) await core.parsePom(pom, meta);
|
|
23
|
-
for (const pom of Object.keys(meta.byPath)) {
|
|
24
|
-
await core.getAllInheritedProps(pom, meta, propsByPom);
|
|
25
|
-
}
|
|
26
|
-
return { meta, propsByPom, pomFiles };
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
test("hasJsManifests detects the JS packages under the monorepo", () => {
|
|
30
|
-
assert.equal(hasJsManifests(FIX), true);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test("Maven side: parses parent + BOM + submodules", async () => {
|
|
34
|
-
const { meta, pomFiles } = await loadMavenTree(FIX);
|
|
35
|
-
assert.equal(pomFiles.length, 4); // root + bom + api + worker
|
|
36
|
-
assert.ok(meta.byId["com.acme:monorepo-parent"]);
|
|
37
|
-
assert.ok(meta.byId["com.acme:common-bom"]);
|
|
38
|
-
assert.ok(meta.byId["com.acme:api"]);
|
|
39
|
-
assert.ok(meta.byId["com.acme:worker"]);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
test("Maven side: BOM-imported versions surface in api's resolved deps", async () => {
|
|
43
|
-
const { meta, propsByPom } = await loadMavenTree(FIX);
|
|
44
|
-
const resolved = collectResolvedDeps(meta, propsByPom, {});
|
|
45
|
-
const databind = resolved.get("com.fasterxml.jackson.core:jackson-databind");
|
|
46
|
-
assert.ok(databind, "jackson-databind should be resolved via BOM");
|
|
47
|
-
// Version arrives through ${jackson.version} property defined on the root parent
|
|
48
|
-
assert.equal(databind.version, "2.9.10");
|
|
49
|
-
assert.equal(databind.ecosystem, "maven");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("Maven side: exclude regex strips com.acme.private but keeps public deps", async () => {
|
|
53
|
-
const { meta, propsByPom } = await loadMavenTree(FIX);
|
|
54
|
-
const resolved = collectResolvedDeps(meta, propsByPom, {
|
|
55
|
-
deps2Exclude: /^com\.acme\.private/,
|
|
56
|
-
});
|
|
57
|
-
assert.equal(resolved.has("com.acme.private:internal-auth"), false);
|
|
58
|
-
assert.ok(resolved.has("com.fasterxml.jackson.core:jackson-databind"));
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
test("Maven side: --ignore-test drops junit test scope", async () => {
|
|
62
|
-
const { meta, propsByPom } = await loadMavenTree(FIX);
|
|
63
|
-
const withTest = collectResolvedDeps(meta, propsByPom, {});
|
|
64
|
-
const noTest = collectResolvedDeps(meta, propsByPom, { ignoreTest: true });
|
|
65
|
-
assert.ok(withTest.has("junit:junit"));
|
|
66
|
-
assert.equal(noTest.has("junit:junit"), false);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
test("JS side: both packages discovered, namespaced keys, no Maven collision", () => {
|
|
70
|
-
const npm = collectNpmDeps(FIX, {});
|
|
71
|
-
assert.ok(npm.has("npm:axios"));
|
|
72
|
-
assert.ok(npm.has("npm:chalk"));
|
|
73
|
-
assert.ok(npm.has("npm:@acme/private-utils"));
|
|
74
|
-
// No accidental collision with Maven g:a keys
|
|
75
|
-
for (const key of npm.keys()) assert.ok(key.startsWith("npm:"), `${key} should be namespaced`);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("Combined: merged Map preserves both ecosystems and is queryable as one", async () => {
|
|
79
|
-
const { meta, propsByPom } = await loadMavenTree(FIX);
|
|
80
|
-
const combined = collectResolvedDeps(meta, propsByPom, {});
|
|
81
|
-
const npm = collectNpmDeps(FIX, {});
|
|
82
|
-
for (const [k, v] of npm) combined.set(k, v);
|
|
83
|
-
|
|
84
|
-
// Maven and npm coords coexist
|
|
85
|
-
assert.ok(combined.get("com.fasterxml.jackson.core:jackson-databind"));
|
|
86
|
-
assert.ok(combined.get("npm:axios"));
|
|
87
|
-
// Distinct ecosystems
|
|
88
|
-
assert.equal(combined.get("com.fasterxml.jackson.core:jackson-databind").ecosystem, "maven");
|
|
89
|
-
assert.equal(combined.get("npm:axios").ecosystem, "npm");
|
|
90
|
-
// Total deps from both sides
|
|
91
|
-
assert.ok(combined.size >= 10, `expected ≥10 combined deps, got ${combined.size}`);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test("Combined: shared exclusion regex applies per-ecosystem appropriately", async () => {
|
|
95
|
-
// Maven private prefix
|
|
96
|
-
const { meta, propsByPom } = await loadMavenTree(FIX);
|
|
97
|
-
const mvn = collectResolvedDeps(meta, propsByPom, { deps2Exclude: /^com\.acme\.private/ });
|
|
98
|
-
assert.equal(mvn.has("com.acme.private:internal-auth"), false);
|
|
99
|
-
|
|
100
|
-
// npm uses a different convention (@scope) — own regex matches it
|
|
101
|
-
const npm = collectNpmDeps(FIX, { deps2Exclude: /^@acme\// });
|
|
102
|
-
assert.equal(npm.has("npm:@acme/private-utils"), false);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test("Combined: a sample CVE match flow runs without throwing on a mixed Map", async () => {
|
|
106
|
-
const { matchDepsAgainstCves } = require("../lib/cve-match");
|
|
107
|
-
const { meta, propsByPom } = await loadMavenTree(FIX);
|
|
108
|
-
const combined = collectResolvedDeps(meta, propsByPom, {});
|
|
109
|
-
const npm = collectNpmDeps(FIX, {});
|
|
110
|
-
for (const [k, v] of npm) combined.set(k, v);
|
|
111
|
-
|
|
112
|
-
// Tiny stub CVE index. Only Maven-relevant — npm should be silently skipped by matchDepsAgainstCves.
|
|
113
|
-
const idx = {
|
|
114
|
-
byPackageName: {
|
|
115
|
-
"org.apache.logging.log4j:log4j-core": [{
|
|
116
|
-
id: "CVE-2021-44228",
|
|
117
|
-
severity: "CRITICAL",
|
|
118
|
-
score: 10.0,
|
|
119
|
-
description: "Log4Shell",
|
|
120
|
-
fixVersion: "2.15.0",
|
|
121
|
-
ranges: [{ status: "affected", version: "2.0.0", lessThan: "2.15.0", versionType: "maven" }],
|
|
122
|
-
vendor: "apache",
|
|
123
|
-
product: "log4j-core",
|
|
124
|
-
}],
|
|
125
|
-
},
|
|
126
|
-
byProduct: {},
|
|
127
|
-
};
|
|
128
|
-
const matches = matchDepsAgainstCves(combined, idx);
|
|
129
|
-
assert.ok(matches.length >= 1);
|
|
130
|
-
const log4shell = matches.find(m => m.cve.id === "CVE-2021-44228");
|
|
131
|
-
assert.ok(log4shell, "log4-core 2.14.0 should match CVE-2021-44228");
|
|
132
|
-
});
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
const { test } = require("node:test");
|
|
2
|
-
const assert = require("node:assert/strict");
|
|
3
|
-
const { packumentToFindings } = require("../lib/codecs/npm/registry");
|
|
4
|
-
|
|
5
|
-
const dep = (name, version) => ({ ecosystem: "npm", groupId: "", artifactId: name, version });
|
|
6
|
-
|
|
7
|
-
test("packumentToFindings flags a deprecated resolved version", () => {
|
|
8
|
-
const packument = {
|
|
9
|
-
name: "request",
|
|
10
|
-
"dist-tags": { latest: "2.88.2" },
|
|
11
|
-
versions: {
|
|
12
|
-
"2.88.2": { deprecated: "request has been deprecated, see https://github.com/request/request/issues/3142" },
|
|
13
|
-
},
|
|
14
|
-
time: { "2.88.2": "2020-02-11T00:00:00.000Z" },
|
|
15
|
-
};
|
|
16
|
-
const { deprecated, outdated } = packumentToFindings(packument, dep("request", "2.88.2"));
|
|
17
|
-
assert.ok(deprecated, "should return a deprecated finding");
|
|
18
|
-
assert.match(deprecated.reason, /has been deprecated/);
|
|
19
|
-
assert.equal(deprecated.source, "npm");
|
|
20
|
-
assert.equal(deprecated.dep.artifactId, "request");
|
|
21
|
-
// Latest === current, so not outdated.
|
|
22
|
-
assert.equal(outdated, null);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("packumentToFindings extracts a replacement URL from the deprecation message", () => {
|
|
26
|
-
const packument = {
|
|
27
|
-
"dist-tags": { latest: "1.0.0" },
|
|
28
|
-
versions: { "1.0.0": { deprecated: "use the foo package instead, see https://example.com/why" } },
|
|
29
|
-
};
|
|
30
|
-
const { deprecated } = packumentToFindings(packument, dep("bar", "1.0.0"));
|
|
31
|
-
assert.equal(deprecated.replacement, "https://example.com/why");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("packumentToFindings reports outdated when latest is newer", () => {
|
|
35
|
-
const packument = {
|
|
36
|
-
"dist-tags": { latest: "3.7.1" },
|
|
37
|
-
versions: { "3.6.0": {} },
|
|
38
|
-
time: { "3.7.1": "2023-08-28T00:00:00.000Z" },
|
|
39
|
-
};
|
|
40
|
-
const { deprecated, outdated } = packumentToFindings(packument, dep("jquery", "3.6.0"));
|
|
41
|
-
assert.equal(deprecated, null, "not deprecated");
|
|
42
|
-
assert.ok(outdated, "should be outdated");
|
|
43
|
-
assert.equal(outdated.latest, "3.7.1");
|
|
44
|
-
assert.equal(outdated.releaseDate, "2023-08-28");
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test("packumentToFindings returns nothing for an up-to-date, non-deprecated dep", () => {
|
|
48
|
-
const packument = {
|
|
49
|
-
"dist-tags": { latest: "4.18.2" },
|
|
50
|
-
versions: { "4.18.2": {} },
|
|
51
|
-
};
|
|
52
|
-
const { deprecated, outdated } = packumentToFindings(packument, dep("express", "4.18.2"));
|
|
53
|
-
assert.equal(deprecated, null);
|
|
54
|
-
assert.equal(outdated, null);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test("packumentToFindings tolerates a missing version entry", () => {
|
|
58
|
-
// Resolved version not present in the registry (e.g. unpublished) — must not throw.
|
|
59
|
-
const packument = { "dist-tags": { latest: "2.0.0" }, versions: { "2.0.0": {} } };
|
|
60
|
-
const { deprecated, outdated } = packumentToFindings(packument, dep("ghost", "1.5.0"));
|
|
61
|
-
assert.equal(deprecated, null);
|
|
62
|
-
assert.ok(outdated, "1.5.0 < 2.0.0 so still outdated");
|
|
63
|
-
assert.equal(outdated.latest, "2.0.0");
|
|
64
|
-
});
|
package/test/npm.test.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
const { test } = require("node:test");
|
|
2
|
-
const assert = require("node:assert/strict");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const {
|
|
5
|
-
parsePackageJson,
|
|
6
|
-
parsePackageLock,
|
|
7
|
-
parseYarnLockV1,
|
|
8
|
-
findJsManifests,
|
|
9
|
-
} = require("../lib/codecs/npm/parse");
|
|
10
|
-
const { collectNpmDeps } = require("../lib/codecs/npm/collect");
|
|
11
|
-
|
|
12
|
-
const FIX = path.join(__dirname, "fixtures", "monorepo-mixed");
|
|
13
|
-
|
|
14
|
-
test("parsePackageJson extracts dependencies + dev + peer scopes", () => {
|
|
15
|
-
const res = parsePackageJson(path.join(FIX, "packages", "web-app", "package.json"));
|
|
16
|
-
assert.equal(res.packageName, "@acme/web-app");
|
|
17
|
-
assert.equal(res.packageVersion, "1.0.0");
|
|
18
|
-
const byName = Object.fromEntries(res.deps.map(d => [d.name, d]));
|
|
19
|
-
assert.equal(byName.axios.scope, "prod");
|
|
20
|
-
assert.equal(byName.jest.scope, "dev");
|
|
21
|
-
assert.equal(byName.react.scope, "peer");
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test("parsePackageLock v3 enumerates direct + transitive deps with correct scopes", () => {
|
|
25
|
-
const res = parsePackageLock(path.join(FIX, "packages", "web-app", "package-lock.json"));
|
|
26
|
-
assert.equal(res.lockfileVersion, 3);
|
|
27
|
-
const byName = Object.fromEntries(res.deps.map(d => [d.name, d]));
|
|
28
|
-
// direct prod
|
|
29
|
-
assert.equal(byName.axios.scope, "prod");
|
|
30
|
-
assert.equal(byName.axios.version, "0.21.0");
|
|
31
|
-
// transitive (depth via nested node_modules) — flat in v3, but here axios's "follow-redirects" lives at the top, depth 0
|
|
32
|
-
assert.equal(byName["follow-redirects"].version, "1.13.0");
|
|
33
|
-
// dev
|
|
34
|
-
assert.equal(byName.jest.scope, "dev");
|
|
35
|
-
assert.equal(byName.eslint.scope, "dev");
|
|
36
|
-
// peer
|
|
37
|
-
assert.equal(byName.react.scope, "peer");
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("parseYarnLockV1 picks up versions and dedupes by (name, version)", () => {
|
|
41
|
-
const res = parseYarnLockV1(path.join(FIX, "packages", "cli", "yarn.lock"));
|
|
42
|
-
assert.equal(res.lockfileVersion, 1);
|
|
43
|
-
const byName = Object.fromEntries(res.deps.map(d => [d.name, d]));
|
|
44
|
-
assert.equal(byName.chalk.version, "4.1.2");
|
|
45
|
-
assert.equal(byName["ansi-styles"].version, "4.3.0");
|
|
46
|
-
assert.equal(byName["@acme/private-utils"].version, "1.0.0");
|
|
47
|
-
assert.equal(byName.mocha.version, "9.0.0");
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test("findJsManifests discovers both packages, skips node_modules and Maven dirs", () => {
|
|
51
|
-
const groups = findJsManifests(FIX);
|
|
52
|
-
const dirs = groups.map(g => path.relative(FIX, g.dir)).sort();
|
|
53
|
-
assert.ok(dirs.includes(path.join("packages", "web-app")));
|
|
54
|
-
assert.ok(dirs.includes(path.join("packages", "cli")));
|
|
55
|
-
// No Maven dir should appear
|
|
56
|
-
assert.ok(!dirs.some(d => d.startsWith("services" + path.sep)));
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
test("collectNpmDeps merges both packages and namespaces keys with 'npm:'", () => {
|
|
60
|
-
const map = collectNpmDeps(FIX, { verbose: false });
|
|
61
|
-
assert.ok(map.has("npm:axios"));
|
|
62
|
-
assert.ok(map.has("npm:lodash"));
|
|
63
|
-
assert.ok(map.has("npm:chalk"));
|
|
64
|
-
assert.ok(map.has("npm:@acme/private-utils"));
|
|
65
|
-
const axios = map.get("npm:axios");
|
|
66
|
-
assert.equal(axios.ecosystem, "npm");
|
|
67
|
-
assert.equal(axios.artifactId, "axios");
|
|
68
|
-
assert.equal(axios.groupId, "");
|
|
69
|
-
assert.equal(axios.version, "0.21.0");
|
|
70
|
-
assert.equal(axios.scope, "prod");
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test("collectNpmDeps --ignore-test skips devDependencies (jest, eslint, mocha)", () => {
|
|
74
|
-
const map = collectNpmDeps(FIX, { ignoreTest: true, verbose: false });
|
|
75
|
-
assert.equal(map.has("npm:jest"), false);
|
|
76
|
-
assert.equal(map.has("npm:eslint"), false);
|
|
77
|
-
assert.equal(map.has("npm:mocha"), false);
|
|
78
|
-
// prod deps still present
|
|
79
|
-
assert.ok(map.has("npm:axios"));
|
|
80
|
-
assert.ok(map.has("npm:chalk"));
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
test("collectNpmDeps deps2Exclude regex strips @acme/private-utils", () => {
|
|
84
|
-
const map = collectNpmDeps(FIX, { deps2Exclude: /^@acme\//, verbose: false });
|
|
85
|
-
assert.equal(map.has("npm:@acme/private-utils"), false);
|
|
86
|
-
// public deps still present
|
|
87
|
-
assert.ok(map.has("npm:axios"));
|
|
88
|
-
assert.ok(map.has("npm:chalk"));
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("collectNpmDeps prefers lockfile (resolved) version over package.json range", () => {
|
|
92
|
-
const map = collectNpmDeps(FIX, { verbose: false });
|
|
93
|
-
// In web-app, package.json pins "0.21.0" and lockfile says "0.21.0" — verify
|
|
94
|
-
// concrete resolved version is the one kept.
|
|
95
|
-
const axios = map.get("npm:axios");
|
|
96
|
-
assert.equal(/^\d/.test(axios.version), true);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("collectNpmDeps skips package.json when a lockfile is present in the same dir", () => {
|
|
100
|
-
// web-app has both package.json and package-lock.json. Only the lockfile
|
|
101
|
-
// should source dep entries; the package.json (which carries ranges like
|
|
102
|
-
// "^1.0.0" we can't query OSV with) must be ignored as a source.
|
|
103
|
-
const map = collectNpmDeps(FIX, { verbose: false });
|
|
104
|
-
const axios = map.get("npm:axios");
|
|
105
|
-
assert.ok(axios.lockType?.startsWith("package-lock-v"), `axios should come from lockfile, got lockType=${axios.lockType}`);
|
|
106
|
-
// And the only manifestPaths recorded for axios should be the lockfile
|
|
107
|
-
for (const p of axios.manifestPaths) {
|
|
108
|
-
assert.ok(p.endsWith("package-lock.json") || p.endsWith("yarn.lock"),
|
|
109
|
-
`expected only lockfile manifestPaths for axios, got ${p}`);
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test("collectNpmDeps no-lockfile fallback: warns + scans pinned, skips ranges", () => {
|
|
114
|
-
const map = collectNpmDeps(FIX, { verbose: false });
|
|
115
|
-
assert.ok(Array.isArray(map.warnings), "warnings array should be present");
|
|
116
|
-
const w = map.warnings.find(x => x.type === "no-lockfile" && x.manifestPath.includes("no-lock"));
|
|
117
|
-
assert.ok(w, "expected a no-lockfile warning for packages/no-lock/package.json");
|
|
118
|
-
assert.match(w.message, /best-effort/, "warning should mention best-effort/partial results");
|
|
119
|
-
// Range-only deps must NOT leak into the Map (can't query OSV with "^1.0.0").
|
|
120
|
-
assert.equal(map.has("npm:left-pad"), false, "left-pad ^1.0.0 must not be collected (unresolved range)");
|
|
121
|
-
// Pinned exact versions ARE now collected best-effort (changed contract).
|
|
122
|
-
assert.ok(map.has("npm:semver"), "semver 7.5.0 (pinned) should be collected from no-lock package.json");
|
|
123
|
-
assert.equal(map.get("npm:semver").version, "7.5.0");
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("parsePackageLock tags flattened-transitive entries as scope='transitive'", () => {
|
|
127
|
-
// follow-redirects is pulled in by axios but is not in the root's direct
|
|
128
|
-
// dependency lists — even though npm v3 places it at depth 0 in node_modules,
|
|
129
|
-
// it must be reported as transitive.
|
|
130
|
-
const res = parsePackageLock(path.join(FIX, "packages", "web-app", "package-lock.json"));
|
|
131
|
-
const byName = Object.fromEntries(res.deps.map(d => [d.name, d]));
|
|
132
|
-
assert.equal(byName["follow-redirects"].scope, "transitive");
|
|
133
|
-
assert.equal(byName.qs.scope, "transitive");
|
|
134
|
-
// direct deps stay direct
|
|
135
|
-
assert.equal(byName.axios.scope, "prod");
|
|
136
|
-
assert.equal(byName.express.scope, "prod");
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test("collectNpmDeps sets ecosystemType=npm for package-lock and yarn for yarn.lock", () => {
|
|
140
|
-
const map = collectNpmDeps(FIX, { verbose: false });
|
|
141
|
-
assert.equal(map.get("npm:axios").ecosystemType, "npm"); // from package-lock.json
|
|
142
|
-
assert.equal(map.get("npm:chalk").ecosystemType, "yarn"); // from yarn.lock
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test("collectNpmDeps captures peerDependencies (react@17 in web-app)", () => {
|
|
146
|
-
const map = collectNpmDeps(FIX, { verbose: false });
|
|
147
|
-
const react = map.get("npm:react");
|
|
148
|
-
assert.ok(react, "react peer dep should be present");
|
|
149
|
-
assert.equal(react.scope, "peer");
|
|
150
|
-
});
|
package/test/nuget.test.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
const test = require("node:test");
|
|
2
|
-
const assert = require("node:assert");
|
|
3
|
-
const path = require("path");
|
|
4
|
-
const { parsePackagesLockJson, parseCsproj, parsePackagesConfig, parseDirectoryPackagesProps } = require("../lib/codecs/nuget/parse");
|
|
5
|
-
const F = n => path.join(__dirname, "fixtures", n);
|
|
6
|
-
|
|
7
|
-
test("parsePackagesLockJson reads resolved versions + Direct/Transitive scope", async () => {
|
|
8
|
-
const r = await parsePackagesLockJson(F("csharp-lock/packages.lock.json"));
|
|
9
|
-
const m = Object.fromEntries(r.deps.map(d => [d.name, d]));
|
|
10
|
-
assert.strictEqual(m["Newtonsoft.Json"].version, "13.0.1");
|
|
11
|
-
assert.strictEqual(m["System.Buffers"].scope, "transitive");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test("parseDirectoryPackagesProps returns a name→version map (CPM)", async () => {
|
|
15
|
-
const m = await parseDirectoryPackagesProps(F("csharp-csproj/Directory.Packages.props"));
|
|
16
|
-
assert.strictEqual(m["managed"], "6.0.0"); // keyed lowercase
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("parseCsproj: pinned scanned, floating skipped, CPM resolved against props", async () => {
|
|
20
|
-
const cpm = await parseDirectoryPackagesProps(F("csharp-csproj/Directory.Packages.props"));
|
|
21
|
-
const r = await parseCsproj(F("csharp-csproj/app.csproj"), cpm);
|
|
22
|
-
const m = Object.fromEntries(r.deps.map(d => [d.name, d.version]));
|
|
23
|
-
assert.strictEqual(m["Newtonsoft.Json"], "13.0.1");
|
|
24
|
-
assert.strictEqual(m["Managed"], "6.0.0"); // resolved via CPM
|
|
25
|
-
assert.ok(!("Floating" in m)); // "1.*" skipped
|
|
26
|
-
assert.strictEqual(r.skipped, 1);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("parsePackagesConfig reads legacy id/version", async () => {
|
|
30
|
-
const r = await parsePackagesConfig(F("csharp-config/packages.config"));
|
|
31
|
-
assert.strictEqual(r.deps.find(d => d.name === "EntityFramework").version, "6.4.4");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const { nugetRegistrationToFindings } = require("../lib/codecs/nuget/registry");
|
|
35
|
-
test("nugetRegistrationToFindings extracts latest stable + deprecation for version", () => {
|
|
36
|
-
const reg = { items: [ { items: [
|
|
37
|
-
{ catalogEntry: { version: "13.0.1", deprecation: { reasons: ["Legacy"], alternatePackage: { id: "NewPkg" } } } },
|
|
38
|
-
{ catalogEntry: { version: "13.0.3" } },
|
|
39
|
-
{ catalogEntry: { version: "14.0.0-preview" } },
|
|
40
|
-
] } ] };
|
|
41
|
-
const f = nugetRegistrationToFindings(reg, { version: "13.0.1" });
|
|
42
|
-
assert.strictEqual(f.outdated.latest, "13.0.3");
|
|
43
|
-
assert.deepStrictEqual(f.deprecated, { reason: "Legacy", replacement: "NewPkg" });
|
|
44
|
-
const f2 = nugetRegistrationToFindings(reg, { version: "13.0.3" });
|
|
45
|
-
assert.strictEqual(f2.deprecated, null);
|
|
46
|
-
assert.strictEqual(f2.outdated, null);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const nuget = require("../lib/codecs/nuget.codec");
|
|
50
|
-
const { assertCodecShape } = require("../lib/codecs/codec.interface");
|
|
51
|
-
test("nuget codec: shape, detect, collect lockfile, case-insensitive key", async () => {
|
|
52
|
-
assertCodecShape(nuget);
|
|
53
|
-
assert.strictEqual(nuget.detect(F("csharp-lock")), true);
|
|
54
|
-
const { deps } = await nuget.collect(F("csharp-lock"), {});
|
|
55
|
-
const j = deps.get("nuget:newtonsoft.json"); // key lowercased
|
|
56
|
-
assert.ok(j);
|
|
57
|
-
assert.strictEqual(j.name, "Newtonsoft.Json"); // display keeps original case
|
|
58
|
-
assert.strictEqual(nuget.osvPackageName(j), "Newtonsoft.Json");
|
|
59
|
-
});
|
|
60
|
-
test("nuget codec: csproj uses CPM + skips floating with warning", async () => {
|
|
61
|
-
const { deps, warnings } = await nuget.collect(F("csharp-csproj"), {});
|
|
62
|
-
assert.ok(deps.has("nuget:newtonsoft.json"));
|
|
63
|
-
assert.ok(deps.has("nuget:managed")); // resolved via Directory.Packages.props
|
|
64
|
-
assert.ok(!deps.has("nuget:floating"));
|
|
65
|
-
assert.ok(warnings.find(w => w.type === "no-lockfile"));
|
|
66
|
-
});
|
package/test/osv.test.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
const test = require("node:test");
|
|
2
|
-
const assert = require("node:assert");
|
|
3
|
-
const { osvEcosystemFor, osvPkgName } = require("../lib/osv");
|
|
4
|
-
|
|
5
|
-
test("osvEcosystemFor maps codec ids to OSV ecosystem names", () => {
|
|
6
|
-
assert.strictEqual(osvEcosystemFor({ ecosystem: "maven" }), "Maven");
|
|
7
|
-
assert.strictEqual(osvEcosystemFor({ ecosystem: "npm" }), "npm");
|
|
8
|
-
assert.strictEqual(osvEcosystemFor({ ecosystem: "yarn" }), "npm");
|
|
9
|
-
assert.strictEqual(osvEcosystemFor({ ecosystem: "nuget" }), "NuGet");
|
|
10
|
-
assert.strictEqual(osvEcosystemFor({ ecosystem: "composer" }), "Packagist");
|
|
11
|
-
assert.strictEqual(osvEcosystemFor({ ecosystem: "pypi" }), "PyPI");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test("osvPkgName delegates to codec for maven (g:a) and npm (bare name)", () => {
|
|
15
|
-
assert.strictEqual(osvPkgName({ ecosystem: "maven", namespace: "org.apache", name: "log4j", groupId: "org.apache", artifactId: "log4j" }), "org.apache:log4j");
|
|
16
|
-
assert.strictEqual(osvPkgName({ ecosystem: "npm", namespace: "", name: "lodash", artifactId: "lodash" }), "lodash");
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
const { queryOsvForDeps, OSV_CACHE_DIR } = require("../lib/osv");
|
|
20
|
-
const { makeDepRecord } = require("../lib/dep-record");
|
|
21
|
-
const fs = require("fs");
|
|
22
|
-
|
|
23
|
-
// Regression: queryOsvForDeps must send the codec's package name + ecosystem to
|
|
24
|
-
// OSV for EVERY ecosystem. A prior bug cherry-picked only groupId/artifactId when
|
|
25
|
-
// rebuilding per-version deps, so composer/pypi/nuget queried name=undefined.
|
|
26
|
-
test("queryOsvForDeps sends correct package name + ecosystem per codec (mock fetcher)", async () => {
|
|
27
|
-
// Purge sentinel cache entries so the mock fetcher is always exercised
|
|
28
|
-
// (queryBatch writes a per-dep cache after a live batch — would self-poison).
|
|
29
|
-
try {
|
|
30
|
-
for (const f of fs.readdirSync(OSV_CACHE_DIR)) {
|
|
31
|
-
if (f.includes("9.9.9-fadtest")) fs.unlinkSync(require("path").join(OSV_CACHE_DIR, f));
|
|
32
|
-
}
|
|
33
|
-
} catch { /* dir may not exist yet */ }
|
|
34
|
-
const captured = [];
|
|
35
|
-
const fetcher = async (url, opts) => {
|
|
36
|
-
if (url.includes("/querybatch")) {
|
|
37
|
-
const body = JSON.parse(opts.body);
|
|
38
|
-
captured.push(...body.queries);
|
|
39
|
-
return { ok: true, json: async () => ({ results: body.queries.map(() => ({ vulns: [] })) }) };
|
|
40
|
-
}
|
|
41
|
-
return { ok: true, json: async () => ({}) };
|
|
42
|
-
};
|
|
43
|
-
const deps = new Map();
|
|
44
|
-
for (const [eco, ns, name, ver, expName, expEco] of [
|
|
45
|
-
["pypi", "", "django", "9.9.9-fadtest", "django", "PyPI"],
|
|
46
|
-
["composer", "guzzlehttp", "guzzle", "9.9.9-fadtest", "guzzlehttp/guzzle", "Packagist"],
|
|
47
|
-
["nuget", "", "Newtonsoft.Json", "9.9.9-fadtest", "Newtonsoft.Json", "NuGet"],
|
|
48
|
-
["maven", "org.apache", "log4j-core", "9.9.9-fadtest", "org.apache:log4j-core", "Maven"],
|
|
49
|
-
["npm", "", "lodash", "9.9.9-fadtest", "lodash", "npm"],
|
|
50
|
-
]) {
|
|
51
|
-
const r = makeDepRecord({ ecosystem: eco, namespace: ns, name, version: ver, manifestPath: "x" });
|
|
52
|
-
r._exp = { name: expName, eco: expEco };
|
|
53
|
-
deps.set(r.coordKey, r);
|
|
54
|
-
}
|
|
55
|
-
await queryOsvForDeps(deps, { fetcher });
|
|
56
|
-
for (const d of deps.values()) {
|
|
57
|
-
const q = captured.find(c => c.package.name === d._exp.name);
|
|
58
|
-
assert.ok(q, `expected an OSV query with package.name="${d._exp.name}" (${d.ecosystem})`);
|
|
59
|
-
assert.strictEqual(q.package.ecosystem, d._exp.eco, `ecosystem for ${d._exp.name}`);
|
|
60
|
-
assert.strictEqual(q.version, d.version);
|
|
61
|
-
}
|
|
62
|
-
});
|
package/test/outdated.test.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
const { test } = require("node:test");
|
|
2
|
-
const assert = require("node:assert/strict");
|
|
3
|
-
const { checkObsoleteDeps, checkObsolete, findEolProduct, KNOWN_OBSOLETE } = require("../lib/outdated");
|
|
4
|
-
|
|
5
|
-
test("known-obsolete.json contains the obvious historical hazards", () => {
|
|
6
|
-
assert.ok(KNOWN_OBSOLETE["log4j:log4j"], "log4j 1.x must be flagged");
|
|
7
|
-
assert.ok(KNOWN_OBSOLETE["commons-logging:commons-logging"]);
|
|
8
|
-
assert.ok(KNOWN_OBSOLETE["org.codehaus.jackson:jackson-databind"] || KNOWN_OBSOLETE["org.codehaus.jackson:jackson-mapper-asl"]);
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
test("checkObsoleteDeps flags log4j 1.x and jackson 1.x", () => {
|
|
12
|
-
const deps = new Map([
|
|
13
|
-
["log4j:log4j", { groupId: "log4j", artifactId: "log4j", version: "1.2.17" }],
|
|
14
|
-
["org.codehaus.jackson:jackson-mapper-asl", { groupId: "org.codehaus.jackson", artifactId: "jackson-mapper-asl", version: "1.9.13" }],
|
|
15
|
-
["com.fasterxml.jackson.core:jackson-databind", { groupId: "com.fasterxml.jackson.core", artifactId: "jackson-databind", version: "2.16.0" }],
|
|
16
|
-
]);
|
|
17
|
-
const out = checkObsoleteDeps(deps);
|
|
18
|
-
const ids = out.map(o => `${o.dep.groupId}:${o.dep.artifactId}`);
|
|
19
|
-
assert.ok(ids.includes("log4j:log4j"));
|
|
20
|
-
assert.ok(ids.includes("org.codehaus.jackson:jackson-mapper-asl"));
|
|
21
|
-
assert.ok(!ids.includes("com.fasterxml.jackson.core:jackson-databind"));
|
|
22
|
-
const log4j = out.find(o => o.dep.artifactId === "log4j");
|
|
23
|
-
assert.equal(log4j.severity, "CRITICAL");
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("checkObsoleteDeps deduplicates by g:a", () => {
|
|
27
|
-
const deps = new Map([
|
|
28
|
-
["log4j:log4j", { groupId: "log4j", artifactId: "log4j", version: "1.2.17" }],
|
|
29
|
-
]);
|
|
30
|
-
// Call twice — should still report once
|
|
31
|
-
const out1 = checkObsoleteDeps(deps);
|
|
32
|
-
const out2 = checkObsoleteDeps(deps);
|
|
33
|
-
assert.equal(out1.length, 1);
|
|
34
|
-
assert.equal(out2.length, 1);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test("checkObsolete single-dep returns details or null", () => {
|
|
38
|
-
const o = checkObsolete({ groupId: "log4j", artifactId: "log4j", version: "1.2" });
|
|
39
|
-
assert.ok(o);
|
|
40
|
-
assert.equal(o.severity, "CRITICAL");
|
|
41
|
-
assert.equal(checkObsolete({ groupId: "com.fasterxml.jackson.core", artifactId: "jackson-databind" }), null);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
test("findEolProduct matches Spring Boot by exact coord and by prefix", () => {
|
|
45
|
-
const sb = findEolProduct({ groupId: "org.springframework.boot", artifactId: "spring-boot-starter-parent" });
|
|
46
|
-
assert.equal(sb.product, "spring-boot");
|
|
47
|
-
|
|
48
|
-
const sbcustom = findEolProduct({ groupId: "org.springframework.boot", artifactId: "spring-boot-starter-anything" });
|
|
49
|
-
assert.equal(sbcustom.product, "spring-boot", "prefix-only mapping must still match");
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("findEolProduct picks longest prefix match", () => {
|
|
53
|
-
const sec = findEolProduct({ groupId: "org.springframework.security", artifactId: "made-up" });
|
|
54
|
-
assert.equal(sec.product, "spring-framework");
|
|
55
|
-
assert.equal(sec.label, "Spring Security");
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("findEolProduct maps the npm 'angular' package to AngularJS 1.x", () => {
|
|
59
|
-
// The literal npm package named "angular" IS AngularJS (1.x), EOL since 2022.
|
|
60
|
-
const a = findEolProduct({ ecosystem: "npm", groupId: "", artifactId: "angular" });
|
|
61
|
-
assert.equal(a.product, "angularjs");
|
|
62
|
-
assert.equal(a.label, "AngularJS");
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
test("findEolProduct maps @angular/* scoped packages to modern Angular", () => {
|
|
66
|
-
const core = findEolProduct({ ecosystem: "npm", groupId: "", artifactId: "@angular/core" });
|
|
67
|
-
assert.equal(core.product, "angular");
|
|
68
|
-
const router = findEolProduct({ ecosystem: "npm", groupId: "", artifactId: "@angular/router" });
|
|
69
|
-
assert.equal(router.product, "angular");
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test("findEolProduct maps react / react-dom / jquery / vue / bootstrap", () => {
|
|
73
|
-
assert.equal(findEolProduct({ ecosystem: "npm", artifactId: "react" }).product, "react");
|
|
74
|
-
assert.equal(findEolProduct({ ecosystem: "npm", artifactId: "react-dom" }).product, "react");
|
|
75
|
-
assert.equal(findEolProduct({ ecosystem: "npm", artifactId: "jquery" }).product, "jquery");
|
|
76
|
-
assert.equal(findEolProduct({ ecosystem: "npm", artifactId: "vue" }).product, "vue");
|
|
77
|
-
assert.equal(findEolProduct({ ecosystem: "npm", artifactId: "bootstrap" }).product, "bootstrap");
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("findEolProduct returns null for an unmapped npm package", () => {
|
|
81
|
-
assert.equal(findEolProduct({ ecosystem: "npm", artifactId: "left-pad" }), null);
|
|
82
|
-
// A Maven groupId must never leak into the npm lookup.
|
|
83
|
-
assert.equal(findEolProduct({ ecosystem: "npm", artifactId: "org.springframework" }), null);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
test("findEolProduct maps WebJars (client-side JS shipped as Maven artifacts)", () => {
|
|
87
|
-
// org.webjars:angularjs:1.8.3 — AngularJS 1.x, EOL since 2021.
|
|
88
|
-
const ajs = findEolProduct({ groupId: "org.webjars", artifactId: "angularjs", version: "1.8.3" });
|
|
89
|
-
assert.ok(ajs, "org.webjars:angularjs must map");
|
|
90
|
-
assert.equal(ajs.product, "angularjs");
|
|
91
|
-
|
|
92
|
-
assert.equal(findEolProduct({ groupId: "org.webjars", artifactId: "jquery" }).product, "jquery");
|
|
93
|
-
assert.equal(findEolProduct({ groupId: "org.webjars", artifactId: "bootstrap" }).product, "bootstrap");
|
|
94
|
-
// org.webjars.npm mirrors npm names; scope slash is encoded as "__".
|
|
95
|
-
assert.equal(findEolProduct({ groupId: "org.webjars.npm", artifactId: "vue" }).product, "vue");
|
|
96
|
-
assert.equal(findEolProduct({ groupId: "org.webjars.npm", artifactId: "angular__core" }).product, "angular");
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
test("findEolProduct returns null for an unmapped WebJar artifact", () => {
|
|
100
|
-
assert.equal(findEolProduct({ groupId: "org.webjars", artifactId: "datatables" }), null);
|
|
101
|
-
});
|