@fourteensystems/prodcheck 0.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 +252 -0
- package/bin/prodcheck.mjs +2 -0
- package/dist/cli/commands/baseline.d.ts +7 -0
- package/dist/cli/commands/baseline.d.ts.map +1 -0
- package/dist/cli/commands/baseline.js +22 -0
- package/dist/cli/commands/baseline.js.map +1 -0
- package/dist/cli/commands/ci.d.ts +14 -0
- package/dist/cli/commands/ci.d.ts.map +1 -0
- package/dist/cli/commands/ci.js +104 -0
- package/dist/cli/commands/ci.js.map +1 -0
- package/dist/cli/commands/explain.d.ts +2 -0
- package/dist/cli/commands/explain.d.ts.map +1 -0
- package/dist/cli/commands/explain.js +20 -0
- package/dist/cli/commands/explain.js.map +1 -0
- package/dist/cli/commands/init.d.ts +7 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +127 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/rules.d.ts +2 -0
- package/dist/cli/commands/rules.d.ts.map +1 -0
- package/dist/cli/commands/rules.js +13 -0
- package/dist/cli/commands/rules.js.map +1 -0
- package/dist/cli/commands/scan.d.ts +10 -0
- package/dist/cli/commands/scan.d.ts.map +1 -0
- package/dist/cli/commands/scan.js +65 -0
- package/dist/cli/commands/scan.js.map +1 -0
- package/dist/cli/commands/waive.d.ts +8 -0
- package/dist/cli/commands/waive.d.ts.map +1 -0
- package/dist/cli/commands/waive.js +34 -0
- package/dist/cli/commands/waive.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +64 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/engine/baseline.d.ts +11 -0
- package/dist/engine/baseline.d.ts.map +1 -0
- package/dist/engine/baseline.js +39 -0
- package/dist/engine/baseline.js.map +1 -0
- package/dist/engine/baseline.test.d.ts +2 -0
- package/dist/engine/baseline.test.d.ts.map +1 -0
- package/dist/engine/baseline.test.js +135 -0
- package/dist/engine/baseline.test.js.map +1 -0
- package/dist/engine/config.d.ts +8 -0
- package/dist/engine/config.d.ts.map +1 -0
- package/dist/engine/config.js +134 -0
- package/dist/engine/config.js.map +1 -0
- package/dist/engine/config.test.d.ts +2 -0
- package/dist/engine/config.test.d.ts.map +1 -0
- package/dist/engine/config.test.js +107 -0
- package/dist/engine/config.test.js.map +1 -0
- package/dist/engine/extensions/load.d.ts +11 -0
- package/dist/engine/extensions/load.d.ts.map +1 -0
- package/dist/engine/extensions/load.js +26 -0
- package/dist/engine/extensions/load.js.map +1 -0
- package/dist/engine/extensions/registry.d.ts +5 -0
- package/dist/engine/extensions/registry.d.ts.map +1 -0
- package/dist/engine/extensions/registry.js +11 -0
- package/dist/engine/extensions/registry.js.map +1 -0
- package/dist/engine/extensions/types.d.ts +51 -0
- package/dist/engine/extensions/types.d.ts.map +1 -0
- package/dist/engine/extensions/types.js +2 -0
- package/dist/engine/extensions/types.js.map +1 -0
- package/dist/engine/license.d.ts +40 -0
- package/dist/engine/license.d.ts.map +1 -0
- package/dist/engine/license.js +104 -0
- package/dist/engine/license.js.map +1 -0
- package/dist/engine/report.d.ts +5 -0
- package/dist/engine/report.d.ts.map +1 -0
- package/dist/engine/report.js +115 -0
- package/dist/engine/report.js.map +1 -0
- package/dist/engine/run.d.ts +11 -0
- package/dist/engine/run.d.ts.map +1 -0
- package/dist/engine/run.js +105 -0
- package/dist/engine/run.js.map +1 -0
- package/dist/engine/sarif.d.ts +3 -0
- package/dist/engine/sarif.d.ts.map +1 -0
- package/dist/engine/sarif.js +58 -0
- package/dist/engine/sarif.js.map +1 -0
- package/dist/engine/sarif.test.d.ts +2 -0
- package/dist/engine/sarif.test.d.ts.map +1 -0
- package/dist/engine/sarif.test.js +152 -0
- package/dist/engine/sarif.test.js.map +1 -0
- package/dist/engine/score.d.ts +13 -0
- package/dist/engine/score.d.ts.map +1 -0
- package/dist/engine/score.js +116 -0
- package/dist/engine/score.js.map +1 -0
- package/dist/engine/score.test.d.ts +2 -0
- package/dist/engine/score.test.d.ts.map +1 -0
- package/dist/engine/score.test.js +227 -0
- package/dist/engine/score.test.js.map +1 -0
- package/dist/engine/types.d.ts +123 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +2 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/engine/version.d.ts +5 -0
- package/dist/engine/version.d.ts.map +1 -0
- package/dist/engine/version.js +15 -0
- package/dist/engine/version.js.map +1 -0
- package/dist/engine/waivers.d.ts +9 -0
- package/dist/engine/waivers.d.ts.map +1 -0
- package/dist/engine/waivers.js +55 -0
- package/dist/engine/waivers.js.map +1 -0
- package/dist/engine/waivers.test.d.ts +2 -0
- package/dist/engine/waivers.test.d.ts.map +1 -0
- package/dist/engine/waivers.test.js +147 -0
- package/dist/engine/waivers.test.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/next/deps.d.ts +4 -0
- package/dist/next/deps.d.ts.map +1 -0
- package/dist/next/deps.js +118 -0
- package/dist/next/deps.js.map +1 -0
- package/dist/next/deps.test.d.ts +2 -0
- package/dist/next/deps.test.d.ts.map +1 -0
- package/dist/next/deps.test.js +249 -0
- package/dist/next/deps.test.js.map +1 -0
- package/dist/next/detect.d.ts +10 -0
- package/dist/next/detect.d.ts.map +1 -0
- package/dist/next/detect.js +57 -0
- package/dist/next/detect.js.map +1 -0
- package/dist/next/detect.test.d.ts +2 -0
- package/dist/next/detect.test.d.ts.map +1 -0
- package/dist/next/detect.test.js +74 -0
- package/dist/next/detect.test.js.map +1 -0
- package/dist/next/index.d.ts +5 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +59 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/middleware.d.ts +3 -0
- package/dist/next/middleware.d.ts.map +1 -0
- package/dist/next/middleware.js +48 -0
- package/dist/next/middleware.js.map +1 -0
- package/dist/next/middleware.test.d.ts +2 -0
- package/dist/next/middleware.test.d.ts.map +1 -0
- package/dist/next/middleware.test.js +203 -0
- package/dist/next/middleware.test.js.map +1 -0
- package/dist/next/routes.d.ts +10 -0
- package/dist/next/routes.d.ts.map +1 -0
- package/dist/next/routes.js +172 -0
- package/dist/next/routes.js.map +1 -0
- package/dist/next/routes.test.d.ts +2 -0
- package/dist/next/routes.test.d.ts.map +1 -0
- package/dist/next/routes.test.js +175 -0
- package/dist/next/routes.test.js.map +1 -0
- package/dist/next/server-actions.d.ts +4 -0
- package/dist/next/server-actions.d.ts.map +1 -0
- package/dist/next/server-actions.js +107 -0
- package/dist/next/server-actions.js.map +1 -0
- package/dist/next/server-actions.test.d.ts +2 -0
- package/dist/next/server-actions.test.d.ts.map +1 -0
- package/dist/next/server-actions.test.js +138 -0
- package/dist/next/server-actions.test.js.map +1 -0
- package/dist/next/trpc.d.ts +3 -0
- package/dist/next/trpc.d.ts.map +1 -0
- package/dist/next/trpc.js +312 -0
- package/dist/next/trpc.js.map +1 -0
- package/dist/next/types.d.ts +144 -0
- package/dist/next/types.d.ts.map +1 -0
- package/dist/next/types.js +2 -0
- package/dist/next/types.js.map +1 -0
- package/dist/next/wrappers.d.ts +10 -0
- package/dist/next/wrappers.d.ts.map +1 -0
- package/dist/next/wrappers.js +536 -0
- package/dist/next/wrappers.js.map +1 -0
- package/dist/next/wrappers.test.d.ts +2 -0
- package/dist/next/wrappers.test.d.ts.map +1 -0
- package/dist/next/wrappers.test.js +361 -0
- package/dist/next/wrappers.test.js.map +1 -0
- package/dist/rules/auth-boundary-missing.d.ts +5 -0
- package/dist/rules/auth-boundary-missing.d.ts.map +1 -0
- package/dist/rules/auth-boundary-missing.js +463 -0
- package/dist/rules/auth-boundary-missing.js.map +1 -0
- package/dist/rules/auth-boundary-missing.test.d.ts +2 -0
- package/dist/rules/auth-boundary-missing.test.d.ts.map +1 -0
- package/dist/rules/auth-boundary-missing.test.js +492 -0
- package/dist/rules/auth-boundary-missing.test.js.map +1 -0
- package/dist/rules/index.d.ts +12 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +95 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/input-validation-missing.d.ts +5 -0
- package/dist/rules/input-validation-missing.d.ts.map +1 -0
- package/dist/rules/input-validation-missing.js +272 -0
- package/dist/rules/input-validation-missing.js.map +1 -0
- package/dist/rules/input-validation-missing.test.d.ts +2 -0
- package/dist/rules/input-validation-missing.test.d.ts.map +1 -0
- package/dist/rules/input-validation-missing.test.js +449 -0
- package/dist/rules/input-validation-missing.test.js.map +1 -0
- package/dist/rules/rate-limit-missing.d.ts +5 -0
- package/dist/rules/rate-limit-missing.d.ts.map +1 -0
- package/dist/rules/rate-limit-missing.js +316 -0
- package/dist/rules/rate-limit-missing.js.map +1 -0
- package/dist/rules/rate-limit-missing.test.d.ts +2 -0
- package/dist/rules/rate-limit-missing.test.d.ts.map +1 -0
- package/dist/rules/rate-limit-missing.test.js +381 -0
- package/dist/rules/rate-limit-missing.test.js.map +1 -0
- package/dist/rules/tenancy-scope-missing.d.ts +5 -0
- package/dist/rules/tenancy-scope-missing.d.ts.map +1 -0
- package/dist/rules/tenancy-scope-missing.js +149 -0
- package/dist/rules/tenancy-scope-missing.js.map +1 -0
- package/dist/rules/wrapper-unrecognized.d.ts +5 -0
- package/dist/rules/wrapper-unrecognized.d.ts.map +1 -0
- package/dist/rules/wrapper-unrecognized.js +81 -0
- package/dist/rules/wrapper-unrecognized.js.map +1 -0
- package/dist/util/hof.d.ts +22 -0
- package/dist/util/hof.d.ts.map +1 -0
- package/dist/util/hof.js +99 -0
- package/dist/util/hof.js.map +1 -0
- package/dist/util/hof.test.d.ts +2 -0
- package/dist/util/hof.test.d.ts.map +1 -0
- package/dist/util/hof.test.js +79 -0
- package/dist/util/hof.test.js.map +1 -0
- package/dist/util/monorepo.d.ts +6 -0
- package/dist/util/monorepo.d.ts.map +1 -0
- package/dist/util/monorepo.js +29 -0
- package/dist/util/monorepo.js.map +1 -0
- package/dist/util/outbound-fetch.d.ts +14 -0
- package/dist/util/outbound-fetch.d.ts.map +1 -0
- package/dist/util/outbound-fetch.js +59 -0
- package/dist/util/outbound-fetch.js.map +1 -0
- package/dist/util/outbound-fetch.test.d.ts +2 -0
- package/dist/util/outbound-fetch.test.d.ts.map +1 -0
- package/dist/util/outbound-fetch.test.js +83 -0
- package/dist/util/outbound-fetch.test.js.map +1 -0
- package/dist/util/paths.d.ts +6 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +18 -0
- package/dist/util/paths.js.map +1 -0
- package/dist/util/resolve.d.ts +30 -0
- package/dist/util/resolve.d.ts.map +1 -0
- package/dist/util/resolve.js +306 -0
- package/dist/util/resolve.js.map +1 -0
- package/dist/util/resolve.test.d.ts +2 -0
- package/dist/util/resolve.test.d.ts.map +1 -0
- package/dist/util/resolve.test.js +186 -0
- package/dist/util/resolve.test.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { computeScore, summarizeFindings, parseConfidence, parseSeverity, parseIntOrThrow, confidenceLevel, severityLevel, scoreStatus, buildDetectedList, } from "./score.js";
|
|
3
|
+
function makeFinding(overrides = {}) {
|
|
4
|
+
return {
|
|
5
|
+
ruleId: "TEST-RULE",
|
|
6
|
+
severity: "high",
|
|
7
|
+
confidence: "high",
|
|
8
|
+
message: "test finding",
|
|
9
|
+
file: "test.ts",
|
|
10
|
+
evidence: [],
|
|
11
|
+
confidenceRationale: "",
|
|
12
|
+
remediation: [],
|
|
13
|
+
tags: [],
|
|
14
|
+
...overrides,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
describe("computeScore", () => {
|
|
18
|
+
it("returns 100 with no findings", () => {
|
|
19
|
+
expect(computeScore([])).toBe(100);
|
|
20
|
+
});
|
|
21
|
+
it("subtracts confidence-weighted penalty for each finding", () => {
|
|
22
|
+
// critical/high = 15 × 1.0 = 15 → score 85
|
|
23
|
+
const findings = [makeFinding({ severity: "critical" })];
|
|
24
|
+
expect(computeScore(findings)).toBe(85);
|
|
25
|
+
});
|
|
26
|
+
it("applies correct penalties per severity (high confidence)", () => {
|
|
27
|
+
// All findings default to confidence: "high" (weight 1.0)
|
|
28
|
+
expect(computeScore([makeFinding({ severity: "critical" })])).toBe(85); // 15 × 1.0
|
|
29
|
+
expect(computeScore([makeFinding({ severity: "high" })])).toBe(94); // 6 × 1.0
|
|
30
|
+
expect(computeScore([makeFinding({ severity: "med" })])).toBe(97); // 3 × 1.0
|
|
31
|
+
expect(computeScore([makeFinding({ severity: "low" })])).toBe(99); // 1 × 1.0
|
|
32
|
+
});
|
|
33
|
+
it("weights penalties by confidence level", () => {
|
|
34
|
+
// Same severity, different confidence
|
|
35
|
+
const critHigh = computeScore([makeFinding({ severity: "critical", confidence: "high" })]);
|
|
36
|
+
const critMed = computeScore([makeFinding({ severity: "critical", confidence: "med" })]);
|
|
37
|
+
const critLow = computeScore([makeFinding({ severity: "critical", confidence: "low" })]);
|
|
38
|
+
// critical: 15 base. high=1.0→15, med=0.25→3.75, low=0.1→1.5
|
|
39
|
+
expect(critHigh).toBe(85); // 100 - 15
|
|
40
|
+
expect(critMed).toBe(96); // 100 - 3.75 → round(96.25)
|
|
41
|
+
expect(critLow).toBe(99); // 100 - 1.5 → round(98.5)
|
|
42
|
+
});
|
|
43
|
+
it("med/med finding costs much less than high/high", () => {
|
|
44
|
+
// med/med = 3 × 0.25 = 0.75 per finding. 10 findings = 7.5 → score 93
|
|
45
|
+
const findings = Array.from({ length: 10 }, () => makeFinding({ severity: "med", confidence: "med" }));
|
|
46
|
+
expect(computeScore(findings)).toBe(93); // round(100 - 7.5)
|
|
47
|
+
});
|
|
48
|
+
it("low/low findings have minimal impact", () => {
|
|
49
|
+
// low/low = 1 × 0.1 = 0.1 per finding. 20 findings = 2.0 → score 98
|
|
50
|
+
const findings = Array.from({ length: 20 }, () => makeFinding({ severity: "low", confidence: "low" }));
|
|
51
|
+
expect(computeScore(findings)).toBe(98);
|
|
52
|
+
});
|
|
53
|
+
it("accumulates multiple findings from different rules", () => {
|
|
54
|
+
const findings = [
|
|
55
|
+
makeFinding({ ruleId: "RULE-A", severity: "critical" }), // 15
|
|
56
|
+
makeFinding({ ruleId: "RULE-B", severity: "critical" }), // 15
|
|
57
|
+
makeFinding({ ruleId: "RULE-C", severity: "high" }), // 6
|
|
58
|
+
];
|
|
59
|
+
// All high confidence: 15 + 15 + 6 = 36 → score 64
|
|
60
|
+
expect(computeScore(findings)).toBe(64);
|
|
61
|
+
});
|
|
62
|
+
it("caps deduction per rule at 35% of start", () => {
|
|
63
|
+
// 10 critical/high from same rule = 150 raw penalty, capped at 35
|
|
64
|
+
const findings = Array.from({ length: 10 }, () => makeFinding({ severity: "critical" }));
|
|
65
|
+
expect(computeScore(findings)).toBe(65);
|
|
66
|
+
});
|
|
67
|
+
it("floors at 0 with enough different rules", () => {
|
|
68
|
+
// 3 rules × 3 critical/high findings each
|
|
69
|
+
const findings = [
|
|
70
|
+
makeFinding({ ruleId: "RULE-A", severity: "critical" }),
|
|
71
|
+
makeFinding({ ruleId: "RULE-A", severity: "critical" }),
|
|
72
|
+
makeFinding({ ruleId: "RULE-A", severity: "critical" }),
|
|
73
|
+
makeFinding({ ruleId: "RULE-B", severity: "critical" }),
|
|
74
|
+
makeFinding({ ruleId: "RULE-B", severity: "critical" }),
|
|
75
|
+
makeFinding({ ruleId: "RULE-B", severity: "critical" }),
|
|
76
|
+
makeFinding({ ruleId: "RULE-C", severity: "critical" }),
|
|
77
|
+
makeFinding({ ruleId: "RULE-C", severity: "critical" }),
|
|
78
|
+
makeFinding({ ruleId: "RULE-C", severity: "critical" }),
|
|
79
|
+
];
|
|
80
|
+
// Each rule: 45 raw, capped at 35. 3 × 35 = 105 > 100 → floors at 0
|
|
81
|
+
expect(computeScore(findings)).toBe(0);
|
|
82
|
+
});
|
|
83
|
+
it("uses custom scoring config", () => {
|
|
84
|
+
const config = { start: 50, penalties: { critical: 10, high: 5, med: 2, low: 1 } };
|
|
85
|
+
// critical/high with default confidence weight 1.0 → 10 × 1.0 = 10 → 40
|
|
86
|
+
expect(computeScore([makeFinding({ severity: "critical" })], config)).toBe(40);
|
|
87
|
+
});
|
|
88
|
+
it("respects custom maxPenaltyPerRule", () => {
|
|
89
|
+
const config = {
|
|
90
|
+
start: 100,
|
|
91
|
+
penalties: { critical: 25, high: 10, med: 3, low: 1 },
|
|
92
|
+
maxPenaltyPerRule: 25,
|
|
93
|
+
};
|
|
94
|
+
// 5 critical/high from same rule = 125 raw, capped at 25
|
|
95
|
+
const findings = Array.from({ length: 5 }, () => makeFinding({ severity: "critical" }));
|
|
96
|
+
expect(computeScore(findings, config)).toBe(75);
|
|
97
|
+
});
|
|
98
|
+
it("respects custom confidenceWeights", () => {
|
|
99
|
+
const config = {
|
|
100
|
+
start: 100,
|
|
101
|
+
penalties: { critical: 20, high: 10, med: 5, low: 2 },
|
|
102
|
+
confidenceWeights: { high: 1.0, med: 0.5, low: 0.2 },
|
|
103
|
+
};
|
|
104
|
+
// critical/med = 20 × 0.5 = 10 → score 90
|
|
105
|
+
expect(computeScore([makeFinding({ severity: "critical", confidence: "med" })], config)).toBe(90);
|
|
106
|
+
});
|
|
107
|
+
it("applies cap independently per rule", () => {
|
|
108
|
+
const findings = [
|
|
109
|
+
makeFinding({ ruleId: "AUTH", severity: "critical" }),
|
|
110
|
+
makeFinding({ ruleId: "AUTH", severity: "critical" }),
|
|
111
|
+
makeFinding({ ruleId: "AUTH", severity: "critical" }),
|
|
112
|
+
makeFinding({ ruleId: "RATE", severity: "critical" }),
|
|
113
|
+
makeFinding({ ruleId: "RATE", severity: "critical" }),
|
|
114
|
+
makeFinding({ ruleId: "RATE", severity: "critical" }),
|
|
115
|
+
];
|
|
116
|
+
// AUTH: 45 raw, capped at 35. RATE: 45 raw, capped at 35. Total: 70 → score 30
|
|
117
|
+
expect(computeScore(findings)).toBe(30);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe("summarizeFindings", () => {
|
|
121
|
+
it("returns zero counts with no findings", () => {
|
|
122
|
+
expect(summarizeFindings([])).toEqual({ critical: 0, high: 0, med: 0, low: 0 });
|
|
123
|
+
});
|
|
124
|
+
it("counts by severity", () => {
|
|
125
|
+
const findings = [
|
|
126
|
+
makeFinding({ severity: "critical" }),
|
|
127
|
+
makeFinding({ severity: "critical" }),
|
|
128
|
+
makeFinding({ severity: "high" }),
|
|
129
|
+
makeFinding({ severity: "low" }),
|
|
130
|
+
];
|
|
131
|
+
expect(summarizeFindings(findings)).toEqual({ critical: 2, high: 1, med: 0, low: 1 });
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
describe("parseConfidence", () => {
|
|
135
|
+
it("parses valid values", () => {
|
|
136
|
+
expect(parseConfidence("high")).toBe("high");
|
|
137
|
+
expect(parseConfidence("med")).toBe("med");
|
|
138
|
+
expect(parseConfidence("low")).toBe("low");
|
|
139
|
+
});
|
|
140
|
+
it("throws on invalid values", () => {
|
|
141
|
+
expect(() => parseConfidence("invalid")).toThrow("Invalid confidence");
|
|
142
|
+
expect(() => parseConfidence("")).toThrow("Invalid confidence");
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
describe("parseSeverity", () => {
|
|
146
|
+
it("parses valid values", () => {
|
|
147
|
+
expect(parseSeverity("critical")).toBe("critical");
|
|
148
|
+
expect(parseSeverity("high")).toBe("high");
|
|
149
|
+
expect(parseSeverity("med")).toBe("med");
|
|
150
|
+
expect(parseSeverity("low")).toBe("low");
|
|
151
|
+
});
|
|
152
|
+
it("throws on invalid values", () => {
|
|
153
|
+
expect(() => parseSeverity("medium")).toThrow("Invalid severity");
|
|
154
|
+
expect(() => parseSeverity("")).toThrow("Invalid severity");
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
describe("parseIntOrThrow", () => {
|
|
158
|
+
it("parses valid integers", () => {
|
|
159
|
+
expect(parseIntOrThrow("42", "test")).toBe(42);
|
|
160
|
+
expect(parseIntOrThrow("0", "test")).toBe(0);
|
|
161
|
+
expect(parseIntOrThrow("-1", "test")).toBe(-1);
|
|
162
|
+
});
|
|
163
|
+
it("throws on non-numbers", () => {
|
|
164
|
+
expect(() => parseIntOrThrow("abc", "test")).toThrow("Invalid test");
|
|
165
|
+
expect(() => parseIntOrThrow("", "test")).toThrow("Invalid test");
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
describe("confidenceLevel", () => {
|
|
169
|
+
it("maps confidence to numeric level", () => {
|
|
170
|
+
expect(confidenceLevel("high")).toBe(3);
|
|
171
|
+
expect(confidenceLevel("med")).toBe(2);
|
|
172
|
+
expect(confidenceLevel("low")).toBe(1);
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
describe("severityLevel", () => {
|
|
176
|
+
it("maps severity to numeric level", () => {
|
|
177
|
+
expect(severityLevel("critical")).toBe(4);
|
|
178
|
+
expect(severityLevel("high")).toBe(3);
|
|
179
|
+
expect(severityLevel("med")).toBe(2);
|
|
180
|
+
expect(severityLevel("low")).toBe(1);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe("scoreStatus", () => {
|
|
184
|
+
it("returns PASS for scores >= 80", () => {
|
|
185
|
+
expect(scoreStatus(100)).toBe("PASS");
|
|
186
|
+
expect(scoreStatus(80)).toBe("PASS");
|
|
187
|
+
});
|
|
188
|
+
it("returns WARN for scores 50-79", () => {
|
|
189
|
+
expect(scoreStatus(79)).toBe("WARN");
|
|
190
|
+
expect(scoreStatus(50)).toBe("WARN");
|
|
191
|
+
});
|
|
192
|
+
it("returns FAIL for scores < 50", () => {
|
|
193
|
+
expect(scoreStatus(49)).toBe("FAIL");
|
|
194
|
+
expect(scoreStatus(0)).toBe("FAIL");
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe("buildDetectedList", () => {
|
|
198
|
+
function makeEmptyDeps() {
|
|
199
|
+
return {
|
|
200
|
+
hasNextAuth: false, hasClerk: false, hasSupabase: false,
|
|
201
|
+
hasKinde: false, hasWorkOS: false, hasBetterAuth: false,
|
|
202
|
+
hasLucia: false, hasAuth0: false, hasIronSession: false,
|
|
203
|
+
hasFirebaseAuth: false, hasPrisma: false, hasDrizzle: false,
|
|
204
|
+
hasTrpc: false, hasUpstashRatelimit: false, hasArcjet: false,
|
|
205
|
+
hasUnkey: false,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
it("always includes next-app-router", () => {
|
|
209
|
+
const result = {
|
|
210
|
+
detected: { deps: makeEmptyDeps(), trpc: false, middleware: false },
|
|
211
|
+
};
|
|
212
|
+
expect(buildDetectedList(result)).toEqual(["next-app-router"]);
|
|
213
|
+
});
|
|
214
|
+
it("includes detected deps", () => {
|
|
215
|
+
const deps = makeEmptyDeps();
|
|
216
|
+
deps.hasClerk = true;
|
|
217
|
+
deps.hasPrisma = true;
|
|
218
|
+
const result = {
|
|
219
|
+
detected: { deps, trpc: false, middleware: true },
|
|
220
|
+
};
|
|
221
|
+
const list = buildDetectedList(result);
|
|
222
|
+
expect(list).toContain("clerk");
|
|
223
|
+
expect(list).toContain("prisma");
|
|
224
|
+
expect(list).toContain("middleware");
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
//# sourceMappingURL=score.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.test.js","sourceRoot":"","sources":["../../src/engine/score.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { Severity, Confidence, NextDepsIndex } from "../next/types.js";
|
|
2
|
+
export interface Finding {
|
|
3
|
+
ruleId: string;
|
|
4
|
+
severity: Severity;
|
|
5
|
+
confidence: Confidence;
|
|
6
|
+
message: string;
|
|
7
|
+
file: string;
|
|
8
|
+
line?: number;
|
|
9
|
+
column?: number;
|
|
10
|
+
endLine?: number;
|
|
11
|
+
endColumn?: number;
|
|
12
|
+
snippet?: string;
|
|
13
|
+
evidence: string[];
|
|
14
|
+
confidenceRationale: string;
|
|
15
|
+
remediation: string[];
|
|
16
|
+
tags: string[];
|
|
17
|
+
}
|
|
18
|
+
export interface Waiver {
|
|
19
|
+
ruleId: string;
|
|
20
|
+
file: string;
|
|
21
|
+
reason: string;
|
|
22
|
+
expiry?: string;
|
|
23
|
+
createdAt: string;
|
|
24
|
+
}
|
|
25
|
+
export interface WaiversFile {
|
|
26
|
+
version: 1;
|
|
27
|
+
waivers: Waiver[];
|
|
28
|
+
}
|
|
29
|
+
export interface Baseline {
|
|
30
|
+
version: 1;
|
|
31
|
+
prodcheckVersion: string;
|
|
32
|
+
configHash: string;
|
|
33
|
+
indexVersion: number;
|
|
34
|
+
createdAt: string;
|
|
35
|
+
score: number;
|
|
36
|
+
findingKeys: string[];
|
|
37
|
+
}
|
|
38
|
+
export interface ScanResult {
|
|
39
|
+
version: 1;
|
|
40
|
+
prodcheckVersion: string;
|
|
41
|
+
configHash: string;
|
|
42
|
+
indexVersion: number;
|
|
43
|
+
timestamp: string;
|
|
44
|
+
framework: string;
|
|
45
|
+
detected: {
|
|
46
|
+
deps: NextDepsIndex;
|
|
47
|
+
trpc: boolean;
|
|
48
|
+
middleware: boolean;
|
|
49
|
+
};
|
|
50
|
+
score: number;
|
|
51
|
+
findings: Finding[];
|
|
52
|
+
waivedFindings: Finding[];
|
|
53
|
+
summary: {
|
|
54
|
+
total: number;
|
|
55
|
+
critical: number;
|
|
56
|
+
high: number;
|
|
57
|
+
med: number;
|
|
58
|
+
low: number;
|
|
59
|
+
waived: number;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export interface ScoringConfig {
|
|
63
|
+
start: number;
|
|
64
|
+
penalties: Record<Severity, number>;
|
|
65
|
+
/** Multiplier applied to penalties based on finding confidence. Defaults to { high: 1.0, med: 0.25, low: 0.1 } */
|
|
66
|
+
confidenceWeights?: Record<Confidence, number>;
|
|
67
|
+
/** Max deduction any single rule can impose. Defaults to start * 0.35 */
|
|
68
|
+
maxPenaltyPerRule?: number;
|
|
69
|
+
}
|
|
70
|
+
export interface ProdcheckConfig {
|
|
71
|
+
framework: "next-app-router";
|
|
72
|
+
include: string[];
|
|
73
|
+
exclude: string[];
|
|
74
|
+
ci: {
|
|
75
|
+
failOn: Severity;
|
|
76
|
+
minConfidence: Confidence;
|
|
77
|
+
minScore: number;
|
|
78
|
+
maxNewCritical: number;
|
|
79
|
+
maxNewHigh?: number;
|
|
80
|
+
};
|
|
81
|
+
scoring: ScoringConfig;
|
|
82
|
+
hints: {
|
|
83
|
+
auth: {
|
|
84
|
+
functions: string[];
|
|
85
|
+
middlewareFiles: string[];
|
|
86
|
+
allowlistPaths: string[];
|
|
87
|
+
};
|
|
88
|
+
rateLimit: {
|
|
89
|
+
wrappers: string[];
|
|
90
|
+
allowlistPaths: string[];
|
|
91
|
+
};
|
|
92
|
+
tenancy: {
|
|
93
|
+
orgFieldNames: string[];
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
rules: Record<string, {
|
|
97
|
+
severity: Severity;
|
|
98
|
+
}>;
|
|
99
|
+
waiversFile: string;
|
|
100
|
+
license?: {
|
|
101
|
+
key?: string;
|
|
102
|
+
};
|
|
103
|
+
/** Reserved for governance module. Ignored by OSS core if governance not loaded. */
|
|
104
|
+
governance?: {
|
|
105
|
+
enabled?: boolean;
|
|
106
|
+
requiredRules?: string[];
|
|
107
|
+
waiver?: {
|
|
108
|
+
requireReason?: boolean;
|
|
109
|
+
requireExpiry?: boolean;
|
|
110
|
+
maxDays?: number;
|
|
111
|
+
};
|
|
112
|
+
thresholds?: {
|
|
113
|
+
minScore?: number;
|
|
114
|
+
maxCritical?: number;
|
|
115
|
+
};
|
|
116
|
+
report?: {
|
|
117
|
+
preAudit?: boolean;
|
|
118
|
+
format?: "md" | "json" | "pdf";
|
|
119
|
+
outputDir?: string;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/engine/version.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,eAAO,MAAM,iBAAiB,UAAU,CAAC;AACzC,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAU1D"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export const PRODCHECK_VERSION = "0.2.7";
|
|
3
|
+
export const INDEX_VERSION = 1;
|
|
4
|
+
export function hashConfig(config) {
|
|
5
|
+
const normalized = JSON.stringify({
|
|
6
|
+
framework: config.framework,
|
|
7
|
+
include: config.include,
|
|
8
|
+
exclude: config.exclude,
|
|
9
|
+
hints: config.hints,
|
|
10
|
+
rules: config.rules,
|
|
11
|
+
scoring: config.scoring,
|
|
12
|
+
});
|
|
13
|
+
return createHash("sha256").update(normalized).digest("hex").slice(0, 12);
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/engine/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC;AACzC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC;AAE/B,MAAM,UAAU,UAAU,CAAC,MAAuB;IAChD,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC,CAAC;IACH,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Finding, Waiver } from "./types.js";
|
|
2
|
+
export declare function loadWaivers(rootDir: string, waiversFile: string): Waiver[];
|
|
3
|
+
export declare function saveWaivers(rootDir: string, waiversFile: string, waivers: Waiver[]): void;
|
|
4
|
+
export declare function addWaiver(rootDir: string, waiversFile: string, waiver: Omit<Waiver, "createdAt">): Waiver;
|
|
5
|
+
export declare function applyWaivers(findings: Finding[], waivers: Waiver[]): {
|
|
6
|
+
active: Finding[];
|
|
7
|
+
waived: Finding[];
|
|
8
|
+
};
|
|
9
|
+
//# sourceMappingURL=waivers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waivers.d.ts","sourceRoot":"","sources":["../../src/engine/waivers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAe,MAAM,YAAY,CAAC;AAE/D,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAY1E;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAIzF;AAED,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC,GAChC,MAAM,CASR;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,OAAO,EAAE,EACnB,OAAO,EAAE,MAAM,EAAE,GAChB;IAAE,MAAM,EAAE,OAAO,EAAE,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAmB1C"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
export function loadWaivers(rootDir, waiversFile) {
|
|
4
|
+
const abs = path.join(rootDir, waiversFile);
|
|
5
|
+
if (!existsSync(abs))
|
|
6
|
+
return [];
|
|
7
|
+
let raw;
|
|
8
|
+
try {
|
|
9
|
+
raw = JSON.parse(readFileSync(abs, "utf8"));
|
|
10
|
+
}
|
|
11
|
+
catch (err) {
|
|
12
|
+
throw new Error(`Failed to parse ${abs}: ${err instanceof Error ? err.message : String(err)}`);
|
|
13
|
+
}
|
|
14
|
+
// Support both legacy array format and versioned format
|
|
15
|
+
if (Array.isArray(raw))
|
|
16
|
+
return raw;
|
|
17
|
+
return raw.waivers ?? [];
|
|
18
|
+
}
|
|
19
|
+
export function saveWaivers(rootDir, waiversFile, waivers) {
|
|
20
|
+
const abs = path.join(rootDir, waiversFile);
|
|
21
|
+
const file = { version: 1, waivers };
|
|
22
|
+
writeFileSync(abs, JSON.stringify(file, null, 2) + "\n");
|
|
23
|
+
}
|
|
24
|
+
export function addWaiver(rootDir, waiversFile, waiver) {
|
|
25
|
+
const waivers = loadWaivers(rootDir, waiversFile);
|
|
26
|
+
const full = {
|
|
27
|
+
...waiver,
|
|
28
|
+
createdAt: new Date().toISOString(),
|
|
29
|
+
};
|
|
30
|
+
waivers.push(full);
|
|
31
|
+
saveWaivers(rootDir, waiversFile, waivers);
|
|
32
|
+
return full;
|
|
33
|
+
}
|
|
34
|
+
export function applyWaivers(findings, waivers) {
|
|
35
|
+
const active = [];
|
|
36
|
+
const waived = [];
|
|
37
|
+
for (const f of findings) {
|
|
38
|
+
const hasWaiver = waivers.some((w) => w.ruleId === f.ruleId &&
|
|
39
|
+
w.file === f.file &&
|
|
40
|
+
!isExpired(w));
|
|
41
|
+
if (hasWaiver) {
|
|
42
|
+
waived.push(f);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
active.push(f);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { active, waived };
|
|
49
|
+
}
|
|
50
|
+
function isExpired(w) {
|
|
51
|
+
if (!w.expiry)
|
|
52
|
+
return false;
|
|
53
|
+
return new Date(w.expiry) < new Date();
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=waivers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waivers.js","sourceRoot":"","sources":["../../src/engine/waivers.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAGlE,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,WAAmB;IAC9D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC5C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChC,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,mBAAmB,GAAG,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACjG,CAAC;IACD,wDAAwD;IACxD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAe,CAAC;IAC/C,OAAQ,GAAmB,CAAC,OAAO,IAAI,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,WAAmB,EAAE,OAAiB;IACjF,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAgB,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC;IAClD,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,OAAe,EACf,WAAmB,EACnB,MAAiC;IAEjC,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,IAAI,GAAW;QACnB,GAAG,MAAM;QACT,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAC3C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAmB,EACnB,OAAiB;IAEjB,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAc,EAAE,CAAC;IAE7B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAC5B,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;YACrB,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI;YACjB,CAAC,SAAS,CAAC,CAAC,CAAC,CAChB,CAAC;QACF,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED,SAAS,SAAS,CAAC,CAAS;IAC1B,IAAI,CAAC,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC5B,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"waivers.test.d.ts","sourceRoot":"","sources":["../../src/engine/waivers.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync, readFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { loadWaivers, saveWaivers, addWaiver, applyWaivers } from "./waivers.js";
|
|
6
|
+
function makeFinding(overrides = {}) {
|
|
7
|
+
return {
|
|
8
|
+
ruleId: "TEST-RULE",
|
|
9
|
+
severity: "high",
|
|
10
|
+
confidence: "high",
|
|
11
|
+
message: "test",
|
|
12
|
+
file: "test.ts",
|
|
13
|
+
evidence: [],
|
|
14
|
+
confidenceRationale: "",
|
|
15
|
+
remediation: [],
|
|
16
|
+
tags: [],
|
|
17
|
+
...overrides,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
describe("loadWaivers", () => {
|
|
21
|
+
let tmpDir;
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
tmpDir = mkdtempSync(path.join(os.tmpdir(), "prodcheck-waivers-"));
|
|
24
|
+
});
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
it("returns empty array when file does not exist", () => {
|
|
29
|
+
expect(loadWaivers(tmpDir, "waivers.json")).toEqual([]);
|
|
30
|
+
});
|
|
31
|
+
it("loads legacy array format", () => {
|
|
32
|
+
const waivers = [{ ruleId: "R1", file: "a.ts", reason: "ok", createdAt: "2024-01-01" }];
|
|
33
|
+
writeFileSync(path.join(tmpDir, "waivers.json"), JSON.stringify(waivers));
|
|
34
|
+
expect(loadWaivers(tmpDir, "waivers.json")).toEqual(waivers);
|
|
35
|
+
});
|
|
36
|
+
it("loads versioned format", () => {
|
|
37
|
+
const file = { version: 1, waivers: [{ ruleId: "R1", file: "a.ts", reason: "ok", createdAt: "2024-01-01" }] };
|
|
38
|
+
writeFileSync(path.join(tmpDir, "waivers.json"), JSON.stringify(file));
|
|
39
|
+
expect(loadWaivers(tmpDir, "waivers.json")).toHaveLength(1);
|
|
40
|
+
});
|
|
41
|
+
it("throws on malformed JSON", () => {
|
|
42
|
+
writeFileSync(path.join(tmpDir, "waivers.json"), "not json");
|
|
43
|
+
expect(() => loadWaivers(tmpDir, "waivers.json")).toThrow("Failed to parse");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe("saveWaivers / addWaiver", () => {
|
|
47
|
+
let tmpDir;
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
tmpDir = mkdtempSync(path.join(os.tmpdir(), "prodcheck-waivers-"));
|
|
50
|
+
});
|
|
51
|
+
afterEach(() => {
|
|
52
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
53
|
+
});
|
|
54
|
+
it("saves and loads roundtrip", () => {
|
|
55
|
+
const waivers = [{ ruleId: "R1", file: "a.ts", reason: "ok", createdAt: "2024-01-01" }];
|
|
56
|
+
saveWaivers(tmpDir, "w.json", waivers);
|
|
57
|
+
const loaded = loadWaivers(tmpDir, "w.json");
|
|
58
|
+
expect(loaded).toEqual(waivers);
|
|
59
|
+
});
|
|
60
|
+
it("saves in versioned format", () => {
|
|
61
|
+
saveWaivers(tmpDir, "w.json", []);
|
|
62
|
+
const raw = JSON.parse(readFileSync(path.join(tmpDir, "w.json"), "utf8"));
|
|
63
|
+
expect(raw.version).toBe(1);
|
|
64
|
+
expect(raw.waivers).toEqual([]);
|
|
65
|
+
});
|
|
66
|
+
it("addWaiver appends and sets createdAt", () => {
|
|
67
|
+
const waiver = addWaiver(tmpDir, "w.json", { ruleId: "R1", file: "a.ts", reason: "test" });
|
|
68
|
+
expect(waiver.createdAt).toBeDefined();
|
|
69
|
+
expect(waiver.ruleId).toBe("R1");
|
|
70
|
+
const loaded = loadWaivers(tmpDir, "w.json");
|
|
71
|
+
expect(loaded).toHaveLength(1);
|
|
72
|
+
});
|
|
73
|
+
it("addWaiver accumulates waivers", () => {
|
|
74
|
+
addWaiver(tmpDir, "w.json", { ruleId: "R1", file: "a.ts", reason: "first" });
|
|
75
|
+
addWaiver(tmpDir, "w.json", { ruleId: "R2", file: "b.ts", reason: "second" });
|
|
76
|
+
const loaded = loadWaivers(tmpDir, "w.json");
|
|
77
|
+
expect(loaded).toHaveLength(2);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
describe("applyWaivers", () => {
|
|
81
|
+
it("returns all findings as active when no waivers", () => {
|
|
82
|
+
const findings = [makeFinding({ ruleId: "R1", file: "a.ts" })];
|
|
83
|
+
const { active, waived } = applyWaivers(findings, []);
|
|
84
|
+
expect(active).toHaveLength(1);
|
|
85
|
+
expect(waived).toHaveLength(0);
|
|
86
|
+
});
|
|
87
|
+
it("waives matching findings", () => {
|
|
88
|
+
const findings = [
|
|
89
|
+
makeFinding({ ruleId: "R1", file: "a.ts" }),
|
|
90
|
+
makeFinding({ ruleId: "R2", file: "b.ts" }),
|
|
91
|
+
];
|
|
92
|
+
const waivers = [
|
|
93
|
+
{ ruleId: "R1", file: "a.ts", reason: "ok", createdAt: "2024-01-01" },
|
|
94
|
+
];
|
|
95
|
+
const { active, waived } = applyWaivers(findings, waivers);
|
|
96
|
+
expect(active).toHaveLength(1);
|
|
97
|
+
expect(active[0].ruleId).toBe("R2");
|
|
98
|
+
expect(waived).toHaveLength(1);
|
|
99
|
+
expect(waived[0].ruleId).toBe("R1");
|
|
100
|
+
});
|
|
101
|
+
it("does not waive when ruleId differs", () => {
|
|
102
|
+
const findings = [makeFinding({ ruleId: "R1", file: "a.ts" })];
|
|
103
|
+
const waivers = [
|
|
104
|
+
{ ruleId: "R2", file: "a.ts", reason: "ok", createdAt: "2024-01-01" },
|
|
105
|
+
];
|
|
106
|
+
const { active, waived } = applyWaivers(findings, waivers);
|
|
107
|
+
expect(active).toHaveLength(1);
|
|
108
|
+
expect(waived).toHaveLength(0);
|
|
109
|
+
});
|
|
110
|
+
it("does not waive when file differs", () => {
|
|
111
|
+
const findings = [makeFinding({ ruleId: "R1", file: "a.ts" })];
|
|
112
|
+
const waivers = [
|
|
113
|
+
{ ruleId: "R1", file: "b.ts", reason: "ok", createdAt: "2024-01-01" },
|
|
114
|
+
];
|
|
115
|
+
const { active, waived } = applyWaivers(findings, waivers);
|
|
116
|
+
expect(active).toHaveLength(1);
|
|
117
|
+
expect(waived).toHaveLength(0);
|
|
118
|
+
});
|
|
119
|
+
it("ignores expired waivers", () => {
|
|
120
|
+
const findings = [makeFinding({ ruleId: "R1", file: "a.ts" })];
|
|
121
|
+
const waivers = [
|
|
122
|
+
{ ruleId: "R1", file: "a.ts", reason: "ok", expiry: "2020-01-01", createdAt: "2019-01-01" },
|
|
123
|
+
];
|
|
124
|
+
const { active, waived } = applyWaivers(findings, waivers);
|
|
125
|
+
expect(active).toHaveLength(1);
|
|
126
|
+
expect(waived).toHaveLength(0);
|
|
127
|
+
});
|
|
128
|
+
it("applies non-expired waivers", () => {
|
|
129
|
+
const findings = [makeFinding({ ruleId: "R1", file: "a.ts" })];
|
|
130
|
+
const waivers = [
|
|
131
|
+
{ ruleId: "R1", file: "a.ts", reason: "ok", expiry: "2099-01-01", createdAt: "2024-01-01" },
|
|
132
|
+
];
|
|
133
|
+
const { active, waived } = applyWaivers(findings, waivers);
|
|
134
|
+
expect(active).toHaveLength(0);
|
|
135
|
+
expect(waived).toHaveLength(1);
|
|
136
|
+
});
|
|
137
|
+
it("applies waivers without expiry", () => {
|
|
138
|
+
const findings = [makeFinding({ ruleId: "R1", file: "a.ts" })];
|
|
139
|
+
const waivers = [
|
|
140
|
+
{ ruleId: "R1", file: "a.ts", reason: "ok", createdAt: "2024-01-01" },
|
|
141
|
+
];
|
|
142
|
+
const { active, waived } = applyWaivers(findings, waivers);
|
|
143
|
+
expect(active).toHaveLength(0);
|
|
144
|
+
expect(waived).toHaveLength(1);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
//# sourceMappingURL=waivers.test.js.map
|