Skip to content

Commit 3970ead

Browse files
author
Max Schaefer
committed
JavaScript: Add support for rate-limiter-flexible package.
1 parent e7d8fa4 commit 3970ead

File tree

3 files changed

+50
-0
lines changed

3 files changed

+50
-0
lines changed

change-notes/1.23/analysis-javascript.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
- [firebase](https://www.npmjs.com/package/firebase)
77
- [mongodb](https://www.npmjs.com/package/mongodb)
88
- [mongoose](https://www.npmjs.com/package/mongoose)
9+
- [rate-limiter-flexible](https://www.npmjs.com/package/rate-limiter-flexible)
910

1011
* The call graph has been improved to resolve method calls in more cases. This may produce more security alerts.
1112

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

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,44 @@ class RouteHandlerLimitedByExpressLimiter extends RateLimitedRouteHandlerExpr {
155155
)
156156
}
157157
}
158+
159+
/**
160+
* A rate-handler function implemented using one of the rate-limiting classes provided
161+
* by the `rate-limiter-flexible` package.
162+
*
163+
* We look for functions that invoke the `consume` method of one of the `RateLimiter*`
164+
* classes from the `rate-limiter-flexible` package on a property of their first argument,
165+
* like the `rateLimiterMiddleware` function in this example:
166+
*
167+
* ```
168+
* import { RateLimiterRedis } from 'rate-limiter-flexible';
169+
* const rateLimiter = new RateLimiterRedis(...);
170+
* function rateLimiterMiddleware(req, res, next) {
171+
* rateLimiter.consume(req.ip).then(next).catch(res.status(429).send('rate limited'));
172+
* }
173+
* ```
174+
*/
175+
class RateLimiterFlexibleRateLimiter extends DataFlow::FunctionNode {
176+
RateLimiterFlexibleRateLimiter() {
177+
exists(
178+
string rateLimiterClassName, DataFlow::SourceNode rateLimiterClass,
179+
DataFlow::SourceNode rateLimiterInstance
180+
|
181+
rateLimiterClassName.matches("RateLimiter%") and
182+
rateLimiterClass = DataFlow::moduleMember("rate-limiter-flexible", rateLimiterClassName) and
183+
rateLimiterInstance = rateLimiterClass.getAnInstantiation() and
184+
getParameter(0).getAPropertyRead() = rateLimiterInstance
185+
.getAMemberCall("consume")
186+
.getAnArgument()
187+
)
188+
}
189+
}
190+
191+
/**
192+
* A route-handler expression that is rate-limited by the `rate-limiter-flexible` package.
193+
*/
194+
class RouteHandlerLimitedByRateLimiterFlexible extends RateLimiter {
195+
RouteHandlerLimitedByRateLimiterFlexible() {
196+
any(RateLimiterFlexibleRateLimiter rl).flowsToExpr(this)
197+
}
198+
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,11 @@ app3.get('/:path', expensiveHandler1); // OK
6363

6464
express().get('/:path', function(req, res) { verifyUser(req); }); // NOT OK
6565
express().get('/:path', RateLimit(), function(req, res) { verifyUser(req); }); // OK
66+
67+
// rate limiting using rate-limiter-flexible
68+
const { RateLimiterRedis } = require('rate-limiter-flexible');
69+
const rateLimiter = new RateLimiterRedis();
70+
const rateLimiterMiddleware = (req, res, next) => {
71+
rateLimiter.consume(req.ip).then(next).catch(res.status(429).send('rate limited'));
72+
};
73+
express().get('/:path', rateLimiterMiddleware, expensiveHandler1);

0 commit comments

Comments
 (0)