Skip to content

Commit 6f2e485

Browse files
authored
Merge pull request github#1950 from xiemaisi/js/rate-limiter-flexible
Approved by esben-semmle
2 parents dcd62e5 + 4e1e7bc commit 6f2e485

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
@@ -23,6 +23,7 @@
2323
*/
2424

2525
import javascript
26+
private import semmle.javascript.frameworks.ConnectExpressShared::ConnectExpressShared
2627

2728
// main concepts
2829
/**
@@ -155,3 +156,43 @@ class RouteHandlerLimitedByExpressLimiter extends RateLimitedRouteHandlerExpr {
155156
)
156157
}
157158
}
159+
160+
/**
161+
* A rate-handler function implemented using one of the rate-limiting classes provided
162+
* by the `rate-limiter-flexible` package.
163+
*
164+
* We look for route handlers that invoke the `consume` method of one of the `RateLimiter*`
165+
* classes from the `rate-limiter-flexible` package on a property of their request parameter,
166+
* like the `rateLimiterMiddleware` function in this example:
167+
*
168+
* ```
169+
* import { RateLimiterRedis } from 'rate-limiter-flexible';
170+
* const rateLimiter = new RateLimiterRedis(...);
171+
* function rateLimiterMiddleware(req, res, next) {
172+
* rateLimiter.consume(req.ip).then(next).catch(res.status(429).send('rate limited'));
173+
* }
174+
* ```
175+
*/
176+
class RateLimiterFlexibleRateLimiter extends DataFlow::FunctionNode {
177+
RateLimiterFlexibleRateLimiter() {
178+
exists(
179+
string rateLimiterClassName, DataFlow::SourceNode rateLimiterClass,
180+
DataFlow::SourceNode rateLimiterInstance, DataFlow::ParameterNode request
181+
|
182+
rateLimiterClassName.matches("RateLimiter%") and
183+
rateLimiterClass = DataFlow::moduleMember("rate-limiter-flexible", rateLimiterClassName) and
184+
rateLimiterInstance = rateLimiterClass.getAnInstantiation() and
185+
request.getParameter() = getRouteHandlerParameter(getFunction(), "request") and
186+
request.getAPropertyRead() = rateLimiterInstance.getAMemberCall("consume").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)