Skip to content

Commit 3eaa56e

Browse files
committed
support containers with decorated route handlers
1 parent c087e94 commit 3eaa56e

File tree

3 files changed

+82
-6
lines changed

3 files changed

+82
-6
lines changed

javascript/ql/src/semmle/javascript/frameworks/HTTP.qll

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -555,26 +555,51 @@ module HTTP {
555555
create.getArgument(0).asExpr() instanceof NullLiteral
556556
)
557557
) and
558-
exists(RouteHandlerCandidate candidate | candidate.flowsTo(getAPropertyWrite().getRhs()))
558+
exists(RouteHandlerCandidate candidate |
559+
getAPossiblyDecoratedHandler(candidate).flowsTo(getAPropertyWrite().getRhs())
560+
)
559561
}
560562

561-
override DataFlow::SourceNode getRouteHandler(DataFlow::SourceNode access) {
562-
result instanceof RouteHandlerCandidate and
563+
override RouteHandlerCandidate getRouteHandler(DataFlow::SourceNode access) {
563564
exists(DataFlow::PropWrite write, DataFlow::PropRead read |
564565
access = read and
565566
ref(this).getAPropertyRead() = read and
566-
result.flowsTo(write.getRhs()) and
567+
getAPossiblyDecoratedHandler(result).flowsTo(write.getRhs()) and
567568
write = this.getAPropertyWrite()
568569
|
569570
write.getPropertyName() = read.getPropertyName()
570571
or
571572
exists(EnumeratedPropName prop | access = prop.getASourceProp())
572573
or
573574
read = DataFlow::lvalueNode(any(ForOfStmt stmt).getLValue())
575+
or
576+
// for forwarding calls to an element where the key is determined by the request.
577+
getRequestParameterRead(read.getContainer().(Function).flow())
578+
.flowsToExpr(read.getPropertyNameExpr())
574579
)
575580
}
576581
}
577582

583+
/**
584+
* Gets a (chained) property-read/method-call on the request parameter of the route-handler `f`.
585+
*/
586+
private DataFlow::SourceNode getRequestParameterRead(RouteHandlerCandidate f) {
587+
result = f.getParameter(0)
588+
or
589+
result = getRequestParameterRead(f).getAPropertyRead()
590+
or
591+
result = getRequestParameterRead(f).getAMethodCall()
592+
}
593+
594+
/**
595+
* Gets a node that is either `candidate`, or a call that decorates `candidate`.
596+
*/
597+
DataFlow::SourceNode getAPossiblyDecoratedHandler(RouteHandlerCandidate candidate) {
598+
result = candidate
599+
or
600+
Express::decoratedRouteHandler(candidate, result)
601+
}
602+
578603
/**
579604
* A collection that contains one or more route potential handlers.
580605
*/
@@ -591,14 +616,14 @@ module HTTP {
591616
)
592617
}
593618

594-
override DataFlow::SourceNode getRouteHandler(DataFlow::SourceNode access) {
619+
override RouteHandlerCandidate getRouteHandler(DataFlow::SourceNode access) {
595620
exists(
596621
DataFlow::Node input, TypeTrackingPseudoProperty key, CollectionFlowStep store,
597622
CollectionFlowStep load, DataFlow::Node storeTo, DataFlow::Node loadFrom
598623
|
599624
this.flowsTo(storeTo) and
600625
store.store(input, storeTo, key) and
601-
result.(RouteHandlerCandidate).flowsTo(input) and
626+
getAPossiblyDecoratedHandler(result).flowsTo(input) and
602627
ref(this).flowsTo(loadFrom) and
603628
load.load(loadFrom, access, key)
604629
)
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
var http = require('http');
2+
3+
// These are exceptions where we override the routes
4+
var handlers = {
5+
someKey: myIndirectHandler
6+
};
7+
8+
9+
function get(req, res) { // route handler
10+
handlers[req.params.key.toLowerCase()](req, res);
11+
}
12+
13+
function myIndirectHandler(req, res) { // route handler
14+
res.setHeader('Content-Type', 'application/json');
15+
res.send("\"some result\"");
16+
}
17+
18+
var server = http.createServer(get);

javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ test_isCreateServer
1010
| src/http.js:70:1:70:36 | http.cr ... dler()) |
1111
| src/https.js:4:14:10:2 | https.c ... foo;\\n}) |
1212
| src/https.js:12:1:16:2 | https.c ... r");\\n}) |
13+
| src/indirect2.js:18:14:18:35 | http.cr ... er(get) |
1314
| src/indirect.js:34:14:34:58 | http.cr ... dler()) |
1415
test_RequestInputAccess
1516
| src/http.js:6:26:6:32 | req.url | url | src/http.js:4:32:10:1 | functio ... .foo;\\n} |
@@ -24,9 +25,11 @@ test_RouteHandler_getAResponseHeader
2425
| src/http.js:12:19:16:1 | functio ... ar");\\n} | content-type | src/http.js:13:3:13:44 | res.set ... /html') |
2526
| src/https.js:4:33:10:1 | functio ... .foo;\\n} | location | src/https.js:7:3:7:42 | res.wri ... rget }) |
2627
| src/https.js:12:20:16:1 | functio ... ar");\\n} | content-type | src/https.js:13:3:13:44 | res.set ... /html') |
28+
| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | content-type | src/indirect2.js:14:3:14:51 | res.set ... /json') |
2729
test_HeaderDefinition_defines
2830
| src/http.js:13:3:13:44 | res.set ... /html') | content-type | text/html |
2931
| src/https.js:13:3:13:44 | res.set ... /html') | content-type | text/html |
32+
| src/indirect2.js:14:3:14:51 | res.set ... /json') | content-type | application/json |
3033
test_SystemCommandExecution
3134
| es6-imported-exec.js:3:1:3:11 | exec("cmd") | es6-imported-exec.js:3:6:3:10 | "cmd" |
3235
| exec.js:3:1:3:38 | cp.exec ... "], cb) | exec.js:3:13:3:18 | "node" |
@@ -56,6 +59,11 @@ test_ResponseExpr
5659
| src/https.js:13:3:13:5 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} |
5760
| src/https.js:14:3:14:5 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} |
5861
| src/https.js:15:3:15:5 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} |
62+
| src/indirect2.js:9:19:9:21 | res | src/indirect2.js:9:1:11:1 | functio ... res);\\n} |
63+
| src/indirect2.js:10:47:10:49 | res | src/indirect2.js:9:1:11:1 | functio ... res);\\n} |
64+
| src/indirect2.js:13:33:13:35 | res | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} |
65+
| src/indirect2.js:14:3:14:5 | res | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} |
66+
| src/indirect2.js:15:3:15:5 | res | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} |
5967
| src/indirect.js:16:26:16:28 | res | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
6068
| src/indirect.js:19:38:19:40 | res | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
6169
test_HeaderDefinition
@@ -64,6 +72,7 @@ test_HeaderDefinition
6472
| src/http.js:63:3:63:40 | res.set ... , "23") | src/http.js:62:19:65:1 | functio ... r2");\\n} |
6573
| src/https.js:7:3:7:42 | res.wri ... rget }) | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
6674
| src/https.js:13:3:13:44 | res.set ... /html') | src/https.js:12:20:16:1 | functio ... ar");\\n} |
75+
| src/indirect2.js:14:3:14:51 | res.set ... /json') | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} |
6776
test_RouteSetup_getServer
6877
| createServer.js:2:1:2:42 | https.c ... es) {}) | createServer.js:2:1:2:42 | https.c ... es) {}) |
6978
| createServer.js:3:1:3:45 | https.c ... es) {}) | createServer.js:3:1:3:45 | https.c ... es) {}) |
@@ -76,6 +85,7 @@ test_RouteSetup_getServer
7685
| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:70:1:70:36 | http.cr ... dler()) |
7786
| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:14:10:2 | https.c ... foo;\\n}) |
7887
| src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:1:16:2 | https.c ... r");\\n}) |
88+
| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:18:14:18:35 | http.cr ... er(get) |
7989
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:34:14:34:58 | http.cr ... dler()) |
8090
test_ClientRequest
8191
| src/http.js:18:1:18:30 | http.re ... uth" }) |
@@ -87,6 +97,7 @@ test_HeaderDefinition_getAHeaderName
8797
| src/http.js:13:3:13:44 | res.set ... /html') | content-type |
8898
| src/https.js:7:3:7:42 | res.wri ... rget }) | location |
8999
| src/https.js:13:3:13:44 | res.set ... /html') | content-type |
100+
| src/indirect2.js:14:3:14:51 | res.set ... /json') | content-type |
90101
test_ServerDefinition
91102
| createServer.js:2:1:2:42 | https.c ... es) {}) |
92103
| createServer.js:3:1:3:45 | https.c ... es) {}) |
@@ -99,6 +110,7 @@ test_ServerDefinition
99110
| src/http.js:70:1:70:36 | http.cr ... dler()) |
100111
| src/https.js:4:14:10:2 | https.c ... foo;\\n}) |
101112
| src/https.js:12:1:16:2 | https.c ... r");\\n}) |
113+
| src/indirect2.js:18:14:18:35 | http.cr ... er(get) |
102114
| src/indirect.js:34:14:34:58 | http.cr ... dler()) |
103115
test_HeaderAccess
104116
| src/http.js:9:3:9:17 | req.headers.foo | foo |
@@ -109,6 +121,7 @@ test_HeaderDefinition_getNameExpr
109121
| src/http.js:63:3:63:40 | res.set ... , "23") | src/http.js:63:17:63:33 | req.query.myParam |
110122
| src/https.js:7:3:7:42 | res.wri ... rget }) | src/https.js:7:17:7:19 | 302 |
111123
| src/https.js:13:3:13:44 | res.set ... /html') | src/https.js:13:17:13:30 | 'Content-Type' |
124+
| src/indirect2.js:14:3:14:51 | res.set ... /json') | src/indirect2.js:14:17:14:30 | 'Content-Type' |
112125
test_RouteHandler_getAResponseExpr
113126
| createServer.js:2:20:2:41 | functio ... res) {} | createServer.js:2:35:2:37 | res |
114127
| createServer.js:3:23:3:44 | functio ... res) {} | createServer.js:3:38:3:40 | res |
@@ -131,6 +144,11 @@ test_RouteHandler_getAResponseExpr
131144
| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:13:3:13:5 | res |
132145
| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:14:3:14:5 | res |
133146
| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:15:3:15:5 | res |
147+
| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:9:19:9:21 | res |
148+
| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:10:47:10:49 | res |
149+
| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:13:33:13:35 | res |
150+
| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:14:3:14:5 | res |
151+
| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:15:3:15:5 | res |
134152
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:26:16:28 | res |
135153
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:19:38:19:40 | res |
136154
test_ServerDefinition_getARouteHandler
@@ -145,6 +163,8 @@ test_ServerDefinition_getARouteHandler
145163
| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:68:12:68:27 | (req,res) => f() |
146164
| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
147165
| src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:20:16:1 | functio ... ar");\\n} |
166+
| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:9:1:11:1 | functio ... res);\\n} |
167+
| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} |
148168
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
149169
test_ResponseSendArgument
150170
| src/http.js:14:13:14:17 | "foo" | src/http.js:12:19:16:1 | functio ... ar");\\n} |
@@ -168,6 +188,9 @@ test_RouteSetup_getARouteHandler
168188
| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:70:19:70:35 | getArrowHandler() |
169189
| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
170190
| src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:20:16:1 | functio ... ar");\\n} |
191+
| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:9:1:11:1 | functio ... res);\\n} |
192+
| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:10:3:10:40 | handler ... Case()] |
193+
| src/indirect2.js:18:14:18:35 | http.cr ... er(get) | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} |
171194
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:14:19:21:3 | return of method requestHandler |
172195
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
173196
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:16 | functio ... d(this) |
@@ -203,6 +226,8 @@ test_RouteHandler
203226
| src/http.js:68:12:68:27 | (req,res) => f() | src/http.js:70:1:70:36 | http.cr ... dler()) |
204227
| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:14:10:2 | https.c ... foo;\\n}) |
205228
| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:1:16:2 | https.c ... r");\\n}) |
229+
| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:18:14:18:35 | http.cr ... er(get) |
230+
| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:18:14:18:35 | http.cr ... er(get) |
206231
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:34:14:34:58 | http.cr ... dler()) |
207232
test_RequestExpr
208233
| createServer.js:2:30:2:32 | req | createServer.js:2:20:2:41 | functio ... res) {} |
@@ -223,6 +248,10 @@ test_RequestExpr
223248
| src/https.js:8:3:8:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
224249
| src/https.js:9:3:9:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
225250
| src/https.js:12:29:12:31 | req | src/https.js:12:20:16:1 | functio ... ar");\\n} |
251+
| src/indirect2.js:9:14:9:16 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} |
252+
| src/indirect2.js:10:12:10:14 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} |
253+
| src/indirect2.js:10:42:10:44 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} |
254+
| src/indirect2.js:13:28:13:30 | req | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} |
226255
| src/indirect.js:16:21:16:23 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
227256
| src/indirect.js:17:28:17:30 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
228257
| src/indirect.js:19:33:19:35 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
@@ -254,6 +283,10 @@ test_RouteHandler_getARequestExpr
254283
| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:8:3:8:5 | req |
255284
| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:9:3:9:5 | req |
256285
| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:29:12:31 | req |
286+
| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:9:14:9:16 | req |
287+
| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:10:12:10:14 | req |
288+
| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:10:42:10:44 | req |
289+
| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:13:28:13:30 | req |
257290
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:21:16:23 | req |
258291
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:17:28:17:30 | req |
259292
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:19:33:19:35 | req |

0 commit comments

Comments
 (0)