@fourteensystems/shipguard 0.1.0 → 0.2.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.
Files changed (112) hide show
  1. package/README.md +46 -112
  2. package/dist/cli/commands/init.d.ts.map +1 -1
  3. package/dist/cli/commands/init.js +36 -0
  4. package/dist/cli/commands/init.js.map +1 -1
  5. package/dist/engine/baseline.test.d.ts +2 -0
  6. package/dist/engine/baseline.test.d.ts.map +1 -0
  7. package/dist/engine/baseline.test.js +135 -0
  8. package/dist/engine/baseline.test.js.map +1 -0
  9. package/dist/engine/config.d.ts.map +1 -1
  10. package/dist/engine/config.js +2 -0
  11. package/dist/engine/config.js.map +1 -1
  12. package/dist/engine/config.test.d.ts +2 -0
  13. package/dist/engine/config.test.d.ts.map +1 -0
  14. package/dist/engine/config.test.js +107 -0
  15. package/dist/engine/config.test.js.map +1 -0
  16. package/dist/engine/sarif.test.d.ts +2 -0
  17. package/dist/engine/sarif.test.d.ts.map +1 -0
  18. package/dist/engine/sarif.test.js +152 -0
  19. package/dist/engine/sarif.test.js.map +1 -0
  20. package/dist/engine/score.d.ts.map +1 -1
  21. package/dist/engine/score.js +13 -2
  22. package/dist/engine/score.js.map +1 -1
  23. package/dist/engine/score.test.d.ts +2 -0
  24. package/dist/engine/score.test.d.ts.map +1 -0
  25. package/dist/engine/score.test.js +191 -0
  26. package/dist/engine/score.test.js.map +1 -0
  27. package/dist/engine/types.d.ts +2 -0
  28. package/dist/engine/types.d.ts.map +1 -1
  29. package/dist/engine/waivers.test.d.ts +2 -0
  30. package/dist/engine/waivers.test.d.ts.map +1 -0
  31. package/dist/engine/waivers.test.js +147 -0
  32. package/dist/engine/waivers.test.js.map +1 -0
  33. package/dist/next/deps.d.ts.map +1 -1
  34. package/dist/next/deps.js +16 -0
  35. package/dist/next/deps.js.map +1 -1
  36. package/dist/next/deps.test.d.ts +2 -0
  37. package/dist/next/deps.test.d.ts.map +1 -0
  38. package/dist/next/deps.test.js +249 -0
  39. package/dist/next/deps.test.js.map +1 -0
  40. package/dist/next/detect.test.d.ts +2 -0
  41. package/dist/next/detect.test.d.ts.map +1 -0
  42. package/dist/next/detect.test.js +74 -0
  43. package/dist/next/detect.test.js.map +1 -0
  44. package/dist/next/index.d.ts.map +1 -1
  45. package/dist/next/index.js +11 -0
  46. package/dist/next/index.js.map +1 -1
  47. package/dist/next/middleware.d.ts.map +1 -1
  48. package/dist/next/middleware.js +15 -0
  49. package/dist/next/middleware.js.map +1 -1
  50. package/dist/next/middleware.test.d.ts +2 -0
  51. package/dist/next/middleware.test.d.ts.map +1 -0
  52. package/dist/next/middleware.test.js +203 -0
  53. package/dist/next/middleware.test.js.map +1 -0
  54. package/dist/next/routes.test.d.ts +2 -0
  55. package/dist/next/routes.test.d.ts.map +1 -0
  56. package/dist/next/routes.test.js +110 -0
  57. package/dist/next/routes.test.js.map +1 -0
  58. package/dist/next/server-actions.test.d.ts +2 -0
  59. package/dist/next/server-actions.test.d.ts.map +1 -0
  60. package/dist/next/server-actions.test.js +138 -0
  61. package/dist/next/server-actions.test.js.map +1 -0
  62. package/dist/next/trpc.d.ts.map +1 -1
  63. package/dist/next/trpc.js +4 -31
  64. package/dist/next/trpc.js.map +1 -1
  65. package/dist/next/types.d.ts +34 -0
  66. package/dist/next/types.d.ts.map +1 -1
  67. package/dist/next/wrappers.d.ts +10 -0
  68. package/dist/next/wrappers.d.ts.map +1 -0
  69. package/dist/next/wrappers.js +477 -0
  70. package/dist/next/wrappers.js.map +1 -0
  71. package/dist/next/wrappers.test.d.ts +2 -0
  72. package/dist/next/wrappers.test.d.ts.map +1 -0
  73. package/dist/next/wrappers.test.js +361 -0
  74. package/dist/next/wrappers.test.js.map +1 -0
  75. package/dist/rules/auth-boundary-missing.d.ts.map +1 -1
  76. package/dist/rules/auth-boundary-missing.js +23 -80
  77. package/dist/rules/auth-boundary-missing.js.map +1 -1
  78. package/dist/rules/index.d.ts.map +1 -1
  79. package/dist/rules/index.js +12 -0
  80. package/dist/rules/index.js.map +1 -1
  81. package/dist/rules/rate-limit-missing.d.ts.map +1 -1
  82. package/dist/rules/rate-limit-missing.js +110 -71
  83. package/dist/rules/rate-limit-missing.js.map +1 -1
  84. package/dist/rules/rate-limit-missing.test.d.ts +2 -0
  85. package/dist/rules/rate-limit-missing.test.d.ts.map +1 -0
  86. package/dist/rules/rate-limit-missing.test.js +325 -0
  87. package/dist/rules/rate-limit-missing.test.js.map +1 -0
  88. package/dist/rules/wrapper-unrecognized.d.ts +5 -0
  89. package/dist/rules/wrapper-unrecognized.d.ts.map +1 -0
  90. package/dist/rules/wrapper-unrecognized.js +76 -0
  91. package/dist/rules/wrapper-unrecognized.js.map +1 -0
  92. package/dist/util/hof.d.ts +22 -0
  93. package/dist/util/hof.d.ts.map +1 -0
  94. package/dist/util/hof.js +99 -0
  95. package/dist/util/hof.js.map +1 -0
  96. package/dist/util/hof.test.d.ts +2 -0
  97. package/dist/util/hof.test.d.ts.map +1 -0
  98. package/dist/util/hof.test.js +79 -0
  99. package/dist/util/hof.test.js.map +1 -0
  100. package/dist/util/monorepo.d.ts +6 -0
  101. package/dist/util/monorepo.d.ts.map +1 -0
  102. package/dist/util/monorepo.js +29 -0
  103. package/dist/util/monorepo.js.map +1 -0
  104. package/dist/util/resolve.d.ts +30 -0
  105. package/dist/util/resolve.d.ts.map +1 -0
  106. package/dist/util/resolve.js +306 -0
  107. package/dist/util/resolve.js.map +1 -0
  108. package/dist/util/resolve.test.d.ts +2 -0
  109. package/dist/util/resolve.test.d.ts.map +1 -0
  110. package/dist/util/resolve.test.js +186 -0
  111. package/dist/util/resolve.test.js.map +1 -0
  112. package/package.json +1 -1
@@ -0,0 +1,325 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdirSync, writeFileSync, rmSync } from "node:fs";
3
+ import path from "node:path";
4
+ import { run } from "./rate-limit-missing.js";
5
+ /* ------------------------------------------------------------------ */
6
+ /* Helpers */
7
+ /* ------------------------------------------------------------------ */
8
+ const NO_SIGNALS = {
9
+ hasMutationEvidence: false,
10
+ hasDbWriteEvidence: false,
11
+ hasStripeWriteEvidence: false,
12
+ mutationDetails: [],
13
+ };
14
+ const MUTATION_SIGNALS = {
15
+ hasMutationEvidence: true,
16
+ hasDbWriteEvidence: true,
17
+ hasStripeWriteEvidence: false,
18
+ mutationDetails: ["prisma.create"],
19
+ };
20
+ function protectionSummary(opts) {
21
+ return {
22
+ auth: {
23
+ satisfied: opts.authSatisfied ?? false,
24
+ enforced: false,
25
+ sources: opts.authSatisfied ? ["direct"] : [],
26
+ details: [],
27
+ unverifiedWrappers: [],
28
+ },
29
+ rateLimit: {
30
+ satisfied: opts.rlSatisfied ?? false,
31
+ enforced: false,
32
+ sources: [],
33
+ details: [],
34
+ unverifiedWrappers: opts.unverifiedWrappers ?? [],
35
+ },
36
+ };
37
+ }
38
+ let tmpDir;
39
+ beforeEach(() => {
40
+ tmpDir = path.join("/tmp", `shipguard-rl-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
41
+ mkdirSync(tmpDir, { recursive: true });
42
+ });
43
+ afterEach(() => {
44
+ rmSync(tmpDir, { recursive: true, force: true });
45
+ });
46
+ /** Create a route file on disk and return a NextRoute pointing to it */
47
+ function createRoute(relPath, source, overrides = {}) {
48
+ const fullPath = path.join(tmpDir, relPath);
49
+ mkdirSync(path.dirname(fullPath), { recursive: true });
50
+ writeFileSync(fullPath, source);
51
+ const pathname = "/" + relPath
52
+ .replace(/\/route\.(ts|tsx|js|jsx)$/, "")
53
+ .replace(/^app\//, "");
54
+ return {
55
+ kind: "route-handler",
56
+ file: relPath,
57
+ isApi: pathname.startsWith("/api/") || pathname === "/api",
58
+ isPublic: true,
59
+ pathname,
60
+ signals: NO_SIGNALS,
61
+ protection: protectionSummary({}),
62
+ ...overrides,
63
+ };
64
+ }
65
+ function makeIndex(routes) {
66
+ return {
67
+ version: 1,
68
+ framework: "next-app-router",
69
+ rootDir: tmpDir,
70
+ deps: {
71
+ hasNextAuth: false, hasClerk: false, hasSupabase: false,
72
+ hasKinde: false, hasWorkOS: false, hasBetterAuth: false,
73
+ hasLucia: false, hasAuth0: false, hasIronSession: false,
74
+ hasFirebaseAuth: false, hasUpstashRatelimit: false, hasArcjet: false,
75
+ hasUnkey: false, hasPrisma: false, hasDrizzle: false, hasTrpc: false,
76
+ },
77
+ hints: {
78
+ auth: { functions: ["auth"], middlewareFiles: [], allowlistPaths: [] },
79
+ rateLimit: { wrappers: ["rateLimit"], allowlistPaths: [] },
80
+ tenancy: { orgFieldNames: [] },
81
+ },
82
+ middleware: { authLikely: false, rateLimitLikely: false, matcherPatterns: [] },
83
+ wrappers: { wrappers: new Map() },
84
+ routes: { all: routes, mutationRoutes: routes.filter(r => r.signals.hasMutationEvidence) },
85
+ serverActions: { all: [], mutationActions: [] },
86
+ trpc: { detected: false, procedures: [], mutationProcedures: [] },
87
+ };
88
+ }
89
+ function makeConfig(overrides = {}) {
90
+ return {
91
+ framework: "next-app-router",
92
+ include: ["app/**"],
93
+ exclude: [],
94
+ ci: { failOn: "critical", minConfidence: "high", minScore: 70, maxNewCritical: 0 },
95
+ scoring: { start: 100, penalties: { critical: 25, high: 10, med: 3, low: 1 } },
96
+ hints: {
97
+ auth: { functions: ["auth"], middlewareFiles: [], allowlistPaths: [] },
98
+ rateLimit: { wrappers: ["rateLimit"], allowlistPaths: [] },
99
+ tenancy: { orgFieldNames: [] },
100
+ },
101
+ rules: { "RATE-LIMIT-MISSING": { severity: "critical" } },
102
+ waiversFile: "shipguard.waivers.json",
103
+ ...overrides,
104
+ };
105
+ }
106
+ const BASIC_HANDLER = `export async function GET(request: Request) { return Response.json({ ok: true }); }`;
107
+ const MUTATION_HANDLER = `export async function POST(request: Request) {
108
+ const body = await request.json();
109
+ await prisma.user.create({ data: body });
110
+ return Response.json({ ok: true });
111
+ }`;
112
+ const BODY_HANDLER = `export async function POST(request: Request) {
113
+ const body = await request.json();
114
+ return Response.json({ received: true });
115
+ }`;
116
+ /* ------------------------------------------------------------------ */
117
+ /* Framework-managed exemptions */
118
+ /* ------------------------------------------------------------------ */
119
+ describe("framework-managed route exemptions", () => {
120
+ const config = makeConfig();
121
+ it("exempts NextAuth catch-all route", () => {
122
+ const route = createRoute("app/api/auth/[...nextauth]/route.ts", BASIC_HANDLER);
123
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
124
+ });
125
+ it("exempts NextAuth with different param name", () => {
126
+ const route = createRoute("app/api/auth/[...params]/route.ts", BASIC_HANDLER);
127
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
128
+ });
129
+ it("exempts OAuth token endpoint", () => {
130
+ const route = createRoute("app/api/oauth/token/route.ts", BASIC_HANDLER);
131
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
132
+ });
133
+ it("exempts SAML callback route", () => {
134
+ const route = createRoute("app/api/auth/saml/callback/route.ts", BASIC_HANDLER);
135
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
136
+ });
137
+ it("exempts callback routes from external services", () => {
138
+ const route = createRoute("app/api/callback/stripe/route.ts", BASIC_HANDLER);
139
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
140
+ });
141
+ it("exempts nested callback routes", () => {
142
+ const route = createRoute("app/api/slack/callback/route.ts", BASIC_HANDLER, {
143
+ pathname: "/api/slack/callback",
144
+ });
145
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
146
+ });
147
+ it("exempts OG image routes", () => {
148
+ const route = createRoute("app/api/og/analytics/route.tsx", BASIC_HANDLER);
149
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
150
+ });
151
+ it("exempts terminal OG path", () => {
152
+ const route = createRoute("app/api/og/route.tsx", BASIC_HANDLER, {
153
+ pathname: "/api/og",
154
+ });
155
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
156
+ });
157
+ it("does NOT exempt regular API routes", () => {
158
+ const route = createRoute("app/api/users/route.ts", BASIC_HANDLER);
159
+ const findings = run(makeIndex([route]), config);
160
+ expect(findings).toHaveLength(1);
161
+ });
162
+ });
163
+ /* ------------------------------------------------------------------ */
164
+ /* Improved webhook detection */
165
+ /* ------------------------------------------------------------------ */
166
+ describe("webhook path detection", () => {
167
+ const config = makeConfig();
168
+ it("exempts /webhook path", () => {
169
+ const route = createRoute("app/api/webhook/route.ts", BASIC_HANDLER, {
170
+ pathname: "/api/webhook",
171
+ });
172
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
173
+ });
174
+ it("exempts compound webhook path like /stripe-webhook", () => {
175
+ const route = createRoute("app/api/billing/stripe-webhook/route.ts", BASIC_HANDLER, {
176
+ pathname: "/api/billing/stripe-webhook",
177
+ });
178
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
179
+ });
180
+ it("exempts /webhooks/stripe nested path", () => {
181
+ const route = createRoute("app/api/webhooks/stripe/route.ts", BASIC_HANDLER, {
182
+ pathname: "/api/webhooks/stripe",
183
+ });
184
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
185
+ });
186
+ });
187
+ /* ------------------------------------------------------------------ */
188
+ /* Existing exemptions still work */
189
+ /* ------------------------------------------------------------------ */
190
+ describe("existing exemptions", () => {
191
+ const config = makeConfig();
192
+ it("exempts health check routes", () => {
193
+ const route = createRoute("app/api/health/route.ts", BASIC_HANDLER, {
194
+ pathname: "/api/health",
195
+ });
196
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
197
+ });
198
+ it("exempts cron routes", () => {
199
+ const route = createRoute("app/api/cron/daily/route.ts", BASIC_HANDLER, {
200
+ pathname: "/api/cron/daily",
201
+ });
202
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
203
+ });
204
+ it("skips non-API routes", () => {
205
+ const route = createRoute("app/dashboard/route.ts", BASIC_HANDLER, {
206
+ pathname: "/dashboard",
207
+ isApi: false,
208
+ });
209
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
210
+ });
211
+ it("skips routes with rate-limit protection satisfied", () => {
212
+ const route = createRoute("app/api/users/route.ts", BASIC_HANDLER, {
213
+ protection: protectionSummary({ rlSatisfied: true }),
214
+ });
215
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
216
+ });
217
+ it("defers to WRAPPER-UNRECOGNIZED for unverified wrappers", () => {
218
+ const route = createRoute("app/api/users/route.ts", BASIC_HANDLER, {
219
+ protection: protectionSummary({ unverifiedWrappers: ["withCustom"] }),
220
+ });
221
+ expect(run(makeIndex([route]), config)).toHaveLength(0);
222
+ });
223
+ });
224
+ /* ------------------------------------------------------------------ */
225
+ /* Auth-aware severity: public routes (unchanged behavior) */
226
+ /* ------------------------------------------------------------------ */
227
+ describe("severity: public routes (no auth)", () => {
228
+ const config = makeConfig();
229
+ it("public mutation route → critical/high", () => {
230
+ const route = createRoute("app/api/users/route.ts", MUTATION_HANDLER, {
231
+ signals: MUTATION_SIGNALS,
232
+ protection: protectionSummary({ authSatisfied: false }),
233
+ });
234
+ const findings = run(makeIndex([route]), config);
235
+ expect(findings).toHaveLength(1);
236
+ expect(findings[0].severity).toBe("critical");
237
+ expect(findings[0].confidence).toBe("high");
238
+ });
239
+ it("public body-parsing route → high/high", () => {
240
+ const route = createRoute("app/api/upload/route.ts", BODY_HANDLER, {
241
+ protection: protectionSummary({ authSatisfied: false }),
242
+ });
243
+ const findings = run(makeIndex([route]), config);
244
+ expect(findings).toHaveLength(1);
245
+ expect(findings[0].severity).toBe("high");
246
+ expect(findings[0].confidence).toBe("high");
247
+ });
248
+ it("public GET-only route → med/med", () => {
249
+ const route = createRoute("app/api/data/route.ts", BASIC_HANDLER, {
250
+ protection: protectionSummary({ authSatisfied: false }),
251
+ });
252
+ const findings = run(makeIndex([route]), config);
253
+ expect(findings).toHaveLength(1);
254
+ expect(findings[0].severity).toBe("med");
255
+ expect(findings[0].confidence).toBe("med");
256
+ });
257
+ });
258
+ /* ------------------------------------------------------------------ */
259
+ /* Auth-aware severity: authed routes (new behavior) */
260
+ /* ------------------------------------------------------------------ */
261
+ describe("severity: authenticated routes", () => {
262
+ const config = makeConfig();
263
+ it("authed mutation route → med/med (downgraded from critical)", () => {
264
+ const route = createRoute("app/api/users/route.ts", MUTATION_HANDLER, {
265
+ signals: MUTATION_SIGNALS,
266
+ protection: protectionSummary({ authSatisfied: true }),
267
+ });
268
+ const findings = run(makeIndex([route]), config);
269
+ expect(findings).toHaveLength(1);
270
+ expect(findings[0].severity).toBe("med");
271
+ expect(findings[0].confidence).toBe("med");
272
+ expect(findings[0].evidence).toContain("route has auth boundary — rate limiting is secondary defense");
273
+ });
274
+ it("authed body-parsing route → low/low (downgraded from high)", () => {
275
+ const route = createRoute("app/api/upload/route.ts", BODY_HANDLER, {
276
+ protection: protectionSummary({ authSatisfied: true }),
277
+ });
278
+ const findings = run(makeIndex([route]), config);
279
+ expect(findings).toHaveLength(1);
280
+ expect(findings[0].severity).toBe("low");
281
+ expect(findings[0].confidence).toBe("low");
282
+ });
283
+ it("authed GET-only route → low/low (downgraded from med)", () => {
284
+ const route = createRoute("app/api/data/route.ts", BASIC_HANDLER, {
285
+ protection: protectionSummary({ authSatisfied: true }),
286
+ });
287
+ const findings = run(makeIndex([route]), config);
288
+ expect(findings).toHaveLength(1);
289
+ expect(findings[0].severity).toBe("low");
290
+ expect(findings[0].confidence).toBe("low");
291
+ expect(findings[0].evidence).toContain("route has auth boundary — rate limiting is secondary defense");
292
+ });
293
+ it("authed route gets different message and remediation than public", () => {
294
+ const authedRoute = createRoute("app/api/data/route.ts", BASIC_HANDLER, {
295
+ protection: protectionSummary({ authSatisfied: true }),
296
+ });
297
+ const publicRoute = createRoute("app/api/other/route.ts", BASIC_HANDLER, {
298
+ protection: protectionSummary({ authSatisfied: false }),
299
+ });
300
+ const authedFindings = run(makeIndex([authedRoute]), config);
301
+ const publicFindings = run(makeIndex([publicRoute]), config);
302
+ expect(authedFindings[0].message).toContain("Authenticated");
303
+ expect(publicFindings[0].message).toContain("Public");
304
+ expect(authedFindings[0].remediation).not.toEqual(publicFindings[0].remediation);
305
+ });
306
+ });
307
+ /* ------------------------------------------------------------------ */
308
+ /* Severity cap */
309
+ /* ------------------------------------------------------------------ */
310
+ describe("severity cap", () => {
311
+ it("caps severity at rule max from config", () => {
312
+ const config = makeConfig({
313
+ rules: { "RATE-LIMIT-MISSING": { severity: "high" } },
314
+ });
315
+ const route = createRoute("app/api/users/route.ts", MUTATION_HANDLER, {
316
+ signals: MUTATION_SIGNALS,
317
+ protection: protectionSummary({ authSatisfied: false }),
318
+ });
319
+ const findings = run(makeIndex([route]), config);
320
+ expect(findings).toHaveLength(1);
321
+ // Would be critical but capped to high
322
+ expect(findings[0].severity).toBe("high");
323
+ });
324
+ });
325
+ //# sourceMappingURL=rate-limit-missing.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit-missing.test.js","sourceRoot":"","sources":["../../src/rules/rate-limit-missing.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { NextIndex } from "../next/types.js";
2
+ import type { Finding, ShipguardConfig } from "../engine/types.js";
3
+ export declare const RULE_ID = "WRAPPER-UNRECOGNIZED";
4
+ export declare function run(index: NextIndex, config: ShipguardConfig): Finding[];
5
+ //# sourceMappingURL=wrapper-unrecognized.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapper-unrecognized.d.ts","sourceRoot":"","sources":["../../src/rules/wrapper-unrecognized.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGnE,eAAO,MAAM,OAAO,yBAAyB,CAAC;AAU9C,wBAAgB,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,EAAE,CAiFxE"}
@@ -0,0 +1,76 @@
1
+ export const RULE_ID = "WRAPPER-UNRECOGNIZED";
2
+ const SEVERITY_RANK = { critical: 4, high: 3, med: 2, low: 1 };
3
+ function capSeverity(computed, max) {
4
+ const maxRank = SEVERITY_RANK[max] ?? 4;
5
+ const computedRank = SEVERITY_RANK[computed] ?? 2;
6
+ return computedRank > maxRank ? max : computed;
7
+ }
8
+ export function run(index, config) {
9
+ const findings = [];
10
+ const maxSeverity = config.rules[RULE_ID]?.severity ?? "high";
11
+ for (const [name, wrapper] of index.wrappers.wrappers) {
12
+ // Skip fully resolved wrappers where both auth AND rate-limit are enforced
13
+ if (wrapper.resolved && wrapper.evidence.authEnforced && wrapper.evidence.rateLimitEnforced) {
14
+ continue;
15
+ }
16
+ // Determine what this wrapper WOULD have triggered
17
+ const wouldTrigger = [];
18
+ // Check if any wrapped routes are mutation routes (need auth)
19
+ const mutationFileSet = new Set(index.routes.mutationRoutes.map((r) => r.file));
20
+ const wrappedMutationFiles = wrapper.usageFiles.filter((f) => mutationFileSet.has(f));
21
+ if (wrappedMutationFiles.length > 0) {
22
+ if (!wrapper.resolved || !wrapper.evidence.authEnforced) {
23
+ wouldTrigger.push("AUTH-BOUNDARY-MISSING");
24
+ }
25
+ }
26
+ // Check if any wrapped routes are API routes (need rate limiting)
27
+ const apiFileSet = new Set(index.routes.all.filter((r) => r.isApi).map((r) => r.file));
28
+ const wrappedApiFiles = wrapper.usageFiles.filter((f) => apiFileSet.has(f));
29
+ if (wrappedApiFiles.length > 0) {
30
+ if (!wrapper.resolved || !wrapper.evidence.rateLimitEnforced) {
31
+ wouldTrigger.push("RATE-LIMIT-MISSING");
32
+ }
33
+ }
34
+ if (wouldTrigger.length === 0)
35
+ continue;
36
+ // Severity = high if wrapping mutation routes, med otherwise
37
+ const computedSeverity = wrappedMutationFiles.length > 0 ? "high" : "med";
38
+ const status = !wrapper.resolved
39
+ ? "could not be resolved"
40
+ : wrapper.evidence.authCallPresent && !wrapper.evidence.authEnforced
41
+ ? "calls auth but enforcement not proven"
42
+ : wrapper.evidence.rateLimitCallPresent && !wrapper.evidence.rateLimitEnforced
43
+ ? "calls rate limiter but enforcement not proven"
44
+ : "missing protections";
45
+ const evidence = [
46
+ `${name}() wraps ${wrapper.usageCount} route handler(s) (${wrapper.mutationRouteCount} mutation)`,
47
+ `Would have triggered: ${wouldTrigger.join(", ")}`,
48
+ `Top routes: ${wrapper.usageFiles.slice(0, 5).join(", ")}${wrapper.usageCount > 5 ? ` (+${wrapper.usageCount - 5} more)` : ""}`,
49
+ ];
50
+ if (wrapper.evidence.authCallPresent) {
51
+ evidence.push(`Auth call detected: ${wrapper.evidence.authDetails.join(", ")}`);
52
+ }
53
+ if (wrapper.evidence.rateLimitCallPresent) {
54
+ evidence.push(`Rate-limit call detected: ${wrapper.evidence.rateLimitDetails.join(", ")}`);
55
+ }
56
+ findings.push({
57
+ ruleId: RULE_ID,
58
+ severity: capSeverity(computedSeverity, maxSeverity),
59
+ confidence: "high",
60
+ message: `Wrapper "${name}" wraps ${wrapper.usageCount} handler(s); ${status}`,
61
+ file: wrapper.usageFiles[0],
62
+ evidence,
63
+ confidenceRationale: "High: wrapper usage is certain, but protection cannot be verified",
64
+ remediation: [
65
+ `If ${name} enforces auth: add "${name}" to hints.auth.functions`,
66
+ `If ${name} enforces rate limiting: add "${name}" to hints.rateLimit.wrappers`,
67
+ ...(wrapper.definitionFile
68
+ ? [`Verify wrapper implementation at ${wrapper.definitionFile}`]
69
+ : [`Wrapper definition could not be found — check import paths`]),
70
+ ],
71
+ tags: ["wrapper", "config"],
72
+ });
73
+ }
74
+ return findings;
75
+ }
76
+ //# sourceMappingURL=wrapper-unrecognized.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrapper-unrecognized.js","sourceRoot":"","sources":["../../src/rules/wrapper-unrecognized.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shared Higher-Order Function (HOF) detection utilities.
3
+ * Used by wrapper analysis and rules.
4
+ */
5
+ /**
6
+ * Extract the ordered chain of HOF wrapper names from route source.
7
+ * E.g., `export const POST = withWorkspace(withErrorBoundary(handler))` → ["withWorkspace", "withErrorBoundary"]
8
+ * E.g., `export default withAuth(handler)` → ["withAuth"]
9
+ * Returns empty array if no HOF wrapper detected.
10
+ */
11
+ export declare function extractHofWrapperChain(src: string): string[];
12
+ /**
13
+ * Check if route source is exported via a specific known function (HOF pattern).
14
+ * E.g., `export const POST = withAuth(handler)` with functionName="withAuth" → true
15
+ */
16
+ export declare function isWrappedByFunction(src: string, functionName: string): boolean;
17
+ /**
18
+ * Find the import source for a given identifier in the source.
19
+ * Returns the module specifier or undefined if not imported (same-file definition).
20
+ */
21
+ export declare function findImportSource(src: string, identifierName: string): string | undefined;
22
+ //# sourceMappingURL=hof.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hof.d.ts","sourceRoot":"","sources":["../../src/util/hof.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAsB5D;AA2CD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAY9E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAoBxF"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Shared Higher-Order Function (HOF) detection utilities.
3
+ * Used by wrapper analysis and rules.
4
+ */
5
+ const HTTP_METHODS = "GET|POST|PUT|PATCH|DELETE";
6
+ /**
7
+ * Extract the ordered chain of HOF wrapper names from route source.
8
+ * E.g., `export const POST = withWorkspace(withErrorBoundary(handler))` → ["withWorkspace", "withErrorBoundary"]
9
+ * E.g., `export default withAuth(handler)` → ["withAuth"]
10
+ * Returns empty array if no HOF wrapper detected.
11
+ */
12
+ export function extractHofWrapperChain(src) {
13
+ const chains = [];
14
+ // Pattern 1: export const METHOD = wrapper(...)
15
+ const constPattern = new RegExp(`export\\s+(?:const|let|var)\\s+(?:${HTTP_METHODS})\\s*=\\s*(.+)`, "gm");
16
+ for (const m of src.matchAll(constPattern)) {
17
+ chains.push(...extractCallChain(m[1]));
18
+ }
19
+ // Pattern 2: export default wrapper(...)
20
+ const defaultPattern = /export\s+default\s+([a-zA-Z_]\w*)\s*\(/gm;
21
+ for (const m of src.matchAll(defaultPattern)) {
22
+ // Only add if not already captured
23
+ if (!chains.includes(m[1])) {
24
+ chains.push(m[1]);
25
+ }
26
+ }
27
+ return [...new Set(chains)];
28
+ }
29
+ /**
30
+ * Extract function names from a call chain expression.
31
+ * E.g., "withWorkspace(withErrorBoundary(handler))" → ["withWorkspace", "withErrorBoundary"]
32
+ * E.g., "withAuth(async (req) => { ... })" → ["withAuth"]
33
+ *
34
+ * Only extracts the leading nested call chain — stops at the first
35
+ * non-wrapper token (e.g., `async`, handler body) to avoid picking
36
+ * up identifiers deep inside the handler.
37
+ */
38
+ function extractCallChain(expr) {
39
+ const names = [];
40
+ let pos = 0;
41
+ while (pos < expr.length) {
42
+ // Skip whitespace
43
+ while (pos < expr.length && /\s/.test(expr[pos]))
44
+ pos++;
45
+ // Try to match identifier(
46
+ const remaining = expr.slice(pos);
47
+ const match = remaining.match(/^([a-zA-Z_]\w*)\s*\(/);
48
+ if (!match)
49
+ break;
50
+ const name = match[1];
51
+ if (SKIP_IDENTIFIERS.has(name))
52
+ break; // Not a wrapper, stop
53
+ names.push(name);
54
+ pos += match[0].length;
55
+ }
56
+ return names;
57
+ }
58
+ const SKIP_IDENTIFIERS = new Set([
59
+ "async", "await", "function", "return", "new", "typeof", "void",
60
+ "if", "else", "for", "while", "switch", "case", "try", "catch",
61
+ "throw", "const", "let", "var", "class", "import", "export",
62
+ "console", "Error", "Promise", "Array", "Object", "String", "Number",
63
+ "Boolean", "JSON", "Math", "Date", "RegExp", "Map", "Set",
64
+ "Response", "Request", "Headers", "NextResponse", "NextRequest",
65
+ ]);
66
+ /**
67
+ * Check if route source is exported via a specific known function (HOF pattern).
68
+ * E.g., `export const POST = withAuth(handler)` with functionName="withAuth" → true
69
+ */
70
+ export function isWrappedByFunction(src, functionName) {
71
+ const escaped = functionName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
72
+ // export const METHOD = fn(...)
73
+ const hofPattern = new RegExp(`export\\s+(?:const|let|var)\\s+(?:${HTTP_METHODS})\\s*=\\s*${escaped}\\s*\\(`, "m");
74
+ if (hofPattern.test(src))
75
+ return true;
76
+ // export default fn(...)
77
+ const defaultPattern = new RegExp(`export\\s+default\\s+${escaped}\\s*\\(`, "m");
78
+ return defaultPattern.test(src);
79
+ }
80
+ /**
81
+ * Find the import source for a given identifier in the source.
82
+ * Returns the module specifier or undefined if not imported (same-file definition).
83
+ */
84
+ export function findImportSource(src, identifierName) {
85
+ const escaped = identifierName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
86
+ // Named import: import { name } from "source" or import { other as name } from "source"
87
+ const namedPattern = new RegExp(`import\\s*\\{[^}]*\\b(?:${escaped}|\\w+\\s+as\\s+${escaped})\\b[^}]*\\}\\s*from\\s*["']([^"']+)["']`);
88
+ const namedMatch = src.match(namedPattern);
89
+ if (namedMatch)
90
+ return namedMatch[1];
91
+ // Default import: import name from "source"
92
+ const defaultPattern = new RegExp(`import\\s+${escaped}\\s+from\\s*["']([^"']+)["']`);
93
+ const defaultMatch = src.match(defaultPattern);
94
+ if (defaultMatch)
95
+ return defaultMatch[1];
96
+ // Namespace import + property access won't match here — that's fine for v1
97
+ return undefined;
98
+ }
99
+ //# sourceMappingURL=hof.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hof.js","sourceRoot":"","sources":["../../src/util/hof.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hof.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hof.test.d.ts","sourceRoot":"","sources":["../../src/util/hof.test.ts"],"names":[],"mappings":""}