@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.
Files changed (239) hide show
  1. package/README.md +252 -0
  2. package/bin/prodcheck.mjs +2 -0
  3. package/dist/cli/commands/baseline.d.ts +7 -0
  4. package/dist/cli/commands/baseline.d.ts.map +1 -0
  5. package/dist/cli/commands/baseline.js +22 -0
  6. package/dist/cli/commands/baseline.js.map +1 -0
  7. package/dist/cli/commands/ci.d.ts +14 -0
  8. package/dist/cli/commands/ci.d.ts.map +1 -0
  9. package/dist/cli/commands/ci.js +104 -0
  10. package/dist/cli/commands/ci.js.map +1 -0
  11. package/dist/cli/commands/explain.d.ts +2 -0
  12. package/dist/cli/commands/explain.d.ts.map +1 -0
  13. package/dist/cli/commands/explain.js +20 -0
  14. package/dist/cli/commands/explain.js.map +1 -0
  15. package/dist/cli/commands/init.d.ts +7 -0
  16. package/dist/cli/commands/init.d.ts.map +1 -0
  17. package/dist/cli/commands/init.js +127 -0
  18. package/dist/cli/commands/init.js.map +1 -0
  19. package/dist/cli/commands/rules.d.ts +2 -0
  20. package/dist/cli/commands/rules.d.ts.map +1 -0
  21. package/dist/cli/commands/rules.js +13 -0
  22. package/dist/cli/commands/rules.js.map +1 -0
  23. package/dist/cli/commands/scan.d.ts +10 -0
  24. package/dist/cli/commands/scan.d.ts.map +1 -0
  25. package/dist/cli/commands/scan.js +65 -0
  26. package/dist/cli/commands/scan.js.map +1 -0
  27. package/dist/cli/commands/waive.d.ts +8 -0
  28. package/dist/cli/commands/waive.d.ts.map +1 -0
  29. package/dist/cli/commands/waive.js +34 -0
  30. package/dist/cli/commands/waive.js.map +1 -0
  31. package/dist/cli/index.d.ts +2 -0
  32. package/dist/cli/index.d.ts.map +1 -0
  33. package/dist/cli/index.js +64 -0
  34. package/dist/cli/index.js.map +1 -0
  35. package/dist/engine/baseline.d.ts +11 -0
  36. package/dist/engine/baseline.d.ts.map +1 -0
  37. package/dist/engine/baseline.js +39 -0
  38. package/dist/engine/baseline.js.map +1 -0
  39. package/dist/engine/baseline.test.d.ts +2 -0
  40. package/dist/engine/baseline.test.d.ts.map +1 -0
  41. package/dist/engine/baseline.test.js +135 -0
  42. package/dist/engine/baseline.test.js.map +1 -0
  43. package/dist/engine/config.d.ts +8 -0
  44. package/dist/engine/config.d.ts.map +1 -0
  45. package/dist/engine/config.js +134 -0
  46. package/dist/engine/config.js.map +1 -0
  47. package/dist/engine/config.test.d.ts +2 -0
  48. package/dist/engine/config.test.d.ts.map +1 -0
  49. package/dist/engine/config.test.js +107 -0
  50. package/dist/engine/config.test.js.map +1 -0
  51. package/dist/engine/extensions/load.d.ts +11 -0
  52. package/dist/engine/extensions/load.d.ts.map +1 -0
  53. package/dist/engine/extensions/load.js +26 -0
  54. package/dist/engine/extensions/load.js.map +1 -0
  55. package/dist/engine/extensions/registry.d.ts +5 -0
  56. package/dist/engine/extensions/registry.d.ts.map +1 -0
  57. package/dist/engine/extensions/registry.js +11 -0
  58. package/dist/engine/extensions/registry.js.map +1 -0
  59. package/dist/engine/extensions/types.d.ts +51 -0
  60. package/dist/engine/extensions/types.d.ts.map +1 -0
  61. package/dist/engine/extensions/types.js +2 -0
  62. package/dist/engine/extensions/types.js.map +1 -0
  63. package/dist/engine/license.d.ts +40 -0
  64. package/dist/engine/license.d.ts.map +1 -0
  65. package/dist/engine/license.js +104 -0
  66. package/dist/engine/license.js.map +1 -0
  67. package/dist/engine/report.d.ts +5 -0
  68. package/dist/engine/report.d.ts.map +1 -0
  69. package/dist/engine/report.js +115 -0
  70. package/dist/engine/report.js.map +1 -0
  71. package/dist/engine/run.d.ts +11 -0
  72. package/dist/engine/run.d.ts.map +1 -0
  73. package/dist/engine/run.js +105 -0
  74. package/dist/engine/run.js.map +1 -0
  75. package/dist/engine/sarif.d.ts +3 -0
  76. package/dist/engine/sarif.d.ts.map +1 -0
  77. package/dist/engine/sarif.js +58 -0
  78. package/dist/engine/sarif.js.map +1 -0
  79. package/dist/engine/sarif.test.d.ts +2 -0
  80. package/dist/engine/sarif.test.d.ts.map +1 -0
  81. package/dist/engine/sarif.test.js +152 -0
  82. package/dist/engine/sarif.test.js.map +1 -0
  83. package/dist/engine/score.d.ts +13 -0
  84. package/dist/engine/score.d.ts.map +1 -0
  85. package/dist/engine/score.js +116 -0
  86. package/dist/engine/score.js.map +1 -0
  87. package/dist/engine/score.test.d.ts +2 -0
  88. package/dist/engine/score.test.d.ts.map +1 -0
  89. package/dist/engine/score.test.js +227 -0
  90. package/dist/engine/score.test.js.map +1 -0
  91. package/dist/engine/types.d.ts +123 -0
  92. package/dist/engine/types.d.ts.map +1 -0
  93. package/dist/engine/types.js +2 -0
  94. package/dist/engine/types.js.map +1 -0
  95. package/dist/engine/version.d.ts +5 -0
  96. package/dist/engine/version.d.ts.map +1 -0
  97. package/dist/engine/version.js +15 -0
  98. package/dist/engine/version.js.map +1 -0
  99. package/dist/engine/waivers.d.ts +9 -0
  100. package/dist/engine/waivers.d.ts.map +1 -0
  101. package/dist/engine/waivers.js +55 -0
  102. package/dist/engine/waivers.js.map +1 -0
  103. package/dist/engine/waivers.test.d.ts +2 -0
  104. package/dist/engine/waivers.test.d.ts.map +1 -0
  105. package/dist/engine/waivers.test.js +147 -0
  106. package/dist/engine/waivers.test.js.map +1 -0
  107. package/dist/index.d.ts +14 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +12 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/next/deps.d.ts +4 -0
  112. package/dist/next/deps.d.ts.map +1 -0
  113. package/dist/next/deps.js +118 -0
  114. package/dist/next/deps.js.map +1 -0
  115. package/dist/next/deps.test.d.ts +2 -0
  116. package/dist/next/deps.test.d.ts.map +1 -0
  117. package/dist/next/deps.test.js +249 -0
  118. package/dist/next/deps.test.js.map +1 -0
  119. package/dist/next/detect.d.ts +10 -0
  120. package/dist/next/detect.d.ts.map +1 -0
  121. package/dist/next/detect.js +57 -0
  122. package/dist/next/detect.js.map +1 -0
  123. package/dist/next/detect.test.d.ts +2 -0
  124. package/dist/next/detect.test.d.ts.map +1 -0
  125. package/dist/next/detect.test.js +74 -0
  126. package/dist/next/detect.test.js.map +1 -0
  127. package/dist/next/index.d.ts +5 -0
  128. package/dist/next/index.d.ts.map +1 -0
  129. package/dist/next/index.js +59 -0
  130. package/dist/next/index.js.map +1 -0
  131. package/dist/next/middleware.d.ts +3 -0
  132. package/dist/next/middleware.d.ts.map +1 -0
  133. package/dist/next/middleware.js +48 -0
  134. package/dist/next/middleware.js.map +1 -0
  135. package/dist/next/middleware.test.d.ts +2 -0
  136. package/dist/next/middleware.test.d.ts.map +1 -0
  137. package/dist/next/middleware.test.js +203 -0
  138. package/dist/next/middleware.test.js.map +1 -0
  139. package/dist/next/routes.d.ts +10 -0
  140. package/dist/next/routes.d.ts.map +1 -0
  141. package/dist/next/routes.js +172 -0
  142. package/dist/next/routes.js.map +1 -0
  143. package/dist/next/routes.test.d.ts +2 -0
  144. package/dist/next/routes.test.d.ts.map +1 -0
  145. package/dist/next/routes.test.js +175 -0
  146. package/dist/next/routes.test.js.map +1 -0
  147. package/dist/next/server-actions.d.ts +4 -0
  148. package/dist/next/server-actions.d.ts.map +1 -0
  149. package/dist/next/server-actions.js +107 -0
  150. package/dist/next/server-actions.js.map +1 -0
  151. package/dist/next/server-actions.test.d.ts +2 -0
  152. package/dist/next/server-actions.test.d.ts.map +1 -0
  153. package/dist/next/server-actions.test.js +138 -0
  154. package/dist/next/server-actions.test.js.map +1 -0
  155. package/dist/next/trpc.d.ts +3 -0
  156. package/dist/next/trpc.d.ts.map +1 -0
  157. package/dist/next/trpc.js +312 -0
  158. package/dist/next/trpc.js.map +1 -0
  159. package/dist/next/types.d.ts +144 -0
  160. package/dist/next/types.d.ts.map +1 -0
  161. package/dist/next/types.js +2 -0
  162. package/dist/next/types.js.map +1 -0
  163. package/dist/next/wrappers.d.ts +10 -0
  164. package/dist/next/wrappers.d.ts.map +1 -0
  165. package/dist/next/wrappers.js +536 -0
  166. package/dist/next/wrappers.js.map +1 -0
  167. package/dist/next/wrappers.test.d.ts +2 -0
  168. package/dist/next/wrappers.test.d.ts.map +1 -0
  169. package/dist/next/wrappers.test.js +361 -0
  170. package/dist/next/wrappers.test.js.map +1 -0
  171. package/dist/rules/auth-boundary-missing.d.ts +5 -0
  172. package/dist/rules/auth-boundary-missing.d.ts.map +1 -0
  173. package/dist/rules/auth-boundary-missing.js +463 -0
  174. package/dist/rules/auth-boundary-missing.js.map +1 -0
  175. package/dist/rules/auth-boundary-missing.test.d.ts +2 -0
  176. package/dist/rules/auth-boundary-missing.test.d.ts.map +1 -0
  177. package/dist/rules/auth-boundary-missing.test.js +492 -0
  178. package/dist/rules/auth-boundary-missing.test.js.map +1 -0
  179. package/dist/rules/index.d.ts +12 -0
  180. package/dist/rules/index.d.ts.map +1 -0
  181. package/dist/rules/index.js +95 -0
  182. package/dist/rules/index.js.map +1 -0
  183. package/dist/rules/input-validation-missing.d.ts +5 -0
  184. package/dist/rules/input-validation-missing.d.ts.map +1 -0
  185. package/dist/rules/input-validation-missing.js +272 -0
  186. package/dist/rules/input-validation-missing.js.map +1 -0
  187. package/dist/rules/input-validation-missing.test.d.ts +2 -0
  188. package/dist/rules/input-validation-missing.test.d.ts.map +1 -0
  189. package/dist/rules/input-validation-missing.test.js +449 -0
  190. package/dist/rules/input-validation-missing.test.js.map +1 -0
  191. package/dist/rules/rate-limit-missing.d.ts +5 -0
  192. package/dist/rules/rate-limit-missing.d.ts.map +1 -0
  193. package/dist/rules/rate-limit-missing.js +316 -0
  194. package/dist/rules/rate-limit-missing.js.map +1 -0
  195. package/dist/rules/rate-limit-missing.test.d.ts +2 -0
  196. package/dist/rules/rate-limit-missing.test.d.ts.map +1 -0
  197. package/dist/rules/rate-limit-missing.test.js +381 -0
  198. package/dist/rules/rate-limit-missing.test.js.map +1 -0
  199. package/dist/rules/tenancy-scope-missing.d.ts +5 -0
  200. package/dist/rules/tenancy-scope-missing.d.ts.map +1 -0
  201. package/dist/rules/tenancy-scope-missing.js +149 -0
  202. package/dist/rules/tenancy-scope-missing.js.map +1 -0
  203. package/dist/rules/wrapper-unrecognized.d.ts +5 -0
  204. package/dist/rules/wrapper-unrecognized.d.ts.map +1 -0
  205. package/dist/rules/wrapper-unrecognized.js +81 -0
  206. package/dist/rules/wrapper-unrecognized.js.map +1 -0
  207. package/dist/util/hof.d.ts +22 -0
  208. package/dist/util/hof.d.ts.map +1 -0
  209. package/dist/util/hof.js +99 -0
  210. package/dist/util/hof.js.map +1 -0
  211. package/dist/util/hof.test.d.ts +2 -0
  212. package/dist/util/hof.test.d.ts.map +1 -0
  213. package/dist/util/hof.test.js +79 -0
  214. package/dist/util/hof.test.js.map +1 -0
  215. package/dist/util/monorepo.d.ts +6 -0
  216. package/dist/util/monorepo.d.ts.map +1 -0
  217. package/dist/util/monorepo.js +29 -0
  218. package/dist/util/monorepo.js.map +1 -0
  219. package/dist/util/outbound-fetch.d.ts +14 -0
  220. package/dist/util/outbound-fetch.d.ts.map +1 -0
  221. package/dist/util/outbound-fetch.js +59 -0
  222. package/dist/util/outbound-fetch.js.map +1 -0
  223. package/dist/util/outbound-fetch.test.d.ts +2 -0
  224. package/dist/util/outbound-fetch.test.d.ts.map +1 -0
  225. package/dist/util/outbound-fetch.test.js +83 -0
  226. package/dist/util/outbound-fetch.test.js.map +1 -0
  227. package/dist/util/paths.d.ts +6 -0
  228. package/dist/util/paths.d.ts.map +1 -0
  229. package/dist/util/paths.js +18 -0
  230. package/dist/util/paths.js.map +1 -0
  231. package/dist/util/resolve.d.ts +30 -0
  232. package/dist/util/resolve.d.ts.map +1 -0
  233. package/dist/util/resolve.js +306 -0
  234. package/dist/util/resolve.js.map +1 -0
  235. package/dist/util/resolve.test.d.ts +2 -0
  236. package/dist/util/resolve.test.d.ts.map +1 -0
  237. package/dist/util/resolve.test.js +186 -0
  238. package/dist/util/resolve.test.js.map +1 -0
  239. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/engine/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ import type { ProdcheckConfig } from "./types.js";
2
+ export declare const PRODCHECK_VERSION = "0.2.7";
3
+ export declare const INDEX_VERSION = 1;
4
+ export declare function hashConfig(config: ProdcheckConfig): string;
5
+ //# sourceMappingURL=version.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=waivers.test.d.ts.map
@@ -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