fad-checker 1.0.0 → 1.0.2

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/test/cpe.test.js CHANGED
@@ -51,6 +51,24 @@ test("matchVersionRange honours hard-pinned criteria version", () => {
51
51
  assert.equal(matchVersionRange("2.14.1", m), false);
52
52
  });
53
53
 
54
+ test("matchVersionRange folds CPE update qualifier into the version pin (H5)", () => {
55
+ // CPE 2.3 ":1.0.0:rc1:" describes the 1.0.0-rc1 pre-release. A release dep
56
+ // at 1.0.0 must NOT match — that was the H5 cascade.
57
+ const beta = { criteria: "cpe:2.3:a:apache:foo:1.0.0:beta1:*:*:*:*:*:*", vulnerable: true };
58
+ assert.equal(matchVersionRange("1.0.0", beta), false);
59
+ assert.equal(matchVersionRange("1.0.0-beta1", beta), true);
60
+
61
+ const rc = { criteria: "cpe:2.3:a:apache:foo:1.0.0:rc1:*:*:*:*:*:*", vulnerable: true };
62
+ assert.equal(matchVersionRange("1.0.0", rc), false);
63
+ assert.equal(matchVersionRange("1.0.0-rc1", rc), true);
64
+ });
65
+
66
+ test("cpeMatchesDep rejects short-token vendor leak (M4 mirror of H2)", () => {
67
+ const cpe = parseCpe23("cpe:2.3:a:a:log4j-core:*:*:*:*:*:*:*:*");
68
+ const dep = { groupId: "com.unrelated.log4j-core", artifactId: "log4j-core", ecosystem: "maven" };
69
+ assert.equal(cpeMatchesDep(cpe, dep), false);
70
+ });
71
+
54
72
  test("matchVersionRange returns true for unknown dep version (conservative)", () => {
55
73
  const m = { criteria: "cpe:2.3:a:apache:log4j:*:*:*:*:*:*:*:*", vulnerable: true, versionEndExcluding: "2.15.0" };
56
74
  assert.equal(matchVersionRange(null, m), true);
@@ -48,6 +48,62 @@ test("vendorMatchesGroup heuristic", () => {
48
48
  assert.equal(vendorMatchesGroup("springframework", "org.springframework"), true);
49
49
  });
50
50
 
51
+ test("vendorMatchesGroup rejects substring leaks (H2)", () => {
52
+ // All these cases used to leak through unbounded `g.includes(v)` / `v.includes(g)`
53
+ // branches. The fix keeps only equality + dot-segment membership.
54
+ assert.equal(vendorMatchesGroup("a", "com.acme.client"), false);
55
+ assert.equal(vendorMatchesGroup("ibm", "com.ibmcloudant.driver"), false);
56
+ assert.equal(vendorMatchesGroup("go", "org.golang.x"), false);
57
+ // "open" used to wrongly match every org.opensaml.* groupId via substring
58
+ assert.equal(vendorMatchesGroup("open", "org.opensaml.core"), false);
59
+ // Dot-segment match still works for legitimate cases
60
+ assert.equal(vendorMatchesGroup("apache", "org.apache.commons"), true);
61
+ assert.equal(vendorMatchesGroup("springframework", "org.springframework.boot"), true);
62
+ });
63
+
64
+ test("matchDepsAgainstCves hides 'possible' tier by default (H3)", () => {
65
+ // product matches `log4j-core` but vendor `acme` doesn't match groupId.
66
+ // Without opts.includePossibleTier we expect zero matches.
67
+ const idx = {
68
+ byPackageName: {},
69
+ byProduct: {
70
+ "log4j-core": [
71
+ { id: "CVE-FAKE-0001", severity: "HIGH", vendor: "acme", product: "log4j-core",
72
+ ranges: [{ version: "2.0", lessThan: "2.20.0", status: "affected" }] },
73
+ ],
74
+ },
75
+ };
76
+ const deps = new Map([
77
+ ["org.apache.logging.log4j:log4j-core", {
78
+ groupId: "org.apache.logging.log4j", artifactId: "log4j-core", version: "2.14.0", scope: "compile", pomPaths: [],
79
+ }],
80
+ ]);
81
+ assert.equal(matchDepsAgainstCves(deps, idx).length, 0);
82
+ assert.equal(matchDepsAgainstCves(deps, idx, { includePossibleTier: true }).length, 1);
83
+ });
84
+
85
+ test("matchDepsAgainstCves does not flag deps when range has no bounds (H1+H3)", () => {
86
+ // Sparse range entry (`{status:"affected"}` with no bounds) used to flag
87
+ // every version that hit the product bucket. Combined with H3 hiding the
88
+ // possible tier, this should now produce zero matches even when the
89
+ // vendor matches.
90
+ const idx = {
91
+ byPackageName: {},
92
+ byProduct: {
93
+ "log4j-core": [
94
+ { id: "CVE-FAKE-0002", severity: "HIGH", vendor: "apache", product: "log4j-core",
95
+ ranges: [{ status: "affected" }] },
96
+ ],
97
+ },
98
+ };
99
+ const deps = new Map([
100
+ ["org.apache.logging.log4j:log4j-core", {
101
+ groupId: "org.apache.logging.log4j", artifactId: "log4j-core", version: "2.99.0", scope: "compile", pomPaths: [],
102
+ }],
103
+ ]);
104
+ assert.equal(matchDepsAgainstCves(deps, idx).length, 0);
105
+ });
106
+
51
107
  test("collectResolvedDeps dedupes by g:a and includes external parent POMs", async () => {
52
108
  const { store, props } = await pipeline(COMPLEX);
53
109
  const deps = collectResolvedDeps(store, props, {});
@@ -61,7 +61,7 @@ test("generateWordReport contains Word XML namespaces", () => {
61
61
  });
62
62
 
63
63
  test("writeReports writes both files to disk", async () => {
64
- const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "fad-check-rep-"));
64
+ const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "fad-checker-rep-"));
65
65
  const r = await writeReports({
66
66
  cveMatches: sampleMatches,
67
67
  eolResults: [{ dep: { groupId: "org.hibernate", artifactId: "hibernate-core", version: "5.6" }, product: "Hibernate ORM", eol: "2025-08-08" }],
@@ -41,6 +41,15 @@ test("isVersionAffected returns false when status != affected", () => {
41
41
  assert.equal(isVersionAffected("1.5", spec), false);
42
42
  });
43
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
+
44
53
  test("parseRange handles Maven range syntax", () => {
45
54
  assert.deepEqual(parseRange("1.2.3"), { exact: "1.2.3" });
46
55
  assert.deepEqual(parseRange("[1.0,2.0)"), { lower: "1.0", lowerInclusive: true, upper: "2.0", upperInclusive: false });
@@ -4,7 +4,7 @@
4
4
  * - 1 npm package (package-lock v3) with prod/dev/peer deps
5
5
  * - 1 yarn-v1 package with prod/dev deps + a private @acme/* dep
6
6
  *
7
- * Drives the same code paths as `fad-check --report` minus the network calls.
7
+ * Drives the same code paths as `fad-checker --report` minus the network calls.
8
8
  */
9
9
  const { test } = require("node:test");
10
10
  const assert = require("node:assert/strict");
package/test/snyk.test.js CHANGED
@@ -35,7 +35,7 @@ test("parseSnykStdout accepts a JSON object or array", () => {
35
35
  assert.equal(obj.length, 1);
36
36
  });
37
37
 
38
- test("parseSnykResults normalises to fad-check match shape", () => {
38
+ test("parseSnykResults normalises to fad-checker match shape", () => {
39
39
  const out = parseSnykResults(snykSample);
40
40
  assert.equal(out.length, 2);
41
41
  assert.equal(out[0].dep.groupId, "org.apache.logging.log4j");
@@ -5,9 +5,9 @@ const os = require("os");
5
5
  const path = require("path");
6
6
  const { parsePomXml, resolveProps, buildMgmt, SCOPE_MATRIX, resolveTransitiveDeps } = require("../lib/transitive");
7
7
 
8
- // Per-test cache dir so we don't poison ~/.fad-check/poms-cache during tests
8
+ // Per-test cache dir so we don't poison ~/.fad-checker/poms-cache during tests
9
9
  function freshCache() {
10
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "fad-check-pom-cache-"));
10
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "fad-checker-pom-cache-"));
11
11
  return dir;
12
12
  }
13
13