Skip to content

Commit f6fb613

Browse files
authored
Merge pull request #21700 from hvitved/js/fastify-per-route-rate-limiting
JS: Recognize Fastify per-route rate limiting
2 parents e0ce5bc + fcfb8c9 commit f6fb613

File tree

4 files changed

+46
-0
lines changed

4 files changed

+46
-0
lines changed

javascript/ql/lib/semmle/javascript/security/dataflow/MissingRateLimiting.qll

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,21 @@ class RouteHandlerLimitedByRateLimiterFlexible extends RateLimitingMiddleware in
191191
private class FastifyRateLimiter extends RateLimitingMiddleware {
192192
FastifyRateLimiter() { this = DataFlow::moduleImport("fastify-rate-limit") }
193193
}
194+
195+
/**
196+
* An options object with a `rateLimit` config passed to a Fastify shorthand route method,
197+
* such as `fastify.post('/path', { config: { rateLimit: { ... } } }, handler)`.
198+
*/
199+
private class FastifyPerRouteRateLimit extends RateLimitingMiddleware {
200+
FastifyPerRouteRateLimit() {
201+
exists(Fastify::RouteSetup setup |
202+
not setup.getMethodName() = ["route", "addHook"] and
203+
setup.getNumArgument() >= 3 and
204+
this.flowsTo(setup.getArgument(1))
205+
|
206+
exists(this.getAPropertySource("config").getAPropertySource("rateLimit"))
207+
or
208+
exists(this.getAPropertySource("rateLimit"))
209+
)
210+
}
211+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The query `js/missing-rate-limiting` now takes Fastify per-route
5+
rate limiting into account.

javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/MissingRateLimiting.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@
99
| tst.js:64:25:64:63 | functio ... req); } | This route handler performs $@, but is not rate-limited. | tst.js:64:46:64:60 | verifyUser(req) | authorization |
1010
| tst.js:76:25:76:53 | catchAs ... ndler1) | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
1111
| tst.js:88:24:88:40 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
12+
| tst.js:112:28:112:44 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |

javascript/ql/test/query-tests/Security/CWE-770/MissingRateLimit/tst.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,25 @@ const fastifyApp = require('fastify')();
8888
fastifyApp.get('/foo', expensiveHandler1); // $ Alert
8989
fastifyApp.register(require('fastify-rate-limit'));
9090
fastifyApp.get('/bar', expensiveHandler1);
91+
92+
// Fastify per-route rate limiting via config.rateLimit
93+
const fastifyApp2 = require('fastify')();
94+
fastifyApp2.register(require('@fastify/rate-limit'));
95+
96+
fastifyApp2.post('/login', {
97+
config: {
98+
rateLimit: {
99+
max: 3,
100+
timeWindow: '1 minute'
101+
}
102+
}
103+
}, expensiveHandler1); // OK - has per-route rateLimit config
104+
105+
fastifyApp2.post('/signup', {
106+
rateLimit: {
107+
max: 5,
108+
timeWindow: '1 minute'
109+
}
110+
}, expensiveHandler1); // OK - has per-route rateLimit directly in options
111+
112+
fastifyApp2.post('/other', expensiveHandler1); // $ Alert - no rate limiting

0 commit comments

Comments
 (0)