@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.
- package/README.md +46 -112
- 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 +110 -71
- package/dist/rules/rate-limit-missing.js.map +1 -1
- package/dist/rules/rate-limit-missing.test.d.ts +2 -0
- package/dist/rules/rate-limit-missing.test.d.ts.map +1 -0
- package/dist/rules/rate-limit-missing.test.js +325 -0
- package/dist/rules/rate-limit-missing.test.js.map +1 -0
- package/dist/rules/wrapper-unrecognized.d.ts +5 -0
- package/dist/rules/wrapper-unrecognized.d.ts.map +1 -0
- package/dist/rules/wrapper-unrecognized.js +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
package/dist/rules/index.js.map
CHANGED
|
@@ -1 +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;
|
|
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;AAC3D,OAAO,KAAK,mBAAmB,MAAM,2BAA2B,CAAC;AAUjE,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;IACD;QACE,EAAE,EAAE,sBAAsB;QAC1B,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,mFAAmF;QAChG,eAAe,EAAE,MAAM;QACvB,IAAI,EAAE,iTAAiT;KACxT;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;IACD,0EAA0E;IAC1E,IAAI,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrG,QAAQ,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -1 +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;
|
|
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;AAsC5C,wBAAgB,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,EAAE,CAoFxE"}
|
|
@@ -17,15 +17,27 @@ const EXEMPT_PATH_PATTERNS = [
|
|
|
17
17
|
];
|
|
18
18
|
/**
|
|
19
19
|
* Webhook path patterns — rate limiting is inappropriate for inbound webhooks.
|
|
20
|
-
*
|
|
20
|
+
* Matches any path containing "webhook" (e.g., /stripe-webhook, /webhooks/stripe).
|
|
21
21
|
*/
|
|
22
22
|
const WEBHOOK_PATH_PATTERNS = [
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
/webhook/i,
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Framework-managed routes where rate limiting is handled by the framework
|
|
27
|
+
* or is inappropriate (auth protocol flows, external callbacks, OG images).
|
|
28
|
+
*/
|
|
29
|
+
const FRAMEWORK_MANAGED_PATTERNS = [
|
|
30
|
+
/\/auth\/\[\.{3}[^\]]*\]/, // NextAuth catch-all: auth/[...nextauth], auth/[...params]
|
|
31
|
+
/\/callback\//, // Inbound callbacks from external services (OAuth, Stripe, Slack)
|
|
32
|
+
/\/callback$/, // Terminal callback path
|
|
33
|
+
/\/oauth\//, // OAuth protocol endpoints (token, userinfo, authorize)
|
|
34
|
+
/\/saml\//, // SAML SSO endpoints
|
|
35
|
+
/\/og\//, // OG image generation routes (stateless, CDN-cached)
|
|
36
|
+
/\/og$/, // Terminal OG path
|
|
25
37
|
];
|
|
26
38
|
export function run(index, config) {
|
|
27
39
|
const findings = [];
|
|
28
|
-
const
|
|
40
|
+
const maxSeverity = config.rules[RULE_ID]?.severity ?? "critical";
|
|
29
41
|
for (const route of index.routes.all) {
|
|
30
42
|
// Only check API routes
|
|
31
43
|
if (!route.isApi)
|
|
@@ -38,47 +50,63 @@ export function run(index, config) {
|
|
|
38
50
|
// Skip tRPC proxy routes — rate limiting is checked at the procedure level
|
|
39
51
|
if (index.trpc.detected && route.file === index.trpc.proxyFile)
|
|
40
52
|
continue;
|
|
53
|
+
// Skip framework-managed routes (NextAuth, OAuth, SAML, callbacks, OG images)
|
|
54
|
+
if (isFrameworkManaged(route.pathname))
|
|
55
|
+
continue;
|
|
41
56
|
const result = checkRoute(route, index, config);
|
|
42
57
|
if (result) {
|
|
58
|
+
const isAuthed = route.protection?.auth.satisfied ?? false;
|
|
43
59
|
findings.push({
|
|
44
60
|
ruleId: RULE_ID,
|
|
45
|
-
severity,
|
|
61
|
+
severity: capSeverity(result.severity, maxSeverity),
|
|
46
62
|
confidence: result.confidence,
|
|
47
|
-
message:
|
|
63
|
+
message: isAuthed
|
|
64
|
+
? `Authenticated API route has no recognized rate limiting`
|
|
65
|
+
: `Public API route has no recognized rate limiting`,
|
|
48
66
|
file: route.file,
|
|
49
67
|
line: result.line,
|
|
50
68
|
snippet: result.snippet,
|
|
51
69
|
evidence: result.evidence,
|
|
52
70
|
confidenceRationale: result.confidenceRationale,
|
|
53
|
-
remediation:
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
remediation: isAuthed
|
|
72
|
+
? [
|
|
73
|
+
"Consider adding rate limiting as defense-in-depth",
|
|
74
|
+
"Authenticated routes are lower risk but can still be abused with stolen credentials",
|
|
75
|
+
"If rate limiting is at the edge (Cloudflare, Vercel WAF), add a waiver",
|
|
76
|
+
]
|
|
77
|
+
: [
|
|
78
|
+
"Add rate limiting middleware or wrapper to this route",
|
|
79
|
+
"If using @upstash/ratelimit, wrap the handler with a rate limit check",
|
|
80
|
+
"If rate limiting is handled at the edge (Cloudflare, Vercel), add a waiver with reason",
|
|
81
|
+
"Add custom wrapper names to hints.rateLimit.wrappers in config",
|
|
82
|
+
],
|
|
59
83
|
tags: ["rate-limit", "server"],
|
|
60
84
|
});
|
|
61
85
|
}
|
|
62
86
|
}
|
|
63
|
-
// Check tRPC public
|
|
87
|
+
// Check tRPC mutation procedures (both public and protected)
|
|
64
88
|
for (const proc of index.trpc.mutationProcedures) {
|
|
65
|
-
if (proc.procedureType === "protected")
|
|
66
|
-
continue;
|
|
67
89
|
if (isAllowlisted(proc.file, config.hints.rateLimit.allowlistPaths))
|
|
68
90
|
continue;
|
|
69
91
|
// Check if the procedure's file has rate limit calls
|
|
70
92
|
const src = readSource(index.rootDir, proc.file);
|
|
71
93
|
if (src && hasRateLimitCall(src, config.hints.rateLimit.wrappers))
|
|
72
94
|
continue;
|
|
95
|
+
const isProtected = proc.procedureType === "protected";
|
|
73
96
|
findings.push({
|
|
74
97
|
ruleId: RULE_ID,
|
|
75
|
-
severity,
|
|
98
|
+
severity: capSeverity(isProtected ? "high" : "med", maxSeverity),
|
|
76
99
|
confidence: "med",
|
|
77
|
-
message: `tRPC
|
|
100
|
+
message: `tRPC ${proc.procedureType} mutation "${proc.name}" has no recognized rate limiting`,
|
|
78
101
|
file: proc.file,
|
|
79
102
|
line: proc.line,
|
|
80
|
-
evidence: [
|
|
81
|
-
|
|
103
|
+
evidence: [
|
|
104
|
+
`${proc.procedureType}Procedure.mutation() without rate limit wrapper`,
|
|
105
|
+
...(isProtected ? ["authenticated but still susceptible to abuse (cost, spam)"] : []),
|
|
106
|
+
],
|
|
107
|
+
confidenceRationale: isProtected
|
|
108
|
+
? "Medium: authenticated mutation still needs rate limiting to prevent abuse"
|
|
109
|
+
: "Medium: tRPC rate limiting may be handled at middleware or procedure level (not detected)",
|
|
82
110
|
remediation: [
|
|
83
111
|
"Add rate limiting middleware to the procedure chain",
|
|
84
112
|
"If rate limiting is handled at the tRPC middleware level, add a waiver with reason",
|
|
@@ -89,19 +117,24 @@ export function run(index, config) {
|
|
|
89
117
|
}
|
|
90
118
|
return findings;
|
|
91
119
|
}
|
|
120
|
+
const SEVERITY_RANK = { critical: 4, high: 3, med: 2, low: 1 };
|
|
121
|
+
function capSeverity(computed, max) {
|
|
122
|
+
const maxRank = SEVERITY_RANK[max] ?? 4;
|
|
123
|
+
const computedRank = SEVERITY_RANK[computed] ?? 2;
|
|
124
|
+
return computedRank > maxRank ? max : computed;
|
|
125
|
+
}
|
|
92
126
|
function checkRoute(route, index, config) {
|
|
127
|
+
// Use ProtectionSummary if available (computed during index building)
|
|
128
|
+
if (route.protection) {
|
|
129
|
+
if (route.protection.rateLimit.satisfied)
|
|
130
|
+
return null;
|
|
131
|
+
// If wrapper introspection deferred this to WRAPPER-UNRECOGNIZED, don't emit per-route finding
|
|
132
|
+
if (route.protection.rateLimit.unverifiedWrappers.length > 0)
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
93
135
|
const src = readSource(index.rootDir, route.file);
|
|
94
136
|
if (!src)
|
|
95
137
|
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
138
|
// Routes with webhook signature verification don't need rate limiting
|
|
106
139
|
if (hasWebhookSignatureAuth(src))
|
|
107
140
|
return null;
|
|
@@ -110,37 +143,60 @@ function checkRoute(route, index, config) {
|
|
|
110
143
|
// Routes with cron key auth are server-to-server (no rate limiting needed)
|
|
111
144
|
if (hasCronKeyAuth(src))
|
|
112
145
|
return null;
|
|
113
|
-
//
|
|
146
|
+
// Determine auth status for severity modulation
|
|
147
|
+
const isAuthed = route.protection?.auth.satisfied ?? false;
|
|
114
148
|
const evidence = [];
|
|
115
|
-
|
|
116
|
-
evidence.push("No middleware-level rate limiting detected");
|
|
149
|
+
let severity;
|
|
117
150
|
let confidence;
|
|
118
151
|
let confidenceRationale;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
152
|
+
const isMutation = route.signals.hasMutationEvidence || route.signals.hasDbWriteEvidence;
|
|
153
|
+
if (isMutation) {
|
|
154
|
+
if (isAuthed) {
|
|
155
|
+
severity = "med";
|
|
156
|
+
confidence = "med";
|
|
157
|
+
confidenceRationale = "Medium: authenticated mutation route — abuse requires stolen credentials";
|
|
158
|
+
evidence.push("route performs mutations");
|
|
159
|
+
evidence.push(...route.signals.mutationDetails);
|
|
160
|
+
evidence.push("route has auth boundary — rate limiting is secondary defense");
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
severity = "critical";
|
|
164
|
+
confidence = "high";
|
|
165
|
+
confidenceRationale = "High: mutation route without rate limiting (higher abuse risk)";
|
|
166
|
+
evidence.push("route performs mutations (higher abuse risk)");
|
|
167
|
+
evidence.push(...route.signals.mutationDetails);
|
|
168
|
+
}
|
|
124
169
|
}
|
|
125
170
|
else if (hasBodyParsing(src)) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
171
|
+
if (isAuthed) {
|
|
172
|
+
severity = "low";
|
|
173
|
+
confidence = "low";
|
|
174
|
+
confidenceRationale = "Low: authenticated route with body parsing — abuse requires stolen credentials";
|
|
175
|
+
evidence.push("route reads request body");
|
|
176
|
+
evidence.push("route has auth boundary — rate limiting is secondary defense");
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
severity = "high";
|
|
180
|
+
confidence = "high";
|
|
181
|
+
confidenceRationale = "High: route reads request body without rate limiting";
|
|
182
|
+
evidence.push("route reads request body");
|
|
183
|
+
}
|
|
129
184
|
}
|
|
130
185
|
else {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
186
|
+
if (isAuthed) {
|
|
187
|
+
severity = "low";
|
|
188
|
+
confidence = "low";
|
|
189
|
+
confidenceRationale = "Low: authenticated GET-only route — rate limiting is good hygiene but low risk";
|
|
190
|
+
evidence.push("route has auth boundary — rate limiting is secondary defense");
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
severity = "med";
|
|
138
194
|
confidence = "med";
|
|
139
|
-
confidenceRationale = "Medium:
|
|
195
|
+
confidenceRationale = "Medium: public API route without rate limiting (GET-only, lower risk)";
|
|
196
|
+
evidence.push("public API route without rate limiting");
|
|
140
197
|
}
|
|
141
|
-
evidence.push("handler exported via HOF wrapper (may contain rate limiting)");
|
|
142
198
|
}
|
|
143
|
-
return { confidence, confidenceRationale, evidence };
|
|
199
|
+
return { severity, confidence, confidenceRationale, evidence };
|
|
144
200
|
}
|
|
145
201
|
function hasRateLimitCall(src, wrappers) {
|
|
146
202
|
for (const wrapper of wrappers) {
|
|
@@ -159,28 +215,6 @@ function hasRateLimitCall(src, wrappers) {
|
|
|
159
215
|
return true;
|
|
160
216
|
return false;
|
|
161
217
|
}
|
|
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
218
|
/**
|
|
185
219
|
* Webhook signature verification — these routes don't need rate limiting.
|
|
186
220
|
*/
|
|
@@ -208,6 +242,11 @@ function isWebhookPath(pathname) {
|
|
|
208
242
|
return false;
|
|
209
243
|
return WEBHOOK_PATH_PATTERNS.some((p) => p.test(pathname));
|
|
210
244
|
}
|
|
245
|
+
function isFrameworkManaged(pathname) {
|
|
246
|
+
if (!pathname)
|
|
247
|
+
return false;
|
|
248
|
+
return FRAMEWORK_MANAGED_PATTERNS.some((p) => p.test(pathname));
|
|
249
|
+
}
|
|
211
250
|
function hasBodyParsing(src) {
|
|
212
251
|
return /request\.json\s*\(|request\.formData\s*\(|req\.body/.test(src);
|
|
213
252
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit-missing.js","sourceRoot":"","sources":["../../src/rules/rate-limit-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,oBAAoB,CAAC;AAE5C;;;GAGG;AACH,MAAM,oBAAoB,GAAG;IAC3B,WAAW;IACX,SAAS;IACT,UAAU;IACV,SAAS;IACT,WAAW;IACX,UAAU,EAAK,mCAAmC;IAClD,WAAW,EAAI,uCAAuC;CACvD,CAAC;AAEF;;;GAGG;AACH,MAAM,qBAAqB,GAAG;IAC5B,
|
|
1
|
+
{"version":3,"file":"rate-limit-missing.js","sourceRoot":"","sources":["../../src/rules/rate-limit-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,oBAAoB,CAAC;AAE5C;;;GAGG;AACH,MAAM,oBAAoB,GAAG;IAC3B,WAAW;IACX,SAAS;IACT,UAAU;IACV,SAAS;IACT,WAAW;IACX,UAAU,EAAK,mCAAmC;IAClD,WAAW,EAAI,uCAAuC;CACvD,CAAC;AAEF;;;GAGG;AACH,MAAM,qBAAqB,GAAG;IAC5B,UAAU;CACX,CAAC;AAEF;;;GAGG;AACH,MAAM,0BAA0B,GAAG;IACjC,yBAAyB,EAAG,2DAA2D;IACvF,cAAc,EAAe,kEAAkE;IAC/F,aAAa,EAAgB,yBAAyB;IACtD,WAAW,EAAkB,wDAAwD;IACrF,UAAU,EAAmB,qBAAqB;IAClD,QAAQ,EAAqB,qDAAqD;IAClF,OAAO,EAAsB,mBAAmB;CACjD,CAAC;AAEF,MAAM,UAAU,GAAG,CAAC,KAAgB,EAAE,MAAuB;IAC3D,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,IAAI,UAAU,CAAC;IAElE,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QACrC,wBAAwB;QACxB,IAAI,CAAC,KAAK,CAAC,KAAK;YAAE,SAAS;QAE3B,+CAA+C;QAC/C,IAAI,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,SAAS;QAC3C,IAAI,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC;YAAE,SAAS;QAE/E,2EAA2E;QAC3E,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,SAAS;YAAE,SAAS;QAEzE,8EAA8E;QAC9E,IAAI,kBAAkB,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,SAAS;QAEjD,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAChD,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;YAE3D,QAAQ,CAAC,IAAI,CAAC;gBACZ,MAAM,EAAE,OAAO;gBACf,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC;gBACnD,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,OAAO,EAAE,QAAQ;oBACf,CAAC,CAAC,yDAAyD;oBAC3D,CAAC,CAAC,kDAAkD;gBACtD,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,mBAAmB,EAAE,MAAM,CAAC,mBAAmB;gBAC/C,WAAW,EAAE,QAAQ;oBACnB,CAAC,CAAC;wBACE,mDAAmD;wBACnD,qFAAqF;wBACrF,wEAAwE;qBACzE;oBACH,CAAC,CAAC;wBACE,uDAAuD;wBACvD,uEAAuE;wBACvE,wFAAwF;wBACxF,gEAAgE;qBACjE;gBACL,IAAI,EAAE,CAAC,YAAY,EAAE,QAAQ,CAAC;aAC/B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;QACjD,IAAI,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC;YAAE,SAAS;QAE9E,qDAAqD;QACrD,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,GAAG,IAAI,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC;YAAE,SAAS;QAE5E,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,KAAK,WAAW,CAAC;QACvD,QAAQ,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,OAAO;YACf,QAAQ,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,WAAW,CAAC;YAChE,UAAU,EAAE,KAAK;YACjB,OAAO,EAAE,QAAQ,IAAI,CAAC,aAAa,cAAc,IAAI,CAAC,IAAI,mCAAmC;YAC7F,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE;gBACR,GAAG,IAAI,CAAC,aAAa,iDAAiD;gBACtE,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,2DAA2D,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;aACtF;YACD,mBAAmB,EAAE,WAAW;gBAC9B,CAAC,CAAC,2EAA2E;gBAC7E,CAAC,CAAC,2FAA2F;YAC/F,WAAW,EAAE;gBACX,qDAAqD;gBACrD,oFAAoF;gBACpF,oEAAoE;aACrE;YACD,IAAI,EAAE,CAAC,YAAY,EAAE,MAAM,CAAC;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,aAAa,GAA2B,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AAEvF,SAAS,WAAW,CAAC,QAAkB,EAAE,GAAW;IAClD,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,YAAY,GAAG,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,YAAY,GAAG,OAAO,CAAC,CAAC,CAAE,GAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC/D,CAAC;AAWD,SAAS,UAAU,CACjB,KAAgB,EAChB,KAAgB,EAChB,MAAuB;IAEvB,sEAAsE;IACtE,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,SAAS;YAAE,OAAO,IAAI,CAAC;QAEtD,+FAA+F;QAC/F,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IAC5E,CAAC;IAED,MAAM,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAEtB,sEAAsE;IACtE,IAAI,uBAAuB,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9C,IAAI,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/C,2EAA2E;IAC3E,IAAI,cAAc,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,gDAAgD;IAChD,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC;IAE3D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,QAAkB,CAAC;IACvB,IAAI,UAAsB,CAAC;IAC3B,IAAI,mBAA2B,CAAC;IAEhC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,mBAAmB,IAAI,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC;IAEzF,IAAI,UAAU,EAAE,CAAC;QACf,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,GAAG,KAAK,CAAC;YACjB,UAAU,GAAG,KAAK,CAAC;YACnB,mBAAmB,GAAG,0EAA0E,CAAC;YACjG,QAAQ,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;YAC1C,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;YAChD,QAAQ,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;QAChF,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,UAAU,CAAC;YACtB,UAAU,GAAG,MAAM,CAAC;YACpB,mBAAmB,GAAG,gEAAgE,CAAC;YACvF,QAAQ,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;YAC9D,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;SAAM,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,GAAG,KAAK,CAAC;YACjB,UAAU,GAAG,KAAK,CAAC;YACnB,mBAAmB,GAAG,gFAAgF,CAAC;YACvG,QAAQ,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;YAC1C,QAAQ,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;QAChF,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,MAAM,CAAC;YAClB,UAAU,GAAG,MAAM,CAAC;YACpB,mBAAmB,GAAG,sDAAsD,CAAC;YAC7E,QAAQ,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;SAAM,CAAC;QACN,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,GAAG,KAAK,CAAC;YACjB,UAAU,GAAG,KAAK,CAAC;YACnB,mBAAmB,GAAG,gFAAgF,CAAC;YACvG,QAAQ,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;QAChF,CAAC;aAAM,CAAC;YACN,QAAQ,GAAG,KAAK,CAAC;YACjB,UAAU,GAAG,KAAK,CAAC;YACnB,mBAAmB,GAAG,uEAAuE,CAAC;YAC9F,QAAQ,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,mBAAmB,EAAE,QAAQ,EAAE,CAAC;AACjE,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAW,EAAE,QAAkB;IACvD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,MAAM,WAAW,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;QACtE,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IACrC,CAAC;IAED,mDAAmD;IACnD,IAAI,qBAAqB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACjD,IAAI,uBAAuB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/C,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,GAAW;IAC1C,IAAI,wCAAwC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpE,IAAI,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACzD,IAAI,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACvE,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,GAAW;IACjC,IAAI,wCAAwC,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACpE,IAAI,6BAA6B,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACzD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,QAAiB;IACtC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAiB;IAC3C,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,OAAO,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,cAAc,CAAC,GAAW;IACjC,OAAO,qDAAqD,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzE,CAAC;AAED,SAAS,YAAY,CAAC,QAAiB;IACrC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,UAAU,CAAC,OAAe,EAAE,IAAY;IAC/C,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,GAAW;IAC9B,OAAO,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit-missing.test.d.ts","sourceRoot":"","sources":["../../src/rules/rate-limit-missing.test.ts"],"names":[],"mappings":""}
|