Skip to content

Commit 298dbe1

Browse files
author
Esben Sparre Andreasen
committed
JS: improve Koa model to account for aliases on the context object
1 parent 0e01988 commit 298dbe1

File tree

2 files changed

+64
-31
lines changed

2 files changed

+64
-31
lines changed

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

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module Koa {
2424

2525
HeaderDefinition() {
2626
// ctx.set('Cache-Control', 'no-cache');
27-
astNode.calls(rh.getAContextExpr(), "set")
27+
astNode.calls(rh.getAResponseOrContextExpr(), "set")
2828
or
2929
// ctx.response.header('Cache-Control', 'no-cache')
3030
astNode.calls(rh.getAResponseExpr(), "header")
@@ -58,6 +58,23 @@ module Koa {
5858
* route handler.
5959
*/
6060
Expr getAContextExpr() { result.(ContextExpr).getRouteHandler() = this }
61+
62+
/**
63+
* Gets an expression that contains the context or response
64+
* object of a route handler invocation.
65+
*/
66+
Expr getAResponseOrContextExpr() {
67+
result = getAResponseExpr() or result = getAContextExpr()
68+
}
69+
70+
/**
71+
* Gets an expression that contains the context or request
72+
* object of a route handler invocation.
73+
*/
74+
Expr getARequestOrContextExpr() {
75+
result = getARequestExpr() or result = getAContextExpr()
76+
}
77+
6178
}
6279

6380
/**
@@ -159,35 +176,39 @@ module Koa {
159176
string kind;
160177

161178
RequestInputAccess() {
162-
exists(Expr request | request = rh.getARequestExpr() |
163-
// `ctx.request.body`
164-
kind = "body" and
165-
this.asExpr().(PropAccess).accesses(request, "body")
166-
or
167-
kind = "parameter" and
168-
this = getAQueryParameterAccess(rh)
169-
or
179+
kind = "parameter" and
180+
this = getAQueryParameterAccess(rh)
181+
or
182+
exists(Expr e | rh.getARequestOrContextExpr() = e |
183+
// `ctx.request.url`, `ctx.request.originalUrl`, or `ctx.request.href`
170184
exists(string propName |
171-
// `ctx.request.url`, `ctx.request.originalUrl`, or `ctx.request.href`
172185
kind = "url" and
173-
this.asExpr().(PropAccess).accesses(request, propName)
174-
|
175-
propName = "url" or
176-
propName = "originalUrl" or
186+
this.asExpr().(PropAccess).accesses(e, propName)
187+
|
188+
propName = "url"
189+
or
190+
propName = "originalUrl"
191+
or
177192
propName = "href"
178193
)
179-
)
180-
or
181-
exists(PropAccess cookies |
194+
or
195+
// `ctx.request.body`
196+
e instanceof RequestExpr and
197+
kind = "body" and
198+
this.asExpr().(PropAccess).accesses(e, "body")
199+
or
182200
// `ctx.cookies.get(<name>)`
183-
kind = "cookie" and
184-
cookies.accesses(rh.getAContextExpr(), "cookies") and
185-
this.asExpr().(MethodCallExpr).calls(cookies, "get")
186-
)
187-
or
188-
exists(RequestHeaderAccess access | access = this |
189-
rh = access.getRouteHandler() and
190-
kind = "header"
201+
exists(PropAccess cookies |
202+
e instanceof ContextExpr and
203+
kind = "cookie" and
204+
cookies.accesses(e, "cookies") and
205+
this.asExpr().(MethodCallExpr).calls(cookies, "get")
206+
)
207+
or
208+
exists(RequestHeaderAccess access | access = this |
209+
rh = access.getRouteHandler() and
210+
kind = "header"
211+
)
191212
)
192213
}
193214

@@ -199,8 +220,8 @@ module Koa {
199220
}
200221

201222
private DataFlow::Node getAQueryParameterAccess(RouteHandler rh) {
202-
// `ctx.request.query.name`
203-
result.asExpr().(PropAccess).getBase().(PropAccess).accesses(rh.getARequestExpr(), "query")
223+
// `ctx.query.name` or `ctx.request.query.name`
224+
result.asExpr().(PropAccess).getBase().(PropAccess).accesses(rh.getARequestOrContextExpr(), "query")
204225
}
205226

206227
/**
@@ -210,18 +231,18 @@ module Koa {
210231
RouteHandler rh;
211232

212233
RequestHeaderAccess() {
213-
exists(Expr request | request = rh.getARequestExpr() |
234+
exists(Expr e | e = rh.getARequestOrContextExpr() |
214235
exists(string propName, PropAccess headers |
215236
// `ctx.request.header.<name>`, `ctx.request.headers.<name>`
216-
headers.accesses(request, propName) and
237+
headers.accesses(e, propName) and
217238
this.asExpr().(PropAccess).accesses(headers, _)
218239
|
219240
propName = "header" or
220241
propName = "headers"
221242
)
222243
or
223244
// `ctx.request.get(<name>)`
224-
this.asExpr().(MethodCallExpr).calls(request, "get")
245+
this.asExpr().(MethodCallExpr).calls(e, "get")
225246
)
226247
}
227248

@@ -264,7 +285,7 @@ module Koa {
264285

265286
ResponseSendArgument() {
266287
exists(DataFlow::PropWrite pwn |
267-
pwn.writes(DataFlow::valueNode(rh.getAResponseExpr()), "body", DataFlow::valueNode(this))
288+
pwn.writes(DataFlow::valueNode(rh.getAResponseOrContextExpr()), "body", DataFlow::valueNode(this))
268289
)
269290
}
270291

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ test_RequestInputAccess
1212
| src/koa.js:25:3:25:25 | ctx.req ... ers.bar | header | src/koa.js:10:10:28:1 | functio ... az');\\n} |
1313
| src/koa.js:26:3:26:24 | ctx.req ... ('bar') | header | src/koa.js:10:10:28:1 | functio ... az');\\n} |
1414
| src/koa.js:27:3:27:24 | ctx.coo ... ('baz') | cookie | src/koa.js:10:10:28:1 | functio ... az');\\n} |
15+
| src/koa.js:33:2:33:14 | ctx.query.foo | parameter | src/koa.js:30:10:45:1 | async c ... url);\\n} |
16+
| src/koa.js:34:2:34:8 | ctx.url | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
17+
| src/koa.js:35:2:35:16 | ctx.originalUrl | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
18+
| src/koa.js:36:2:36:9 | ctx.href | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
19+
| src/koa.js:37:2:37:15 | ctx.header.bar | header | src/koa.js:30:10:45:1 | async c ... url);\\n} |
20+
| src/koa.js:38:2:38:16 | ctx.headers.bar | header | src/koa.js:30:10:45:1 | async c ... url);\\n} |
21+
| src/koa.js:40:2:40:15 | ctx.get('bar') | header | src/koa.js:30:10:45:1 | async c ... url);\\n} |
22+
| src/koa.js:42:12:42:27 | ctx.query.target | parameter | src/koa.js:30:10:45:1 | async c ... url);\\n} |
1523
test_RouteHandler_getAResponseHeader
1624
| src/koa.js:10:10:28:1 | functio ... az');\\n} | header1 | src/koa.js:11:3:11:25 | this.se ... 1', '') |
1725
| src/koa.js:10:10:28:1 | functio ... az');\\n} | header2 | src/koa.js:12:3:12:37 | this.re ... 2', '') |
@@ -81,6 +89,9 @@ test_HeaderAccess
8189
| src/koa.js:24:3:24:24 | ctx.req ... der.bar | bar |
8290
| src/koa.js:25:3:25:25 | ctx.req ... ers.bar | bar |
8391
| src/koa.js:26:3:26:24 | ctx.req ... ('bar') | bar |
92+
| src/koa.js:37:2:37:15 | ctx.header.bar | bar |
93+
| src/koa.js:38:2:38:16 | ctx.headers.bar | bar |
94+
| src/koa.js:40:2:40:15 | ctx.get('bar') | bar |
8495
test_RouteHandler_getAResponseExpr
8596
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:12:3:12:15 | this.response |
8697
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:14:3:14:14 | ctx.response |
@@ -90,6 +101,7 @@ test_RouteHandler_getAResponseExpr
90101
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:44:2:44:13 | ctx.response |
91102
test_ResponseSendArgument
92103
| src/koa.js:18:23:18:23 | x | src/koa.js:10:10:28:1 | functio ... az');\\n} |
104+
| src/koa.js:31:13:31:13 | x | src/koa.js:30:10:45:1 | async c ... url);\\n} |
93105
test_RouteSetup_getARouteHandler
94106
| src/koa.js:8:1:8:18 | app2.use(handler1) | src/koa.js:7:1:7:22 | functio ... r1() {} |
95107
| src/koa.js:10:1:28:2 | app2.us ... z');\\n}) | src/koa.js:10:10:28:1 | functio ... az');\\n} |

0 commit comments

Comments
 (0)