Skip to content

Commit c087e94

Browse files
committed
add additional indirect route-handler steps
1 parent 02c1d68 commit c087e94

File tree

2 files changed

+53
-9
lines changed

2 files changed

+53
-9
lines changed

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

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,24 +73,51 @@ module Express {
7373
}
7474

7575
/**
76-
* Holds if there exists a step from `pred` to `succ` for a RouteHandler - beyond the usual steps defined by TypeTracking.
76+
* Holds if `call` decorates the function `pred`.
77+
* This means that `call` returns a function that forwards its arguments to `pred`.
7778
*/
78-
predicate routeHandlerStep(DataFlow::SourceNode pred, DataFlow::SourceNode succ) {
79+
predicate decoratedRouteHandler(DataFlow::SourceNode pred, DataFlow::CallNode call) {
7980
// indirect route-handler `result` is given to function `outer`, which returns function `inner` which calls the function `pred`.
80-
exists(int i, DataFlow::CallNode call, Function outer, Function inner | call = succ |
81+
exists(int i, Function outer, Function inner |
8182
pred = call.getArgument(i).getALocalSource() and
8283
outer = call.getACallee() and
8384
inner = outer.getAReturnedExpr() and
84-
exists(DataFlow::CallNode innerCall |
85-
innerCall = DataFlow::parameterNode(outer.getParameter(i)).getACall() and
86-
forall(int arg | arg = [0, 1] |
87-
DataFlow::parameterNode(inner.getParameter(arg)).flowsTo(innerCall.getArgument(arg))
88-
)
89-
)
85+
forwardingCall(DataFlow::parameterNode(outer.getParameter(i)), inner.flow())
86+
)
87+
}
88+
89+
/**
90+
* Holds if a call to `callee` inside `f` forwards all of the parameters from `f` to that call.
91+
*/
92+
private predicate forwardingCall(DataFlow::SourceNode callee, DataFlow::FunctionNode f) {
93+
exists(DataFlow::CallNode call | call = callee.getACall() |
94+
f.getNumParameter() >= 2 and
95+
forall(int arg | arg = [0 .. f.getNumParameter() - 1] |
96+
f.getParameter(arg).flowsTo(call.getArgument(arg))
97+
) and
98+
call.getContainer() = f.getFunction()
9099
)
100+
}
101+
102+
/**
103+
* Holds if there exists a step from `pred` to `succ` for a RouteHandler - beyond the usual steps defined by TypeTracking.
104+
*/
105+
predicate routeHandlerStep(DataFlow::SourceNode pred, DataFlow::SourceNode succ) {
106+
decoratedRouteHandler(pred, succ)
107+
or
108+
// A forwarding call
109+
forwardingCall(pred, succ)
91110
or
92111
// a container containing route-handlers.
93112
exists(HTTP::RouteHandlerCandidateContainer container | pred = container.getRouteHandler(succ))
113+
or
114+
// (function (req, res) {}).bind(this);
115+
exists(DataFlow::MethodCallNode call |
116+
call.getMethodName() = "bind" and call.getNumArgument() = 1
117+
|
118+
succ = call and
119+
pred = call.getReceiver().getALocalSource()
120+
)
94121
}
95122

96123
/**

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ test_RequestInputAccess
1818
| src/https.js:6:26:6:32 | req.url | url | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
1919
| src/https.js:8:3:8:20 | req.headers.cookie | cookie | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
2020
| src/https.js:9:3:9:17 | req.headers.foo | header | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
21+
| src/indirect.js:17:28:17:34 | req.url | url | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
2122
test_RouteHandler_getAResponseHeader
2223
| src/http.js:4:32:10:1 | functio ... .foo;\\n} | location | src/http.js:7:3:7:42 | res.wri ... rget }) |
2324
| src/http.js:12:19:16:1 | functio ... ar");\\n} | content-type | src/http.js:13:3:13:44 | res.set ... /html') |
@@ -55,6 +56,8 @@ test_ResponseExpr
5556
| src/https.js:13:3:13:5 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} |
5657
| src/https.js:14:3:14:5 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} |
5758
| src/https.js:15:3:15:5 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} |
59+
| src/indirect.js:16:26:16:28 | res | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
60+
| src/indirect.js:19:38:19:40 | res | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
5861
test_HeaderDefinition
5962
| src/http.js:7:3:7:42 | res.wri ... rget }) | src/http.js:4:32:10:1 | functio ... .foo;\\n} |
6063
| src/http.js:13:3:13:44 | res.set ... /html') | src/http.js:12:19:16:1 | functio ... ar");\\n} |
@@ -128,6 +131,8 @@ test_RouteHandler_getAResponseExpr
128131
| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:13:3:13:5 | res |
129132
| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:14:3:14:5 | res |
130133
| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:15:3:15:5 | res |
134+
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:26:16:28 | res |
135+
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:19:38:19:40 | res |
131136
test_ServerDefinition_getARouteHandler
132137
| createServer.js:2:1:2:42 | https.c ... es) {}) | createServer.js:2:20:2:41 | functio ... res) {} |
133138
| createServer.js:3:1:3:45 | https.c ... es) {}) | createServer.js:3:23:3:44 | functio ... res) {} |
@@ -140,6 +145,7 @@ test_ServerDefinition_getARouteHandler
140145
| src/http.js:70:1:70:36 | http.cr ... dler()) | src/http.js:68:12:68:27 | (req,res) => f() |
141146
| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
142147
| src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:20:16:1 | functio ... ar");\\n} |
148+
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
143149
test_ResponseSendArgument
144150
| src/http.js:14:13:14:17 | "foo" | src/http.js:12:19:16:1 | functio ... ar");\\n} |
145151
| src/http.js:15:11:15:15 | "bar" | src/http.js:12:19:16:1 | functio ... ar");\\n} |
@@ -163,7 +169,10 @@ test_RouteSetup_getARouteHandler
163169
| src/https.js:4:14:10:2 | https.c ... foo;\\n}) | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
164170
| src/https.js:12:1:16:2 | https.c ... r");\\n}) | src/https.js:12:20:16:1 | functio ... ar");\\n} |
165171
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:14:19:21:3 | return of method requestHandler |
172+
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
166173
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:16:12:20:16 | functio ... d(this) |
174+
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:17:21:17:35 | routes[req.url] |
175+
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:17:40:17:50 | routes['*'] |
167176
| src/indirect.js:34:14:34:58 | http.cr ... dler()) | src/indirect.js:34:32:34:57 | appServ ... ndler() |
168177
test_ClientRequest_getADataNode
169178
| src/http.js:27:16:27:73 | http.re ... POST'}) | src/http.js:50:16:50:22 | 'stuff' |
@@ -181,6 +190,7 @@ test_RemoteFlowSources
181190
| src/https.js:6:26:6:32 | req.url |
182191
| src/https.js:8:3:8:20 | req.headers.cookie |
183192
| src/https.js:9:3:9:17 | req.headers.foo |
193+
| src/indirect.js:17:28:17:34 | req.url |
184194
test_RouteHandler
185195
| createServer.js:2:20:2:41 | functio ... res) {} | createServer.js:2:1:2:42 | https.c ... es) {}) |
186196
| createServer.js:3:23:3:44 | functio ... res) {} | createServer.js:3:1:3:45 | https.c ... es) {}) |
@@ -193,6 +203,7 @@ test_RouteHandler
193203
| src/http.js:68:12:68:27 | (req,res) => f() | src/http.js:70:1:70:36 | http.cr ... dler()) |
194204
| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:14:10:2 | https.c ... foo;\\n}) |
195205
| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:1:16:2 | https.c ... r");\\n}) |
206+
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:34:14:34:58 | http.cr ... dler()) |
196207
test_RequestExpr
197208
| createServer.js:2:30:2:32 | req | createServer.js:2:20:2:41 | functio ... res) {} |
198209
| createServer.js:3:33:3:35 | req | createServer.js:3:23:3:44 | functio ... res) {} |
@@ -212,6 +223,9 @@ test_RequestExpr
212223
| src/https.js:8:3:8:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
213224
| src/https.js:9:3:9:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
214225
| src/https.js:12:29:12:31 | req | src/https.js:12:20:16:1 | functio ... ar");\\n} |
226+
| src/indirect.js:16:21:16:23 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
227+
| src/indirect.js:17:28:17:30 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
228+
| src/indirect.js:19:33:19:35 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } |
215229
test_SystemCommandExecution_getAnArgumentForCommand
216230
| exec.js:3:1:3:38 | cp.exec ... "], cb) | exec.js:3:21:3:33 | ["--version"] |
217231
| exec.js:4:1:4:47 | cp.exec ... sion"]) | exec.js:4:23:4:46 | ["-c", ... rsion"] |
@@ -240,3 +254,6 @@ test_RouteHandler_getARequestExpr
240254
| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:8:3:8:5 | req |
241255
| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:9:3:9:5 | req |
242256
| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:29:12:31 | req |
257+
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:21:16:23 | req |
258+
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:17:28:17:30 | req |
259+
| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:19:33:19:35 | req |

0 commit comments

Comments
 (0)