Skip to content

Commit ee25f97

Browse files
authored
Merge pull request github#12956 from asgerf/js/express-array-routes
JS: Properly recognise Express middlewares in an array
2 parents 5f4d089 + 8a9308c commit ee25f97

File tree

7 files changed

+112
-28
lines changed

7 files changed

+112
-28
lines changed

javascript/ql/lib/semmle/javascript/frameworks/Express.qll

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -164,9 +164,9 @@ module Express {
164164
*/
165165
DataFlow::Node getRouteHandlerNode(int index) {
166166
// The first argument is a URI pattern if it is a string. If it could possibly be
167-
// a function, we consider it to be a route handler, otherwise a URI pattern.
167+
// a non-string value, we consider it to be a route handler, otherwise a URI pattern.
168168
exists(AnalyzedNode firstArg | firstArg = this.getArgument(0).analyze() |
169-
if firstArg.getAType() = TTFunction()
169+
if firstArg.getAType() != TTString()
170170
then result = this.getArgument(index)
171171
else (
172172
index >= 0 and result = this.getArgument(index + 1)
@@ -215,6 +215,10 @@ module Express {
215215
or
216216
Http::routeHandlerStep(result, succ) and
217217
t = t2
218+
or
219+
DataFlow::SharedFlowStep::storeStep(result.getALocalUse(), succ,
220+
DataFlow::PseudoProperties::arrayElement()) and
221+
t = t2.continue()
218222
)
219223
}
220224

@@ -510,21 +514,6 @@ module Express {
510514
}
511515
}
512516

513-
/**
514-
* Holds if `call` is a chainable method call on the response object of `handler`.
515-
*/
516-
private predicate isChainableResponseMethodCall(
517-
RouteHandler handler, DataFlow::MethodCallNode call
518-
) {
519-
exists(string name | call.calls(handler.getAResponseNode(), name) |
520-
name =
521-
[
522-
"append", "attachment", "location", "send", "sendStatus", "set", "status", "type", "vary",
523-
"clearCookie", "contentType", "cookie", "format", "header", "json", "jsonp", "links"
524-
]
525-
)
526-
}
527-
528517
/** An Express response source. */
529518
abstract class ResponseSource extends Http::Servers::ResponseSource { }
530519

@@ -535,11 +524,7 @@ module Express {
535524
private class ExplicitResponseSource extends ResponseSource {
536525
RouteHandler rh;
537526

538-
ExplicitResponseSource() {
539-
this = rh.getResponseParameter()
540-
or
541-
isChainableResponseMethodCall(rh, this)
542-
}
527+
ExplicitResponseSource() { this = rh.getResponseParameter() }
543528

544529
/**
545530
* Gets the route handler that provides this response.
@@ -556,6 +541,22 @@ module Express {
556541
override RouteHandler getRouteHandler() { none() } // Not known.
557542
}
558543

544+
private class ChainedResponse extends ResponseSource {
545+
private ResponseSource base;
546+
547+
ChainedResponse() {
548+
this =
549+
base.ref()
550+
.getAMethodCall([
551+
"append", "attachment", "location", "send", "sendStatus", "set", "status", "type",
552+
"vary", "clearCookie", "contentType", "cookie", "format", "header", "json", "jsonp",
553+
"links"
554+
])
555+
}
556+
557+
override Http::RouteHandler getRouteHandler() { result = base.getRouteHandler() }
558+
}
559+
559560
/** An Express request source. */
560561
abstract class RequestSource extends Http::Servers::RequestSource { }
561562

@@ -777,12 +778,12 @@ module Express {
777778
/**
778779
* Holds if `e` is an HTTP request object.
779780
*/
780-
predicate isRequest(DataFlow::Node e) { any(RouteHandler rh).getARequestNode() = e }
781+
predicate isRequest(DataFlow::Node e) { any(RequestSource src).ref().flowsTo(e) }
781782

782783
/**
783784
* Holds if `e` is an HTTP response object.
784785
*/
785-
predicate isResponse(DataFlow::Node e) { any(RouteHandler rh).getAResponseNode() = e }
786+
predicate isResponse(DataFlow::Node e) { any(ResponseSource src).ref().flowsTo(e) }
786787

787788
/**
788789
* An access to the HTTP request body.
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
var express = require('express');
22
var app = express();
33

4-
app.get('/some/path', function(req, res) {
4+
app.get('/some/path', function(req, res) {
55
res.header(req.param("header"), req.param("val"));
66
res.send("val");
77
});
@@ -10,3 +10,12 @@ function getHandler() {
1010
return function (req, res){}
1111
}
1212
app.use(getHandler());
13+
14+
function getHandler2() {
15+
return function (req, res){}
16+
}
17+
app.use([getHandler2()]);
18+
19+
function handler3(req, res) {}
20+
let array = [handler3];
21+
app.use(array);

0 commit comments

Comments
 (0)