@fourteensystems/shipguard 0.1.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 +213 -0
- package/bin/shipguard.mjs +2 -0
- package/dist/cli/commands/baseline.d.ts +7 -0
- package/dist/cli/commands/baseline.d.ts.map +1 -0
- package/dist/cli/commands/baseline.js +22 -0
- package/dist/cli/commands/baseline.js.map +1 -0
- package/dist/cli/commands/ci.d.ts +13 -0
- package/dist/cli/commands/ci.d.ts.map +1 -0
- package/dist/cli/commands/ci.js +91 -0
- package/dist/cli/commands/ci.js.map +1 -0
- package/dist/cli/commands/explain.d.ts +2 -0
- package/dist/cli/commands/explain.d.ts.map +1 -0
- package/dist/cli/commands/explain.js +20 -0
- package/dist/cli/commands/explain.js.map +1 -0
- package/dist/cli/commands/init.d.ts +7 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +91 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/rules.d.ts +2 -0
- package/dist/cli/commands/rules.d.ts.map +1 -0
- package/dist/cli/commands/rules.js +13 -0
- package/dist/cli/commands/rules.js.map +1 -0
- package/dist/cli/commands/scan.d.ts +10 -0
- package/dist/cli/commands/scan.d.ts.map +1 -0
- package/dist/cli/commands/scan.js +55 -0
- package/dist/cli/commands/scan.js.map +1 -0
- package/dist/cli/commands/waive.d.ts +8 -0
- package/dist/cli/commands/waive.d.ts.map +1 -0
- package/dist/cli/commands/waive.js +34 -0
- package/dist/cli/commands/waive.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +63 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/engine/baseline.d.ts +11 -0
- package/dist/engine/baseline.d.ts.map +1 -0
- package/dist/engine/baseline.js +39 -0
- package/dist/engine/baseline.js.map +1 -0
- package/dist/engine/config.d.ts +8 -0
- package/dist/engine/config.d.ts.map +1 -0
- package/dist/engine/config.js +130 -0
- package/dist/engine/config.js.map +1 -0
- package/dist/engine/extensions/load.d.ts +11 -0
- package/dist/engine/extensions/load.d.ts.map +1 -0
- package/dist/engine/extensions/load.js +26 -0
- package/dist/engine/extensions/load.js.map +1 -0
- package/dist/engine/extensions/registry.d.ts +5 -0
- package/dist/engine/extensions/registry.d.ts.map +1 -0
- package/dist/engine/extensions/registry.js +11 -0
- package/dist/engine/extensions/registry.js.map +1 -0
- package/dist/engine/extensions/types.d.ts +51 -0
- package/dist/engine/extensions/types.d.ts.map +1 -0
- package/dist/engine/extensions/types.js +2 -0
- package/dist/engine/extensions/types.js.map +1 -0
- package/dist/engine/report.d.ts +5 -0
- package/dist/engine/report.d.ts.map +1 -0
- package/dist/engine/report.js +88 -0
- package/dist/engine/report.js.map +1 -0
- package/dist/engine/run.d.ts +9 -0
- package/dist/engine/run.d.ts.map +1 -0
- package/dist/engine/run.js +101 -0
- package/dist/engine/run.js.map +1 -0
- package/dist/engine/sarif.d.ts +3 -0
- package/dist/engine/sarif.d.ts.map +1 -0
- package/dist/engine/sarif.js +58 -0
- package/dist/engine/sarif.js.map +1 -0
- package/dist/engine/score.d.ts +13 -0
- package/dist/engine/score.d.ts.map +1 -0
- package/dist/engine/score.js +97 -0
- package/dist/engine/score.js.map +1 -0
- package/dist/engine/types.d.ts +119 -0
- package/dist/engine/types.d.ts.map +1 -0
- package/dist/engine/types.js +2 -0
- package/dist/engine/types.js.map +1 -0
- package/dist/engine/version.d.ts +5 -0
- package/dist/engine/version.d.ts.map +1 -0
- package/dist/engine/version.js +15 -0
- package/dist/engine/version.js.map +1 -0
- package/dist/engine/waivers.d.ts +9 -0
- package/dist/engine/waivers.d.ts.map +1 -0
- package/dist/engine/waivers.js +55 -0
- package/dist/engine/waivers.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/next/deps.d.ts +4 -0
- package/dist/next/deps.d.ts.map +1 -0
- package/dist/next/deps.js +102 -0
- package/dist/next/deps.js.map +1 -0
- package/dist/next/detect.d.ts +10 -0
- package/dist/next/detect.d.ts.map +1 -0
- package/dist/next/detect.js +57 -0
- package/dist/next/detect.js.map +1 -0
- package/dist/next/index.d.ts +5 -0
- package/dist/next/index.d.ts.map +1 -0
- package/dist/next/index.js +41 -0
- package/dist/next/index.js.map +1 -0
- package/dist/next/middleware.d.ts +3 -0
- package/dist/next/middleware.d.ts.map +1 -0
- package/dist/next/middleware.js +33 -0
- package/dist/next/middleware.js.map +1 -0
- package/dist/next/routes.d.ts +5 -0
- package/dist/next/routes.d.ts.map +1 -0
- package/dist/next/routes.js +125 -0
- package/dist/next/routes.js.map +1 -0
- package/dist/next/server-actions.d.ts +4 -0
- package/dist/next/server-actions.d.ts.map +1 -0
- package/dist/next/server-actions.js +107 -0
- package/dist/next/server-actions.js.map +1 -0
- package/dist/next/trpc.d.ts +3 -0
- package/dist/next/trpc.d.ts.map +1 -0
- package/dist/next/trpc.js +339 -0
- package/dist/next/trpc.js.map +1 -0
- package/dist/next/types.d.ts +100 -0
- package/dist/next/types.d.ts.map +1 -0
- package/dist/next/types.js +2 -0
- package/dist/next/types.js.map +1 -0
- package/dist/rules/auth-boundary-missing.d.ts +5 -0
- package/dist/rules/auth-boundary-missing.d.ts.map +1 -0
- package/dist/rules/auth-boundary-missing.js +278 -0
- package/dist/rules/auth-boundary-missing.js.map +1 -0
- package/dist/rules/index.d.ts +12 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +41 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/rate-limit-missing.d.ts +5 -0
- package/dist/rules/rate-limit-missing.d.ts.map +1 -0
- package/dist/rules/rate-limit-missing.js +230 -0
- package/dist/rules/rate-limit-missing.js.map +1 -0
- package/dist/rules/tenancy-scope-missing.d.ts +5 -0
- package/dist/rules/tenancy-scope-missing.d.ts.map +1 -0
- package/dist/rules/tenancy-scope-missing.js +149 -0
- package/dist/rules/tenancy-scope-missing.js.map +1 -0
- package/dist/util/paths.d.ts +6 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +18 -0
- package/dist/util/paths.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { isAllowlisted } from "../util/paths.js";
|
|
4
|
+
export const RULE_ID = "AUTH-BOUNDARY-MISSING";
|
|
5
|
+
export function run(index, config) {
|
|
6
|
+
const findings = [];
|
|
7
|
+
const severity = config.rules[RULE_ID]?.severity ?? "critical";
|
|
8
|
+
const authAllowlist = config.hints.auth.allowlistPaths;
|
|
9
|
+
// Check mutation route handlers
|
|
10
|
+
for (const route of index.routes.mutationRoutes) {
|
|
11
|
+
if (isAllowlisted(route.file, authAllowlist))
|
|
12
|
+
continue;
|
|
13
|
+
const result = checkRoute(route, index, config);
|
|
14
|
+
if (result) {
|
|
15
|
+
findings.push({
|
|
16
|
+
ruleId: RULE_ID,
|
|
17
|
+
severity,
|
|
18
|
+
confidence: result.confidence,
|
|
19
|
+
message: `Route handler performs mutations without a recognized auth boundary`,
|
|
20
|
+
file: route.file,
|
|
21
|
+
line: result.line,
|
|
22
|
+
snippet: result.snippet,
|
|
23
|
+
evidence: result.evidence,
|
|
24
|
+
confidenceRationale: result.confidenceRationale,
|
|
25
|
+
remediation: [
|
|
26
|
+
"Add an auth check at the top of the handler (e.g., `const session = await auth()`)",
|
|
27
|
+
"Ensure middleware.ts protects this route segment",
|
|
28
|
+
"If using a custom auth wrapper, add it to hints.auth.functions in shipguard.config.json",
|
|
29
|
+
],
|
|
30
|
+
tags: ["auth", "server"],
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Check mutation server actions (deduplicate by file since auth check is file-level)
|
|
35
|
+
const seenActionFiles = new Set();
|
|
36
|
+
for (const action of index.serverActions.mutationActions) {
|
|
37
|
+
if (seenActionFiles.has(action.file))
|
|
38
|
+
continue;
|
|
39
|
+
seenActionFiles.add(action.file);
|
|
40
|
+
if (isAllowlisted(action.file, authAllowlist))
|
|
41
|
+
continue;
|
|
42
|
+
const result = checkServerAction(action, index, config);
|
|
43
|
+
if (result) {
|
|
44
|
+
findings.push({
|
|
45
|
+
ruleId: RULE_ID,
|
|
46
|
+
severity,
|
|
47
|
+
confidence: result.confidence,
|
|
48
|
+
message: `Server action performs mutations without a recognized auth boundary`,
|
|
49
|
+
file: action.file,
|
|
50
|
+
line: result.line,
|
|
51
|
+
snippet: result.snippet,
|
|
52
|
+
evidence: result.evidence,
|
|
53
|
+
confidenceRationale: result.confidenceRationale,
|
|
54
|
+
remediation: [
|
|
55
|
+
"Add an auth check at the top of the server action",
|
|
56
|
+
"If using a custom auth wrapper, add it to hints.auth.functions in shipguard.config.json",
|
|
57
|
+
],
|
|
58
|
+
tags: ["auth", "server-action"],
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Check tRPC mutation procedures
|
|
63
|
+
for (const proc of index.trpc.mutationProcedures) {
|
|
64
|
+
if (proc.procedureType === "protected")
|
|
65
|
+
continue;
|
|
66
|
+
if (isAllowlisted(proc.file, authAllowlist))
|
|
67
|
+
continue;
|
|
68
|
+
const confidence = proc.procedureType === "public" ? "high" : "med";
|
|
69
|
+
findings.push({
|
|
70
|
+
ruleId: RULE_ID,
|
|
71
|
+
severity,
|
|
72
|
+
confidence,
|
|
73
|
+
message: `tRPC mutation "${proc.name}" uses ${proc.procedureType}Procedure without auth boundary`,
|
|
74
|
+
file: proc.file,
|
|
75
|
+
line: proc.line,
|
|
76
|
+
evidence: [`${proc.procedureType}Procedure.mutation()`, ...proc.signals.mutationDetails],
|
|
77
|
+
confidenceRationale: proc.procedureType === "public"
|
|
78
|
+
? "High: public tRPC mutation with no auth boundary"
|
|
79
|
+
: "Medium: unrecognized procedure type (may have auth middleware)",
|
|
80
|
+
remediation: [
|
|
81
|
+
"Use protectedProcedure instead of publicProcedure for mutations",
|
|
82
|
+
"Add auth middleware to the procedure chain: publicProcedure.use(authMiddleware).mutation(...)",
|
|
83
|
+
"If this mutation is intentionally public, add a waiver with reason",
|
|
84
|
+
],
|
|
85
|
+
tags: ["auth", "trpc"],
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return findings;
|
|
89
|
+
}
|
|
90
|
+
function checkRoute(route, index, config) {
|
|
91
|
+
const src = readSource(index.rootDir, route.file);
|
|
92
|
+
if (!src)
|
|
93
|
+
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
|
+
// Check for built-in auth patterns (webhook signatures, cron keys, etc.)
|
|
104
|
+
if (hasBuiltInAuthPattern(src))
|
|
105
|
+
return null;
|
|
106
|
+
// No auth found — determine confidence
|
|
107
|
+
const evidence = [...route.signals.mutationDetails];
|
|
108
|
+
evidence.push(`No auth function calls matched: ${config.hints.auth.functions.join(", ")}`);
|
|
109
|
+
evidence.push("No middleware auth covering this route");
|
|
110
|
+
let confidence = "high";
|
|
111
|
+
let confidenceRationale = "High: mutation evidence + no auth calls + no middleware coverage";
|
|
112
|
+
// Downgrade if we see any function calls that could be custom auth
|
|
113
|
+
if (hasPossibleCustomAuth(src)) {
|
|
114
|
+
confidence = "med";
|
|
115
|
+
confidenceRationale = "Medium: mutation evidence present but possible custom auth wrapper detected (not in hints)";
|
|
116
|
+
evidence.push("possible custom auth wrapper detected (not in hints)");
|
|
117
|
+
}
|
|
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
|
+
// Find the line of the first mutation evidence for precise reporting
|
|
125
|
+
const line = findFirstMutationLine(src, route.signals);
|
|
126
|
+
return { confidence, confidenceRationale, line, evidence };
|
|
127
|
+
}
|
|
128
|
+
function checkServerAction(action, index, config) {
|
|
129
|
+
const src = readSource(index.rootDir, action.file);
|
|
130
|
+
if (!src)
|
|
131
|
+
return null;
|
|
132
|
+
if (hasAuthCall(src, config.hints.auth.functions))
|
|
133
|
+
return null;
|
|
134
|
+
if (hasBuiltInAuthPattern(src))
|
|
135
|
+
return null;
|
|
136
|
+
const evidence = [...action.signals.mutationDetails];
|
|
137
|
+
evidence.push(`No auth function calls matched: ${config.hints.auth.functions.join(", ")}`);
|
|
138
|
+
let confidence = "high";
|
|
139
|
+
let confidenceRationale = "High: server action with mutation evidence + no auth calls";
|
|
140
|
+
if (hasPossibleCustomAuth(src)) {
|
|
141
|
+
confidence = "med";
|
|
142
|
+
confidenceRationale = "Medium: mutation evidence present but possible custom auth wrapper detected (not in hints)";
|
|
143
|
+
evidence.push("possible custom auth wrapper detected (not in hints)");
|
|
144
|
+
}
|
|
145
|
+
const line = findFirstMutationLine(src, action.signals);
|
|
146
|
+
return { confidence, confidenceRationale, line, evidence };
|
|
147
|
+
}
|
|
148
|
+
function hasAuthCall(src, authFunctions) {
|
|
149
|
+
for (const fn of authFunctions) {
|
|
150
|
+
// Match: fn(), await fn(), const x = fn(), const x = await fn()
|
|
151
|
+
const pattern = new RegExp(`\\b${escapeRegex(fn)}\\s*\\(`, "m");
|
|
152
|
+
if (pattern.test(src))
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
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
|
+
/**
|
|
185
|
+
* Built-in auth patterns that don't need to be in hints.
|
|
186
|
+
* These are common enough to detect automatically.
|
|
187
|
+
*/
|
|
188
|
+
function hasBuiltInAuthPattern(src) {
|
|
189
|
+
// Stripe webhook signature verification
|
|
190
|
+
if (/stripe\.webhooks\.constructEvent\s*\(/m.test(src))
|
|
191
|
+
return true;
|
|
192
|
+
// Vercel/QStash cron signature verification
|
|
193
|
+
if (/verifyVercelSignature\s*\(/m.test(src))
|
|
194
|
+
return true;
|
|
195
|
+
if (/verifyQstashSignature\s*\(/m.test(src))
|
|
196
|
+
return true;
|
|
197
|
+
// HMAC webhook signature verification (crypto.createHmac + comparison)
|
|
198
|
+
if (/createHmac\s*\(/.test(src) && /signature/i.test(src))
|
|
199
|
+
return true;
|
|
200
|
+
// Cron API key / secret env var check
|
|
201
|
+
if (/process\.env\.CRON_(?:API_KEY|SECRET)/m.test(src))
|
|
202
|
+
return true;
|
|
203
|
+
// Shared secret header verification (common webhook pattern)
|
|
204
|
+
if (/process\.env\.\w+SECRET\b/.test(src) && /headers\.get\s*\(/m.test(src))
|
|
205
|
+
return true;
|
|
206
|
+
// 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
|
+
if (/\.auth\.getUser\s*\(/.test(src))
|
|
210
|
+
return true;
|
|
211
|
+
if (/\.auth\.getSession\s*\(/.test(src))
|
|
212
|
+
return true;
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
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
|
+
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
|
+
if (/\b(verify|check|require|validate|ensure|guard|protect)\w*(Token|Auth|Session|User|Access|Secret|Signature|Permission)\s*\(/i.test(src)) {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
// Direct Authorization header reading is likely auth
|
|
250
|
+
if (/headers?\S*\.get\s*\(\s*["']authorization["']\s*\)/i.test(src)) {
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
function findFirstMutationLine(src, signals) {
|
|
256
|
+
const lines = src.split("\n");
|
|
257
|
+
for (let i = 0; i < lines.length; i++) {
|
|
258
|
+
if (/\.(create|update|delete|upsert|createMany|updateMany|deleteMany)\s*\(/.test(lines[i])) {
|
|
259
|
+
return i + 1;
|
|
260
|
+
}
|
|
261
|
+
if (/stripe\.\w+\.(create|update|del)\s*\(/.test(lines[i])) {
|
|
262
|
+
return i + 1;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
function readSource(rootDir, file) {
|
|
268
|
+
try {
|
|
269
|
+
return readFileSync(path.join(rootDir, file), "utf8");
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function escapeRegex(str) {
|
|
276
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=auth-boundary-missing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-boundary-missing.js","sourceRoot":"","sources":["../../src/rules/auth-boundary-missing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { NextIndex } from "../next/types.js";
|
|
2
|
+
import type { Finding, ShipguardConfig } from "../engine/types.js";
|
|
3
|
+
export interface RuleMeta {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description: string;
|
|
7
|
+
defaultSeverity: string;
|
|
8
|
+
docs: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const RULE_REGISTRY: RuleMeta[];
|
|
11
|
+
export declare function runAllRules(index: NextIndex, config: ShipguardConfig): Finding[];
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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;AAKnE,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,EAsBnC,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,EAAE,CAehF"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as authBoundary from "./auth-boundary-missing.js";
|
|
2
|
+
import * as rateLimit from "./rate-limit-missing.js";
|
|
3
|
+
import * as tenancyScope from "./tenancy-scope-missing.js";
|
|
4
|
+
export const RULE_REGISTRY = [
|
|
5
|
+
{
|
|
6
|
+
id: "AUTH-BOUNDARY-MISSING",
|
|
7
|
+
name: "Auth Boundary Missing",
|
|
8
|
+
description: "Flags server-side mutation endpoints that lack a recognized authentication boundary.",
|
|
9
|
+
defaultSeverity: "critical",
|
|
10
|
+
docs: "Shipguard checks for calls to known auth functions (auth(), getServerSession(), currentUser(), etc.) in route handlers and server actions that perform database writes or Stripe operations. Configure additional auth function names in hints.auth.functions.",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "RATE-LIMIT-MISSING",
|
|
14
|
+
name: "Rate Limit Missing",
|
|
15
|
+
description: "Flags public API routes without recognized rate limiting.",
|
|
16
|
+
defaultSeverity: "critical",
|
|
17
|
+
docs: "Shipguard checks for calls to known rate limiting wrappers (@upstash/ratelimit, rate-limiter-flexible, etc.) in API route handlers. If rate limiting is handled at the edge (Cloudflare WAF, Vercel), add a waiver. Configure custom wrappers in hints.rateLimit.wrappers.",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "TENANCY-SCOPE-MISSING",
|
|
21
|
+
name: "Tenancy Scope Missing",
|
|
22
|
+
description: "Flags Prisma queries on tenant-owned models that lack tenant field in the where clause.",
|
|
23
|
+
defaultSeverity: "critical",
|
|
24
|
+
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
|
+
export function runAllRules(index, config) {
|
|
28
|
+
const findings = [];
|
|
29
|
+
// Only run rules that are configured (all 3 are on by default)
|
|
30
|
+
if (config.rules["AUTH-BOUNDARY-MISSING"]) {
|
|
31
|
+
findings.push(...authBoundary.run(index, config));
|
|
32
|
+
}
|
|
33
|
+
if (config.rules["RATE-LIMIT-MISSING"]) {
|
|
34
|
+
findings.push(...rateLimit.run(index, config));
|
|
35
|
+
}
|
|
36
|
+
if (config.rules["TENANCY-SCOPE-MISSING"]) {
|
|
37
|
+
findings.push(...tenancyScope.run(index, config));
|
|
38
|
+
}
|
|
39
|
+
return findings;
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rules/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,YAAY,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,SAAS,MAAM,yBAAyB,CAAC;AACrD,OAAO,KAAK,YAAY,MAAM,4BAA4B,CAAC;AAU3D,MAAM,CAAC,MAAM,aAAa,GAAe;IACvC;QACE,EAAE,EAAE,uBAAuB;QAC3B,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EAAE,sFAAsF;QACnG,eAAe,EAAE,UAAU;QAC3B,IAAI,EAAE,gQAAgQ;KACvQ;IACD;QACE,EAAE,EAAE,oBAAoB;QACxB,IAAI,EAAE,oBAAoB;QAC1B,WAAW,EAAE,2DAA2D;QACxE,eAAe,EAAE,UAAU;QAC3B,IAAI,EAAE,4QAA4Q;KACnR;IACD;QACE,EAAE,EAAE,uBAAuB;QAC3B,IAAI,EAAE,uBAAuB;QAC7B,WAAW,EAAE,yFAAyF;QACtG,eAAe,EAAE,UAAU;QAC3B,IAAI,EAAE,0PAA0P;KACjQ;CACF,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,KAAgB,EAAE,MAAuB;IACnE,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,+DAA+D;IAC/D,IAAI,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,EAAE,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,CAAC;QACvC,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD,CAAC;IACD,IAAI,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,EAAE,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -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 = "RATE-LIMIT-MISSING";
|
|
4
|
+
export declare function run(index: NextIndex, config: ShipguardConfig): Finding[];
|
|
5
|
+
//# sourceMappingURL=rate-limit-missing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-missing.d.ts","sourceRoot":"","sources":["../../src/rules/rate-limit-missing.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAInE,eAAO,MAAM,OAAO,uBAAuB,CAAC;AAyB5C,wBAAgB,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,EAAE,CAkExE"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { isAllowlisted } from "../util/paths.js";
|
|
4
|
+
export const RULE_ID = "RATE-LIMIT-MISSING";
|
|
5
|
+
/**
|
|
6
|
+
* Paths commonly excluded from rate limiting.
|
|
7
|
+
* Health checks, static assets, internal probes, cron/task routes.
|
|
8
|
+
*/
|
|
9
|
+
const EXEMPT_PATH_PATTERNS = [
|
|
10
|
+
/\/health$/,
|
|
11
|
+
/\/ping$/,
|
|
12
|
+
/\/ready$/,
|
|
13
|
+
/\/live$/,
|
|
14
|
+
/\/_next\//,
|
|
15
|
+
/\/cron\//, // Cron routes are server-to-server
|
|
16
|
+
/\/tasks\//, // Task/job routes are server-to-server
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Webhook path patterns — rate limiting is inappropriate for inbound webhooks.
|
|
20
|
+
* The calling service controls the call rate, and rejecting would miss events.
|
|
21
|
+
*/
|
|
22
|
+
const WEBHOOK_PATH_PATTERNS = [
|
|
23
|
+
/\/webhooks?\//, // /webhook/ or /webhooks/
|
|
24
|
+
/\/webhooks?$/, // /webhook or /webhooks (terminal)
|
|
25
|
+
];
|
|
26
|
+
export function run(index, config) {
|
|
27
|
+
const findings = [];
|
|
28
|
+
const severity = config.rules[RULE_ID]?.severity ?? "critical";
|
|
29
|
+
for (const route of index.routes.all) {
|
|
30
|
+
// Only check API routes
|
|
31
|
+
if (!route.isApi)
|
|
32
|
+
continue;
|
|
33
|
+
// Skip exempt paths and user allowlisted paths
|
|
34
|
+
if (isExemptPath(route.pathname))
|
|
35
|
+
continue;
|
|
36
|
+
if (isAllowlisted(route.file, config.hints.rateLimit.allowlistPaths))
|
|
37
|
+
continue;
|
|
38
|
+
// Skip tRPC proxy routes — rate limiting is checked at the procedure level
|
|
39
|
+
if (index.trpc.detected && route.file === index.trpc.proxyFile)
|
|
40
|
+
continue;
|
|
41
|
+
const result = checkRoute(route, index, config);
|
|
42
|
+
if (result) {
|
|
43
|
+
findings.push({
|
|
44
|
+
ruleId: RULE_ID,
|
|
45
|
+
severity,
|
|
46
|
+
confidence: result.confidence,
|
|
47
|
+
message: `Public API route has no recognized rate limiting`,
|
|
48
|
+
file: route.file,
|
|
49
|
+
line: result.line,
|
|
50
|
+
snippet: result.snippet,
|
|
51
|
+
evidence: result.evidence,
|
|
52
|
+
confidenceRationale: result.confidenceRationale,
|
|
53
|
+
remediation: [
|
|
54
|
+
"Add rate limiting middleware or wrapper to this route",
|
|
55
|
+
"If using @upstash/ratelimit, wrap the handler with a rate limit check",
|
|
56
|
+
"If rate limiting is handled at the edge (Cloudflare, Vercel), add a waiver with reason",
|
|
57
|
+
"Add custom wrapper names to hints.rateLimit.wrappers in config",
|
|
58
|
+
],
|
|
59
|
+
tags: ["rate-limit", "server"],
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Check tRPC public mutation procedures
|
|
64
|
+
for (const proc of index.trpc.mutationProcedures) {
|
|
65
|
+
if (proc.procedureType === "protected")
|
|
66
|
+
continue;
|
|
67
|
+
if (isAllowlisted(proc.file, config.hints.rateLimit.allowlistPaths))
|
|
68
|
+
continue;
|
|
69
|
+
// Check if the procedure's file has rate limit calls
|
|
70
|
+
const src = readSource(index.rootDir, proc.file);
|
|
71
|
+
if (src && hasRateLimitCall(src, config.hints.rateLimit.wrappers))
|
|
72
|
+
continue;
|
|
73
|
+
findings.push({
|
|
74
|
+
ruleId: RULE_ID,
|
|
75
|
+
severity,
|
|
76
|
+
confidence: "med",
|
|
77
|
+
message: `tRPC public mutation "${proc.name}" has no recognized rate limiting`,
|
|
78
|
+
file: proc.file,
|
|
79
|
+
line: proc.line,
|
|
80
|
+
evidence: [`${proc.procedureType}Procedure.mutation() without rate limit wrapper`],
|
|
81
|
+
confidenceRationale: "Medium: tRPC rate limiting may be handled at middleware or procedure level (not detected)",
|
|
82
|
+
remediation: [
|
|
83
|
+
"Add rate limiting middleware to the procedure chain",
|
|
84
|
+
"If rate limiting is handled at the tRPC middleware level, add a waiver with reason",
|
|
85
|
+
"If rate limiting is at the edge (Cloudflare, Vercel), add a waiver",
|
|
86
|
+
],
|
|
87
|
+
tags: ["rate-limit", "trpc"],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return findings;
|
|
91
|
+
}
|
|
92
|
+
function checkRoute(route, index, config) {
|
|
93
|
+
const src = readSource(index.rootDir, route.file);
|
|
94
|
+
if (!src)
|
|
95
|
+
return null;
|
|
96
|
+
// Check for known rate limit wrappers (call inside handler body)
|
|
97
|
+
if (hasRateLimitCall(src, config.hints.rateLimit.wrappers))
|
|
98
|
+
return null;
|
|
99
|
+
// Check if handler is wrapped by a known rate-limit function (HOF pattern)
|
|
100
|
+
if (isWrappedByKnownFunction(src, config.hints.rateLimit.wrappers))
|
|
101
|
+
return null;
|
|
102
|
+
// Check if middleware handles rate limiting for this route
|
|
103
|
+
if (index.middleware.rateLimitLikely)
|
|
104
|
+
return null;
|
|
105
|
+
// Routes with webhook signature verification don't need rate limiting
|
|
106
|
+
if (hasWebhookSignatureAuth(src))
|
|
107
|
+
return null;
|
|
108
|
+
if (isWebhookPath(route.pathname))
|
|
109
|
+
return null;
|
|
110
|
+
// Routes with cron key auth are server-to-server (no rate limiting needed)
|
|
111
|
+
if (hasCronKeyAuth(src))
|
|
112
|
+
return null;
|
|
113
|
+
// No rate limiting found
|
|
114
|
+
const evidence = [];
|
|
115
|
+
evidence.push(`No rate limit wrapper calls matched: ${config.hints.rateLimit.wrappers.join(", ")}`);
|
|
116
|
+
evidence.push("No middleware-level rate limiting detected");
|
|
117
|
+
let confidence;
|
|
118
|
+
let confidenceRationale;
|
|
119
|
+
if (route.signals.hasMutationEvidence || route.signals.hasDbWriteEvidence) {
|
|
120
|
+
confidence = "high";
|
|
121
|
+
confidenceRationale = "High: mutation route without rate limiting (higher abuse risk)";
|
|
122
|
+
evidence.push("route performs mutations (higher abuse risk)");
|
|
123
|
+
evidence.push(...route.signals.mutationDetails);
|
|
124
|
+
}
|
|
125
|
+
else if (hasBodyParsing(src)) {
|
|
126
|
+
confidence = "high";
|
|
127
|
+
confidenceRationale = "High: route reads request body without rate limiting";
|
|
128
|
+
evidence.push("route reads request body");
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
confidence = "med";
|
|
132
|
+
confidenceRationale = "Medium: public API route without rate limiting (GET-only, lower risk)";
|
|
133
|
+
evidence.push("public API route without rate limiting");
|
|
134
|
+
}
|
|
135
|
+
// Downgrade if handler is exported via unknown HOF wrapper (may contain rate limiting)
|
|
136
|
+
if (isWrappedByUnknownFunction(src)) {
|
|
137
|
+
if (confidence === "high") {
|
|
138
|
+
confidence = "med";
|
|
139
|
+
confidenceRationale = "Medium: handler is wrapped by a higher-order function (may contain rate limiting)";
|
|
140
|
+
}
|
|
141
|
+
evidence.push("handler exported via HOF wrapper (may contain rate limiting)");
|
|
142
|
+
}
|
|
143
|
+
return { confidence, confidenceRationale, evidence };
|
|
144
|
+
}
|
|
145
|
+
function hasRateLimitCall(src, wrappers) {
|
|
146
|
+
for (const wrapper of wrappers) {
|
|
147
|
+
const pattern = new RegExp(`\\b${escapeRegex(wrapper)}\\s*[.(]`, "m");
|
|
148
|
+
if (pattern.test(src))
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
// Also check for common rate limit import patterns
|
|
152
|
+
if (/@upstash\/ratelimit/.test(src))
|
|
153
|
+
return true;
|
|
154
|
+
if (/rate-limiter-flexible/.test(src))
|
|
155
|
+
return true;
|
|
156
|
+
if (/@arcjet\/next/.test(src))
|
|
157
|
+
return true;
|
|
158
|
+
if (/@unkey\/ratelimit/.test(src))
|
|
159
|
+
return true;
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Check if handler is wrapped by a known function (HOF pattern).
|
|
164
|
+
* E.g.: export const GET = withWorkspace(async () => { ... })
|
|
165
|
+
*/
|
|
166
|
+
function isWrappedByKnownFunction(src, functions) {
|
|
167
|
+
for (const fn of functions) {
|
|
168
|
+
const escaped = escapeRegex(fn);
|
|
169
|
+
const hofPattern = new RegExp(`export\\s+(?:const|let|var)\\s+(?:GET|POST|PUT|PATCH|DELETE)\\s*=\\s*${escaped}\\s*\\(`, "m");
|
|
170
|
+
if (hofPattern.test(src))
|
|
171
|
+
return true;
|
|
172
|
+
const defaultPattern = new RegExp(`export\\s+default\\s+${escaped}\\s*\\(`, "m");
|
|
173
|
+
if (defaultPattern.test(src))
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Detect if handler is exported via any HOF wrapper (unknown function name).
|
|
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
|
+
/**
|
|
185
|
+
* Webhook signature verification — these routes don't need rate limiting.
|
|
186
|
+
*/
|
|
187
|
+
function hasWebhookSignatureAuth(src) {
|
|
188
|
+
if (/stripe\.webhooks\.constructEvent\s*\(/m.test(src))
|
|
189
|
+
return true;
|
|
190
|
+
if (/verifyQstashSignature\s*\(/m.test(src))
|
|
191
|
+
return true;
|
|
192
|
+
if (/createHmac\s*\(/.test(src) && /signature/i.test(src))
|
|
193
|
+
return true;
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Cron routes protected by API key are server-to-server.
|
|
198
|
+
*/
|
|
199
|
+
function hasCronKeyAuth(src) {
|
|
200
|
+
if (/process\.env\.CRON_(?:API_KEY|SECRET)/m.test(src))
|
|
201
|
+
return true;
|
|
202
|
+
if (/verifyVercelSignature\s*\(/m.test(src))
|
|
203
|
+
return true;
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
function isWebhookPath(pathname) {
|
|
207
|
+
if (!pathname)
|
|
208
|
+
return false;
|
|
209
|
+
return WEBHOOK_PATH_PATTERNS.some((p) => p.test(pathname));
|
|
210
|
+
}
|
|
211
|
+
function hasBodyParsing(src) {
|
|
212
|
+
return /request\.json\s*\(|request\.formData\s*\(|req\.body/.test(src);
|
|
213
|
+
}
|
|
214
|
+
function isExemptPath(pathname) {
|
|
215
|
+
if (!pathname)
|
|
216
|
+
return false;
|
|
217
|
+
return EXEMPT_PATH_PATTERNS.some((p) => p.test(pathname));
|
|
218
|
+
}
|
|
219
|
+
function readSource(rootDir, file) {
|
|
220
|
+
try {
|
|
221
|
+
return readFileSync(path.join(rootDir, file), "utf8");
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function escapeRegex(str) {
|
|
228
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=rate-limit-missing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-missing.js","sourceRoot":"","sources":["../../src/rules/rate-limit-missing.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 = "TENANCY-SCOPE-MISSING";
|
|
4
|
+
export declare function run(index: NextIndex, config: ShipguardConfig): Finding[];
|
|
5
|
+
//# sourceMappingURL=tenancy-scope-missing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tenancy-scope-missing.d.ts","sourceRoot":"","sources":["../../src/rules/tenancy-scope-missing.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGnE,eAAO,MAAM,OAAO,0BAA0B,CAAC;AAY/C,wBAAgB,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,EAAE,CAkDxE"}
|