@fourteensystems/shipguard 0.1.0 → 0.2.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 +42 -110
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +36 -0
- package/dist/cli/commands/init.js.map +1 -1
- 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.map +1 -1
- package/dist/engine/config.js +2 -0
- package/dist/engine/config.js.map +1 -1
- 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/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.map +1 -1
- package/dist/engine/score.js +13 -2
- package/dist/engine/score.js.map +1 -1
- 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 +191 -0
- package/dist/engine/score.test.js.map +1 -0
- package/dist/engine/types.d.ts +2 -0
- package/dist/engine/types.d.ts.map +1 -1
- 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/next/deps.d.ts.map +1 -1
- package/dist/next/deps.js +16 -0
- package/dist/next/deps.js.map +1 -1
- 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.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.map +1 -1
- package/dist/next/index.js +11 -0
- package/dist/next/index.js.map +1 -1
- package/dist/next/middleware.d.ts.map +1 -1
- package/dist/next/middleware.js +15 -0
- package/dist/next/middleware.js.map +1 -1
- 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.test.d.ts +2 -0
- package/dist/next/routes.test.d.ts.map +1 -0
- package/dist/next/routes.test.js +110 -0
- package/dist/next/routes.test.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.map +1 -1
- package/dist/next/trpc.js +4 -31
- package/dist/next/trpc.js.map +1 -1
- package/dist/next/types.d.ts +34 -0
- package/dist/next/types.d.ts.map +1 -1
- package/dist/next/wrappers.d.ts +10 -0
- package/dist/next/wrappers.d.ts.map +1 -0
- package/dist/next/wrappers.js +477 -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.map +1 -1
- package/dist/rules/auth-boundary-missing.js +23 -80
- package/dist/rules/auth-boundary-missing.js.map +1 -1
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +12 -0
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/rate-limit-missing.d.ts.map +1 -1
- package/dist/rules/rate-limit-missing.js +34 -50
- package/dist/rules/rate-limit-missing.js.map +1 -1
- 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 +76 -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/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 +1 -1
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { buildWrapperIndex, analyzeWrapperBody, computeProtection } from "./wrappers.js";
|
|
6
|
+
function makeRoute(overrides = {}) {
|
|
7
|
+
return {
|
|
8
|
+
kind: "route-handler",
|
|
9
|
+
file: "app/api/test/route.ts",
|
|
10
|
+
isApi: true,
|
|
11
|
+
isPublic: true,
|
|
12
|
+
signals: {
|
|
13
|
+
hasMutationEvidence: true,
|
|
14
|
+
hasDbWriteEvidence: true,
|
|
15
|
+
hasStripeWriteEvidence: false,
|
|
16
|
+
mutationDetails: ["prisma.create"],
|
|
17
|
+
},
|
|
18
|
+
...overrides,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const DEFAULT_HINTS = {
|
|
22
|
+
auth: { functions: ["auth", "getSession", "getServerSession"], middlewareFiles: [], allowlistPaths: [] },
|
|
23
|
+
rateLimit: { wrappers: ["rateLimit", "withRateLimit"], allowlistPaths: [] },
|
|
24
|
+
tenancy: { orgFieldNames: [] },
|
|
25
|
+
};
|
|
26
|
+
const DEFAULT_MIDDLEWARE = {
|
|
27
|
+
authLikely: false,
|
|
28
|
+
rateLimitLikely: false,
|
|
29
|
+
matcherPatterns: [],
|
|
30
|
+
};
|
|
31
|
+
describe("buildWrapperIndex", () => {
|
|
32
|
+
let tmpDir;
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
tmpDir = mkdtempSync(path.join(os.tmpdir(), "shipguard-wrapper-"));
|
|
35
|
+
});
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
38
|
+
});
|
|
39
|
+
it("discovers wrapper from route file", () => {
|
|
40
|
+
mkdirSync(path.join(tmpDir, "app", "api", "test"), { recursive: true });
|
|
41
|
+
mkdirSync(path.join(tmpDir, "src", "lib"), { recursive: true });
|
|
42
|
+
writeFileSync(path.join(tmpDir, "app", "api", "test", "route.ts"), `import { withWorkspace } from "@/lib/auth";
|
|
43
|
+
export const POST = withWorkspace(async (req) => {
|
|
44
|
+
await prisma.user.create({ data: { name: "test" } });
|
|
45
|
+
return Response.json({});
|
|
46
|
+
});`);
|
|
47
|
+
writeFileSync(path.join(tmpDir, "src", "lib", "auth.ts"), `export function withWorkspace(handler: any) {
|
|
48
|
+
return async (req: any) => {
|
|
49
|
+
const session = await getSession();
|
|
50
|
+
if (!session) throw new Error("Unauthorized");
|
|
51
|
+
return handler(req, { session });
|
|
52
|
+
};
|
|
53
|
+
}`);
|
|
54
|
+
const routes = [makeRoute({ file: "app/api/test/route.ts" })];
|
|
55
|
+
const result = buildWrapperIndex(routes, tmpDir, { rootDir: tmpDir }, ["auth", "getSession"], ["rateLimit"]);
|
|
56
|
+
expect(result.wrappers.size).toBe(1);
|
|
57
|
+
const wrapper = result.wrappers.get("withWorkspace");
|
|
58
|
+
expect(wrapper).toBeDefined();
|
|
59
|
+
expect(wrapper.resolved).toBe(true);
|
|
60
|
+
expect(wrapper.evidence.authCallPresent).toBe(true);
|
|
61
|
+
expect(wrapper.evidence.authEnforced).toBe(true);
|
|
62
|
+
expect(wrapper.usageCount).toBe(1);
|
|
63
|
+
expect(wrapper.mutationRouteCount).toBe(1);
|
|
64
|
+
});
|
|
65
|
+
it("detects unresolvable npm package wrapper", () => {
|
|
66
|
+
mkdirSync(path.join(tmpDir, "app", "api", "test"), { recursive: true });
|
|
67
|
+
writeFileSync(path.join(tmpDir, "app", "api", "test", "route.ts"), `import { withAuth } from "some-npm-package";
|
|
68
|
+
export const POST = withAuth(async (req) => {
|
|
69
|
+
return Response.json({});
|
|
70
|
+
});`);
|
|
71
|
+
const routes = [makeRoute({ file: "app/api/test/route.ts" })];
|
|
72
|
+
const result = buildWrapperIndex(routes, tmpDir, { rootDir: tmpDir }, ["auth"], ["rateLimit"]);
|
|
73
|
+
const wrapper = result.wrappers.get("withAuth");
|
|
74
|
+
expect(wrapper).toBeDefined();
|
|
75
|
+
expect(wrapper.resolved).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
it("aggregates usage across multiple routes", () => {
|
|
78
|
+
mkdirSync(path.join(tmpDir, "app", "api", "a"), { recursive: true });
|
|
79
|
+
mkdirSync(path.join(tmpDir, "app", "api", "b"), { recursive: true });
|
|
80
|
+
mkdirSync(path.join(tmpDir, "src", "lib"), { recursive: true });
|
|
81
|
+
const routeContent = `import { withWorkspace } from "@/lib/auth";
|
|
82
|
+
export const POST = withWorkspace(async (req) => {
|
|
83
|
+
await prisma.user.create({ data: {} });
|
|
84
|
+
return Response.json({});
|
|
85
|
+
});`;
|
|
86
|
+
writeFileSync(path.join(tmpDir, "app", "api", "a", "route.ts"), routeContent);
|
|
87
|
+
writeFileSync(path.join(tmpDir, "app", "api", "b", "route.ts"), routeContent);
|
|
88
|
+
writeFileSync(path.join(tmpDir, "src", "lib", "auth.ts"), `export function withWorkspace(handler: any) { return handler; }`);
|
|
89
|
+
const routes = [
|
|
90
|
+
makeRoute({ file: "app/api/a/route.ts" }),
|
|
91
|
+
makeRoute({ file: "app/api/b/route.ts" }),
|
|
92
|
+
];
|
|
93
|
+
const result = buildWrapperIndex(routes, tmpDir, { rootDir: tmpDir }, ["auth"], ["rateLimit"]);
|
|
94
|
+
const wrapper = result.wrappers.get("withWorkspace");
|
|
95
|
+
expect(wrapper.usageCount).toBe(2);
|
|
96
|
+
expect(wrapper.mutationRouteCount).toBe(2);
|
|
97
|
+
});
|
|
98
|
+
it("handles same-file wrapper definition", () => {
|
|
99
|
+
mkdirSync(path.join(tmpDir, "app", "api", "test"), { recursive: true });
|
|
100
|
+
writeFileSync(path.join(tmpDir, "app", "api", "test", "route.ts"), `function withAuth(handler: any) {
|
|
101
|
+
return async (req: any) => {
|
|
102
|
+
const session = await auth();
|
|
103
|
+
if (!session) throw new Error("Unauthorized");
|
|
104
|
+
return handler(req);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export const POST = withAuth(async (req) => {
|
|
108
|
+
return Response.json({});
|
|
109
|
+
});`);
|
|
110
|
+
const routes = [makeRoute({ file: "app/api/test/route.ts" })];
|
|
111
|
+
const result = buildWrapperIndex(routes, tmpDir, { rootDir: tmpDir }, ["auth"], ["rateLimit"]);
|
|
112
|
+
const wrapper = result.wrappers.get("withAuth");
|
|
113
|
+
expect(wrapper).toBeDefined();
|
|
114
|
+
expect(wrapper.resolved).toBe(true);
|
|
115
|
+
expect(wrapper.evidence.authCallPresent).toBe(true);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe("analyzeWrapperBody", () => {
|
|
119
|
+
it("detects auth call + enforcement", () => {
|
|
120
|
+
const src = `
|
|
121
|
+
export function withAuth(handler: any) {
|
|
122
|
+
return async (req: any) => {
|
|
123
|
+
const session = await getSession();
|
|
124
|
+
if (!session) {
|
|
125
|
+
throw new Error("Unauthorized");
|
|
126
|
+
}
|
|
127
|
+
return handler(req, { session });
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
`;
|
|
131
|
+
const evidence = analyzeWrapperBody("withAuth", src, ["getSession"], []);
|
|
132
|
+
expect(evidence.authCallPresent).toBe(true);
|
|
133
|
+
expect(evidence.authEnforced).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
it("detects auth call without enforcement", () => {
|
|
136
|
+
const src = `
|
|
137
|
+
export function withLogging(handler: any) {
|
|
138
|
+
return async (req: any) => {
|
|
139
|
+
const session = await getSession();
|
|
140
|
+
console.log("User:", session?.user?.name);
|
|
141
|
+
return handler(req);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
`;
|
|
145
|
+
const evidence = analyzeWrapperBody("withLogging", src, ["getSession"], []);
|
|
146
|
+
expect(evidence.authCallPresent).toBe(true);
|
|
147
|
+
expect(evidence.authEnforced).toBe(false);
|
|
148
|
+
});
|
|
149
|
+
it("detects rate-limit call + enforcement", () => {
|
|
150
|
+
const src = `
|
|
151
|
+
export function withRateLimiting(handler: any) {
|
|
152
|
+
return async (req: any) => {
|
|
153
|
+
const { success } = await rateLimit(req);
|
|
154
|
+
if (!success) {
|
|
155
|
+
return new Response("Too many requests", { status: 429 });
|
|
156
|
+
}
|
|
157
|
+
return handler(req);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
`;
|
|
161
|
+
const evidence = analyzeWrapperBody("withRateLimiting", src, [], ["rateLimit"]);
|
|
162
|
+
expect(evidence.rateLimitCallPresent).toBe(true);
|
|
163
|
+
expect(evidence.rateLimitEnforced).toBe(true);
|
|
164
|
+
});
|
|
165
|
+
it("detects both auth and rate-limit", () => {
|
|
166
|
+
const src = `
|
|
167
|
+
import { getSession } from "next-auth";
|
|
168
|
+
import { Ratelimit } from "@upstash/ratelimit";
|
|
169
|
+
|
|
170
|
+
export function withWorkspace(handler: any) {
|
|
171
|
+
return async (req: any) => {
|
|
172
|
+
const session = await getSession();
|
|
173
|
+
if (!session) return new Response("Unauthorized", { status: 401 });
|
|
174
|
+
|
|
175
|
+
const { success } = await rateLimit.limit(session.user.id);
|
|
176
|
+
if (!success) return new Response("Rate limited", { status: 429 });
|
|
177
|
+
|
|
178
|
+
return handler(req, { session });
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
`;
|
|
182
|
+
const evidence = analyzeWrapperBody("withWorkspace", src, ["getSession"], ["rateLimit"]);
|
|
183
|
+
expect(evidence.authCallPresent).toBe(true);
|
|
184
|
+
expect(evidence.authEnforced).toBe(true);
|
|
185
|
+
expect(evidence.rateLimitCallPresent).toBe(true);
|
|
186
|
+
expect(evidence.rateLimitEnforced).toBe(true);
|
|
187
|
+
});
|
|
188
|
+
it("detects Supabase auth pattern in wrapper", () => {
|
|
189
|
+
const src = `
|
|
190
|
+
export function withAuth(handler: any) {
|
|
191
|
+
return async (req: any) => {
|
|
192
|
+
const { data: { user } } = await supabase.auth.getUser();
|
|
193
|
+
if (!user) throw new Error("Unauthorized");
|
|
194
|
+
return handler(req, { user });
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
const evidence = analyzeWrapperBody("withAuth", src, [], []);
|
|
199
|
+
expect(evidence.authCallPresent).toBe(true);
|
|
200
|
+
expect(evidence.authEnforced).toBe(true);
|
|
201
|
+
});
|
|
202
|
+
it("detects upstash ratelimit import as RL evidence", () => {
|
|
203
|
+
const src = `
|
|
204
|
+
import { Ratelimit } from "@upstash/ratelimit";
|
|
205
|
+
const ratelimit = new Ratelimit({ ... });
|
|
206
|
+
|
|
207
|
+
export function withRL(handler: any) {
|
|
208
|
+
return async (req: any) => {
|
|
209
|
+
const { success } = await ratelimit.limit("key");
|
|
210
|
+
if (!success) throw new Error("Rate limited");
|
|
211
|
+
return handler(req);
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
`;
|
|
215
|
+
const evidence = analyzeWrapperBody("withRL", src, [], []);
|
|
216
|
+
expect(evidence.rateLimitCallPresent).toBe(true);
|
|
217
|
+
expect(evidence.rateLimitEnforced).toBe(true);
|
|
218
|
+
});
|
|
219
|
+
it("returns no evidence for generic wrapper", () => {
|
|
220
|
+
const src = `
|
|
221
|
+
export function withErrorBoundary(handler: any) {
|
|
222
|
+
return async (req: any) => {
|
|
223
|
+
try {
|
|
224
|
+
return handler(req);
|
|
225
|
+
} catch (e) {
|
|
226
|
+
return new Response("Error", { status: 500 });
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
const evidence = analyzeWrapperBody("withErrorBoundary", src, ["auth", "getSession"], ["rateLimit"]);
|
|
232
|
+
expect(evidence.authCallPresent).toBe(false);
|
|
233
|
+
expect(evidence.rateLimitCallPresent).toBe(false);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
describe("computeProtection", () => {
|
|
237
|
+
let tmpDir;
|
|
238
|
+
beforeEach(() => {
|
|
239
|
+
tmpDir = mkdtempSync(path.join(os.tmpdir(), "shipguard-prot-"));
|
|
240
|
+
});
|
|
241
|
+
afterEach(() => {
|
|
242
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
243
|
+
});
|
|
244
|
+
it("marks satisfied when wrapper has auth enforcement", () => {
|
|
245
|
+
mkdirSync(path.join(tmpDir, "app", "api", "test"), { recursive: true });
|
|
246
|
+
writeFileSync(path.join(tmpDir, "app", "api", "test", "route.ts"), `import { withWorkspace } from "@/lib/auth";
|
|
247
|
+
export const POST = withWorkspace(async (req) => {});`);
|
|
248
|
+
const wrapperIndex = {
|
|
249
|
+
wrappers: new Map([
|
|
250
|
+
["withWorkspace", {
|
|
251
|
+
name: "withWorkspace",
|
|
252
|
+
definitionFile: "src/lib/auth.ts",
|
|
253
|
+
resolved: true,
|
|
254
|
+
evidence: {
|
|
255
|
+
authCallPresent: true,
|
|
256
|
+
authEnforced: true,
|
|
257
|
+
rateLimitCallPresent: true,
|
|
258
|
+
rateLimitEnforced: true,
|
|
259
|
+
authDetails: ["calls getSession()"],
|
|
260
|
+
rateLimitDetails: ["calls rateLimit()"],
|
|
261
|
+
},
|
|
262
|
+
usageCount: 1,
|
|
263
|
+
usageFiles: ["app/api/test/route.ts"],
|
|
264
|
+
mutationRouteCount: 1,
|
|
265
|
+
}],
|
|
266
|
+
]),
|
|
267
|
+
};
|
|
268
|
+
const route = makeRoute({ file: "app/api/test/route.ts" });
|
|
269
|
+
const protection = computeProtection(route, wrapperIndex, DEFAULT_MIDDLEWARE, DEFAULT_HINTS, tmpDir);
|
|
270
|
+
expect(protection.auth.satisfied).toBe(true);
|
|
271
|
+
expect(protection.auth.enforced).toBe(true);
|
|
272
|
+
expect(protection.auth.sources).toContain("wrapper");
|
|
273
|
+
expect(protection.rateLimit.satisfied).toBe(true);
|
|
274
|
+
});
|
|
275
|
+
it("marks unverified when wrapper calls auth but doesn't enforce", () => {
|
|
276
|
+
mkdirSync(path.join(tmpDir, "app", "api", "test"), { recursive: true });
|
|
277
|
+
writeFileSync(path.join(tmpDir, "app", "api", "test", "route.ts"), `import { withLogging } from "@/lib/logging";
|
|
278
|
+
export const POST = withLogging(async (req) => {});`);
|
|
279
|
+
const wrapperIndex = {
|
|
280
|
+
wrappers: new Map([
|
|
281
|
+
["withLogging", {
|
|
282
|
+
name: "withLogging",
|
|
283
|
+
definitionFile: "src/lib/logging.ts",
|
|
284
|
+
resolved: true,
|
|
285
|
+
evidence: {
|
|
286
|
+
authCallPresent: true,
|
|
287
|
+
authEnforced: false,
|
|
288
|
+
rateLimitCallPresent: false,
|
|
289
|
+
rateLimitEnforced: false,
|
|
290
|
+
authDetails: ["calls getSession()"],
|
|
291
|
+
rateLimitDetails: [],
|
|
292
|
+
},
|
|
293
|
+
usageCount: 1,
|
|
294
|
+
usageFiles: ["app/api/test/route.ts"],
|
|
295
|
+
mutationRouteCount: 1,
|
|
296
|
+
}],
|
|
297
|
+
]),
|
|
298
|
+
};
|
|
299
|
+
const route = makeRoute({ file: "app/api/test/route.ts" });
|
|
300
|
+
const protection = computeProtection(route, wrapperIndex, DEFAULT_MIDDLEWARE, DEFAULT_HINTS, tmpDir);
|
|
301
|
+
expect(protection.auth.satisfied).toBe(false);
|
|
302
|
+
expect(protection.auth.unverifiedWrappers).toContain("withLogging");
|
|
303
|
+
});
|
|
304
|
+
it("marks satisfied when direct auth call in route", () => {
|
|
305
|
+
mkdirSync(path.join(tmpDir, "app", "api", "test"), { recursive: true });
|
|
306
|
+
writeFileSync(path.join(tmpDir, "app", "api", "test", "route.ts"), `export async function POST(req: Request) {
|
|
307
|
+
const session = await auth();
|
|
308
|
+
return Response.json({});
|
|
309
|
+
}`);
|
|
310
|
+
const emptyWrappers = { wrappers: new Map() };
|
|
311
|
+
const route = makeRoute({ file: "app/api/test/route.ts" });
|
|
312
|
+
const protection = computeProtection(route, emptyWrappers, DEFAULT_MIDDLEWARE, DEFAULT_HINTS, tmpDir);
|
|
313
|
+
expect(protection.auth.satisfied).toBe(true);
|
|
314
|
+
expect(protection.auth.sources).toContain("direct");
|
|
315
|
+
});
|
|
316
|
+
it("marks satisfied when middleware covers route", () => {
|
|
317
|
+
mkdirSync(path.join(tmpDir, "app", "api", "test"), { recursive: true });
|
|
318
|
+
writeFileSync(path.join(tmpDir, "app", "api", "test", "route.ts"), `export async function POST(req: Request) {
|
|
319
|
+
return Response.json({});
|
|
320
|
+
}`);
|
|
321
|
+
const middleware = {
|
|
322
|
+
authLikely: true,
|
|
323
|
+
rateLimitLikely: false,
|
|
324
|
+
matcherPatterns: ["/api/:path*"],
|
|
325
|
+
};
|
|
326
|
+
const emptyWrappers = { wrappers: new Map() };
|
|
327
|
+
const route = makeRoute({ file: "app/api/test/route.ts", pathname: "/api/test" });
|
|
328
|
+
const protection = computeProtection(route, emptyWrappers, middleware, DEFAULT_HINTS, tmpDir);
|
|
329
|
+
expect(protection.auth.satisfied).toBe(true);
|
|
330
|
+
expect(protection.auth.sources).toContain("middleware");
|
|
331
|
+
});
|
|
332
|
+
it("marks unverified when wrapper is unresolved", () => {
|
|
333
|
+
mkdirSync(path.join(tmpDir, "app", "api", "test"), { recursive: true });
|
|
334
|
+
writeFileSync(path.join(tmpDir, "app", "api", "test", "route.ts"), `import { withUnknown } from "some-package";
|
|
335
|
+
export const POST = withUnknown(async (req) => {});`);
|
|
336
|
+
const wrapperIndex = {
|
|
337
|
+
wrappers: new Map([
|
|
338
|
+
["withUnknown", {
|
|
339
|
+
name: "withUnknown",
|
|
340
|
+
resolved: false,
|
|
341
|
+
evidence: {
|
|
342
|
+
authCallPresent: false,
|
|
343
|
+
authEnforced: false,
|
|
344
|
+
rateLimitCallPresent: false,
|
|
345
|
+
rateLimitEnforced: false,
|
|
346
|
+
authDetails: [],
|
|
347
|
+
rateLimitDetails: [],
|
|
348
|
+
},
|
|
349
|
+
usageCount: 1,
|
|
350
|
+
usageFiles: ["app/api/test/route.ts"],
|
|
351
|
+
mutationRouteCount: 1,
|
|
352
|
+
}],
|
|
353
|
+
]),
|
|
354
|
+
};
|
|
355
|
+
const route = makeRoute({ file: "app/api/test/route.ts" });
|
|
356
|
+
const protection = computeProtection(route, wrapperIndex, DEFAULT_MIDDLEWARE, DEFAULT_HINTS, tmpDir);
|
|
357
|
+
expect(protection.auth.satisfied).toBe(false);
|
|
358
|
+
expect(protection.auth.unverifiedWrappers).toContain("withUnknown");
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
//# sourceMappingURL=wrappers.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrappers.test.js","sourceRoot":"","sources":["../../src/next/wrappers.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-boundary-missing.d.ts","sourceRoot":"","sources":["../../src/rules/auth-boundary-missing.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAA+B,MAAM,kBAAkB,CAAC;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAInE,eAAO,MAAM,OAAO,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"auth-boundary-missing.d.ts","sourceRoot":"","sources":["../../src/rules/auth-boundary-missing.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAA+B,MAAM,kBAAkB,CAAC;AAC/E,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAInE,eAAO,MAAM,OAAO,0BAA0B,CAAC;AAc/C,wBAAgB,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,EAAE,CAqFxE"}
|
|
@@ -2,9 +2,19 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { isAllowlisted } from "../util/paths.js";
|
|
4
4
|
export const RULE_ID = "AUTH-BOUNDARY-MISSING";
|
|
5
|
+
const SEVERITY_RANK = { critical: 4, high: 3, med: 2, low: 1 };
|
|
6
|
+
function severityFromConfidence(confidence, maxSeverity) {
|
|
7
|
+
const max = maxSeverity;
|
|
8
|
+
const maxRank = SEVERITY_RANK[max] ?? 4;
|
|
9
|
+
// high confidence → use max severity (typically critical)
|
|
10
|
+
// med confidence → cap at high
|
|
11
|
+
const computed = confidence === "high" ? max : "high";
|
|
12
|
+
const computedRank = SEVERITY_RANK[computed] ?? 3;
|
|
13
|
+
return computedRank > maxRank ? max : computed;
|
|
14
|
+
}
|
|
5
15
|
export function run(index, config) {
|
|
6
16
|
const findings = [];
|
|
7
|
-
const
|
|
17
|
+
const maxSeverity = config.rules[RULE_ID]?.severity ?? "critical";
|
|
8
18
|
const authAllowlist = config.hints.auth.allowlistPaths;
|
|
9
19
|
// Check mutation route handlers
|
|
10
20
|
for (const route of index.routes.mutationRoutes) {
|
|
@@ -14,7 +24,7 @@ export function run(index, config) {
|
|
|
14
24
|
if (result) {
|
|
15
25
|
findings.push({
|
|
16
26
|
ruleId: RULE_ID,
|
|
17
|
-
severity,
|
|
27
|
+
severity: severityFromConfidence(result.confidence, maxSeverity),
|
|
18
28
|
confidence: result.confidence,
|
|
19
29
|
message: `Route handler performs mutations without a recognized auth boundary`,
|
|
20
30
|
file: route.file,
|
|
@@ -43,7 +53,7 @@ export function run(index, config) {
|
|
|
43
53
|
if (result) {
|
|
44
54
|
findings.push({
|
|
45
55
|
ruleId: RULE_ID,
|
|
46
|
-
severity,
|
|
56
|
+
severity: severityFromConfidence(result.confidence, maxSeverity),
|
|
47
57
|
confidence: result.confidence,
|
|
48
58
|
message: `Server action performs mutations without a recognized auth boundary`,
|
|
49
59
|
file: action.file,
|
|
@@ -68,7 +78,7 @@ export function run(index, config) {
|
|
|
68
78
|
const confidence = proc.procedureType === "public" ? "high" : "med";
|
|
69
79
|
findings.push({
|
|
70
80
|
ruleId: RULE_ID,
|
|
71
|
-
severity,
|
|
81
|
+
severity: severityFromConfidence(confidence, maxSeverity),
|
|
72
82
|
confidence,
|
|
73
83
|
message: `tRPC mutation "${proc.name}" uses ${proc.procedureType}Procedure without auth boundary`,
|
|
74
84
|
file: proc.file,
|
|
@@ -88,19 +98,19 @@ export function run(index, config) {
|
|
|
88
98
|
return findings;
|
|
89
99
|
}
|
|
90
100
|
function checkRoute(route, index, config) {
|
|
101
|
+
// Use ProtectionSummary if available (computed during index building)
|
|
102
|
+
if (route.protection) {
|
|
103
|
+
if (route.protection.auth.satisfied)
|
|
104
|
+
return null;
|
|
105
|
+
// If wrapper introspection deferred this to WRAPPER-UNRECOGNIZED, don't emit per-route finding
|
|
106
|
+
if (route.protection.auth.unverifiedWrappers.length > 0)
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
91
109
|
const src = readSource(index.rootDir, route.file);
|
|
92
110
|
if (!src)
|
|
93
111
|
return null;
|
|
94
|
-
// Check if auth boundary exists (call inside handler body)
|
|
95
|
-
if (hasAuthCall(src, config.hints.auth.functions))
|
|
96
|
-
return null;
|
|
97
|
-
// Check if handler is wrapped by a known auth function (HOF pattern)
|
|
98
|
-
if (isWrappedByAuthFunction(src, config.hints.auth.functions))
|
|
99
|
-
return null;
|
|
100
|
-
// Check if middleware covers this route
|
|
101
|
-
if (isProtectedByMiddleware(route, index))
|
|
102
|
-
return null;
|
|
103
112
|
// Check for built-in auth patterns (webhook signatures, cron keys, etc.)
|
|
113
|
+
// These are always checked regardless of ProtectionSummary
|
|
104
114
|
if (hasBuiltInAuthPattern(src))
|
|
105
115
|
return null;
|
|
106
116
|
// No auth found — determine confidence
|
|
@@ -115,12 +125,6 @@ function checkRoute(route, index, config) {
|
|
|
115
125
|
confidenceRationale = "Medium: mutation evidence present but possible custom auth wrapper detected (not in hints)";
|
|
116
126
|
evidence.push("possible custom auth wrapper detected (not in hints)");
|
|
117
127
|
}
|
|
118
|
-
// Downgrade if handler is exported via HOF wrapper (unknown function)
|
|
119
|
-
if (isWrappedByUnknownFunction(src)) {
|
|
120
|
-
confidence = "med";
|
|
121
|
-
confidenceRationale = "Medium: handler is wrapped by a higher-order function (may contain auth)";
|
|
122
|
-
evidence.push("handler exported via HOF wrapper (may contain auth)");
|
|
123
|
-
}
|
|
124
128
|
// Find the line of the first mutation evidence for precise reporting
|
|
125
129
|
const line = findFirstMutationLine(src, route.signals);
|
|
126
130
|
return { confidence, confidenceRationale, line, evidence };
|
|
@@ -147,40 +151,12 @@ function checkServerAction(action, index, config) {
|
|
|
147
151
|
}
|
|
148
152
|
function hasAuthCall(src, authFunctions) {
|
|
149
153
|
for (const fn of authFunctions) {
|
|
150
|
-
// Match: fn(), await fn(), const x = fn(), const x = await fn()
|
|
151
154
|
const pattern = new RegExp(`\\b${escapeRegex(fn)}\\s*\\(`, "m");
|
|
152
155
|
if (pattern.test(src))
|
|
153
156
|
return true;
|
|
154
157
|
}
|
|
155
158
|
return false;
|
|
156
159
|
}
|
|
157
|
-
/**
|
|
158
|
-
* Detect Higher-Order Function (HOF) auth wrapper pattern.
|
|
159
|
-
* E.g.: export const GET = withWorkspace(async ({ workspace }) => { ... })
|
|
160
|
-
* export const POST = withSession(handler)
|
|
161
|
-
* export default withAuth(handler)
|
|
162
|
-
*/
|
|
163
|
-
function isWrappedByAuthFunction(src, authFunctions) {
|
|
164
|
-
for (const fn of authFunctions) {
|
|
165
|
-
const escaped = escapeRegex(fn);
|
|
166
|
-
// export const METHOD = fn(...) or export { fn as METHOD }
|
|
167
|
-
const hofPattern = new RegExp(`export\\s+(?:const|let|var)\\s+(?:GET|POST|PUT|PATCH|DELETE)\\s*=\\s*${escaped}\\s*\\(`, "m");
|
|
168
|
-
if (hofPattern.test(src))
|
|
169
|
-
return true;
|
|
170
|
-
// export default fn(...)
|
|
171
|
-
const defaultPattern = new RegExp(`export\\s+default\\s+${escaped}\\s*\\(`, "m");
|
|
172
|
-
if (defaultPattern.test(src))
|
|
173
|
-
return true;
|
|
174
|
-
}
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Detect if handler is exported via any HOF wrapper (unknown function name).
|
|
179
|
-
* Used to downgrade confidence when we see the wrapping pattern but don't know the function.
|
|
180
|
-
*/
|
|
181
|
-
function isWrappedByUnknownFunction(src) {
|
|
182
|
-
return /export\s+(?:const|let|var)\s+(?:GET|POST|PUT|PATCH|DELETE)\s*=\s*[a-zA-Z_]\w*\s*\(/m.test(src);
|
|
183
|
-
}
|
|
184
160
|
/**
|
|
185
161
|
* Built-in auth patterns that don't need to be in hints.
|
|
186
162
|
* These are common enough to detect automatically.
|
|
@@ -204,49 +180,16 @@ function hasBuiltInAuthPattern(src) {
|
|
|
204
180
|
if (/process\.env\.\w+SECRET\b/.test(src) && /headers\.get\s*\(/m.test(src))
|
|
205
181
|
return true;
|
|
206
182
|
// Supabase auth boundary — call-based, not import-based.
|
|
207
|
-
// Client creation (createServerClient, etc.) is NOT an auth boundary.
|
|
208
|
-
// Only .auth.getUser() / .auth.getSession() counts.
|
|
209
183
|
if (/\.auth\.getUser\s*\(/.test(src))
|
|
210
184
|
return true;
|
|
211
185
|
if (/\.auth\.getSession\s*\(/.test(src))
|
|
212
186
|
return true;
|
|
213
187
|
return false;
|
|
214
188
|
}
|
|
215
|
-
function isProtectedByMiddleware(route, index) {
|
|
216
|
-
if (!index.middleware.authLikely)
|
|
217
|
-
return false;
|
|
218
|
-
// If middleware has no matcher, it applies to all routes
|
|
219
|
-
if (index.middleware.matcherPatterns.length === 0)
|
|
220
|
-
return true;
|
|
221
|
-
// Check if any matcher pattern covers this route
|
|
222
|
-
const pathname = route.pathname ?? "";
|
|
223
|
-
for (const pattern of index.middleware.matcherPatterns) {
|
|
224
|
-
if (pathnameMatchesMatcher(pathname, pattern))
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
function pathnameMatchesMatcher(pathname, matcher) {
|
|
230
|
-
// Simple matcher check: does the matcher pattern cover this path?
|
|
231
|
-
// Next.js matchers use a regex-like syntax; for v1, do prefix matching
|
|
232
|
-
if (matcher.endsWith("/:path*")) {
|
|
233
|
-
const prefix = matcher.replace("/:path*", "");
|
|
234
|
-
return pathname.startsWith(prefix);
|
|
235
|
-
}
|
|
236
|
-
if (matcher.endsWith("(.*)")) {
|
|
237
|
-
const prefix = matcher.replace("(.*)", "");
|
|
238
|
-
return pathname.startsWith(prefix);
|
|
239
|
-
}
|
|
240
|
-
return pathname === matcher || pathname.startsWith(matcher + "/");
|
|
241
|
-
}
|
|
242
189
|
function hasPossibleCustomAuth(src) {
|
|
243
|
-
// Heuristic: looks for patterns like `verifyToken(`, `checkAuth(`, `requireAuth(`
|
|
244
|
-
// that could be custom auth wrappers we don't know about.
|
|
245
|
-
// Allow intermediate words: verifyUnsubscribeToken, checkUserAccessLevel, etc.
|
|
246
190
|
if (/\b(verify|check|require|validate|ensure|guard|protect)\w*(Token|Auth|Session|User|Access|Secret|Signature|Permission)\s*\(/i.test(src)) {
|
|
247
191
|
return true;
|
|
248
192
|
}
|
|
249
|
-
// Direct Authorization header reading is likely auth
|
|
250
193
|
if (/headers?\S*\.get\s*\(\s*["']authorization["']\s*\)/i.test(src)) {
|
|
251
194
|
return true;
|
|
252
195
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-boundary-missing.js","sourceRoot":"","sources":["../../src/rules/auth-boundary-missing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD,MAAM,CAAC,MAAM,OAAO,GAAG,uBAAuB,CAAC;AAE/C,MAAM,UAAU,GAAG,CAAC,KAAgB,EAAE,MAAuB;IAC3D,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,
|
|
1
|
+
{"version":3,"file":"auth-boundary-missing.js","sourceRoot":"","sources":["../../src/rules/auth-boundary-missing.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAMnE,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,eAAO,MAAM,aAAa,EAAE,QAAQ,EA6BnC,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,EAAE,CAmBhF"}
|
package/dist/rules/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as authBoundary from "./auth-boundary-missing.js";
|
|
2
2
|
import * as rateLimit from "./rate-limit-missing.js";
|
|
3
3
|
import * as tenancyScope from "./tenancy-scope-missing.js";
|
|
4
|
+
import * as wrapperUnrecognized from "./wrapper-unrecognized.js";
|
|
4
5
|
export const RULE_REGISTRY = [
|
|
5
6
|
{
|
|
6
7
|
id: "AUTH-BOUNDARY-MISSING",
|
|
@@ -23,6 +24,13 @@ export const RULE_REGISTRY = [
|
|
|
23
24
|
defaultSeverity: "critical",
|
|
24
25
|
docs: "Shipguard checks that Prisma queries include a tenant scoping field (orgId, tenantId, workspaceId) in their where clause. Only runs when Prisma is detected and the schema contains tenant fields. Configure field names in hints.tenancy.orgFieldNames.",
|
|
25
26
|
},
|
|
27
|
+
{
|
|
28
|
+
id: "WRAPPER-UNRECOGNIZED",
|
|
29
|
+
name: "Wrapper Unrecognized",
|
|
30
|
+
description: "Flags HOF wrappers that could not be analyzed for auth or rate-limit enforcement.",
|
|
31
|
+
defaultSeverity: "high",
|
|
32
|
+
docs: "Shipguard resolves and analyzes HOF wrapper implementations to detect auth and rate-limit enforcement. When a wrapper cannot be resolved or its enforcement cannot be verified, this rule emits a single grouped finding. Add the wrapper name to hints.auth.functions or hints.rateLimit.wrappers to suppress.",
|
|
33
|
+
},
|
|
26
34
|
];
|
|
27
35
|
export function runAllRules(index, config) {
|
|
28
36
|
const findings = [];
|
|
@@ -36,6 +44,10 @@ export function runAllRules(index, config) {
|
|
|
36
44
|
if (config.rules["TENANCY-SCOPE-MISSING"]) {
|
|
37
45
|
findings.push(...tenancyScope.run(index, config));
|
|
38
46
|
}
|
|
47
|
+
// WRAPPER-UNRECOGNIZED is always enabled unless explicitly configured out
|
|
48
|
+
if (config.rules["WRAPPER-UNRECOGNIZED"] !== undefined ? config.rules["WRAPPER-UNRECOGNIZED"] : true) {
|
|
49
|
+
findings.push(...wrapperUnrecognized.run(index, config));
|
|
50
|
+
}
|
|
39
51
|
return findings;
|
|
40
52
|
}
|
|
41
53
|
//# sourceMappingURL=index.js.map
|