Skip to content

Commit f8d428c

Browse files
committed
JS: Use function-forwarding steps when tracking rate limiters
1 parent 581f4ed commit f8d428c

File tree

2 files changed

+32
-12
lines changed

2 files changed

+32
-12
lines changed

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

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,29 +113,53 @@ class DatabaseAccessAsExpensiveAction extends ExpensiveAction {
113113
* A route handler expression that is rate-limited by a rate-limiting middleware.
114114
*/
115115
class RouteHandlerExpressionWithRateLimiter extends RateLimitedRouteHandlerExpr {
116-
RouteHandlerExpressionWithRateLimiter() { getAMatchingAncestor() instanceof RateLimiter }
116+
RouteHandlerExpressionWithRateLimiter() {
117+
any(RateLimitingMiddleware m).ref().flowsToExpr(getAMatchingAncestor())
118+
}
117119
}
118120

119121
/**
122+
* DEPRECATED. Use `RateLimitingMiddleware` instead.
123+
*
120124
* A middleware that acts as a rate limiter.
121125
*/
122-
abstract class RateLimiter extends Express::RouteHandlerExpr { }
126+
deprecated class RateLimiter extends Express::RouteHandlerExpr {
127+
RateLimiter() { any(RateLimitingMiddleware m).ref().flowsToExpr(this) }
128+
}
129+
130+
/**
131+
* Creation of a middleware function that acts as a rate limiter.
132+
*/
133+
abstract class RateLimitingMiddleware extends DataFlow::SourceNode {
134+
/** Gets a data flow node referring to this middleware. */
135+
private DataFlow::SourceNode ref(DataFlow::TypeTracker t) {
136+
t.start() and
137+
result = this
138+
or
139+
DataFlow::functionOneWayForwardingStep(ref(t.continue()).getALocalUse(), result)
140+
or
141+
exists(DataFlow::TypeTracker t2 | result = ref(t2).track(t2, t))
142+
}
143+
144+
/** Gets a data flow node referring to this middleware. */
145+
DataFlow::SourceNode ref() { result = ref(DataFlow::TypeTracker::end()) }
146+
}
123147

124148
/**
125149
* A rate limiter constructed using the `express-rate-limit` package.
126150
*/
127-
class ExpressRateLimit extends RateLimiter {
151+
class ExpressRateLimit extends RateLimitingMiddleware {
128152
ExpressRateLimit() {
129-
this = API::moduleImport("express-rate-limit").getReturn().getAUse().asExpr()
153+
this = API::moduleImport("express-rate-limit").getReturn().getAnImmediateUse()
130154
}
131155
}
132156

133157
/**
134158
* A rate limiter constructed using the `express-brute` package.
135159
*/
136-
class BruteForceRateLimit extends RateLimiter {
160+
class BruteForceRateLimit extends RateLimitingMiddleware {
137161
BruteForceRateLimit() {
138-
this = API::moduleImport("express-brute").getInstance().getMember("prevent").getAUse().asExpr()
162+
this = API::moduleImport("express-brute").getInstance().getMember("prevent").getAnImmediateUse()
139163
}
140164
}
141165

@@ -183,8 +207,6 @@ class RateLimiterFlexibleRateLimiter extends DataFlow::FunctionNode {
183207
/**
184208
* A route-handler expression that is rate-limited by the `rate-limiter-flexible` package.
185209
*/
186-
class RouteHandlerLimitedByRateLimiterFlexible extends RateLimiter {
187-
RouteHandlerLimitedByRateLimiterFlexible() {
188-
any(RateLimiterFlexibleRateLimiter rl).flowsToExpr(this)
189-
}
210+
class RouteHandlerLimitedByRateLimiterFlexible extends RateLimitingMiddleware {
211+
RouteHandlerLimitedByRateLimiterFlexible() { this instanceof RateLimiterFlexibleRateLimiter }
190212
}

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,3 @@
88
| tst.js:38:20:38:36 | expensiveHandler4 | This route handler performs $@, but is not rate-limited. | tst.js:17:40:17:83 | connect ... ution') | a database access |
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 |
11-
| tst.js:78:60:78:76 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
12-
| tst.js:79:60:79:88 | catchAs ... ndler1) | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |

0 commit comments

Comments
 (0)