Merge pull request #1152 from esben-semmle/js/koa-improvements

Approved by xiemaisi
This commit is contained in:
semmle-qlci
2019-04-02 08:51:19 +01:00
committed by GitHub
8 changed files with 218 additions and 31 deletions

View File

@@ -1,3 +1,4 @@
/**
* Provides classes for working with [Koa](https://koajs.com) applications.
*/
@@ -24,7 +25,7 @@ module Koa {
HeaderDefinition() {
// ctx.set('Cache-Control', 'no-cache');
astNode.calls(rh.getAContextExpr(), "set")
astNode.calls(rh.getAResponseOrContextExpr(), "set")
or
// ctx.response.header('Cache-Control', 'no-cache')
astNode.calls(rh.getAResponseExpr(), "header")
@@ -58,6 +59,18 @@ module Koa {
* route handler.
*/
Expr getAContextExpr() { result.(ContextExpr).getRouteHandler() = this }
/**
* Gets an expression that contains the context or response
* object of a route handler invocation.
*/
Expr getAResponseOrContextExpr() { result = getAResponseExpr() or result = getAContextExpr() }
/**
* Gets an expression that contains the context or request
* object of a route handler invocation.
*/
Expr getARequestOrContextExpr() { result = getARequestExpr() or result = getAContextExpr() }
}
/**
@@ -159,35 +172,39 @@ module Koa {
string kind;
RequestInputAccess() {
exists(Expr request | request = rh.getARequestExpr() |
// `ctx.request.body`
kind = "body" and
this.asExpr().(PropAccess).accesses(request, "body")
or
kind = "parameter" and
this = getAQueryParameterAccess(rh)
or
kind = "parameter" and
this = getAQueryParameterAccess(rh)
or
exists(Expr e | rh.getARequestOrContextExpr() = e |
// `ctx.request.url`, `ctx.request.originalUrl`, or `ctx.request.href`
exists(string propName |
// `ctx.request.url`, `ctx.request.originalUrl`, or `ctx.request.href`
kind = "url" and
this.asExpr().(PropAccess).accesses(request, propName)
this.asExpr().(PropAccess).accesses(e, propName)
|
propName = "url" or
propName = "originalUrl" or
propName = "url"
or
propName = "originalUrl"
or
propName = "href"
)
)
or
exists(PropAccess cookies |
or
// `ctx.request.body`
e instanceof RequestExpr and
kind = "body" and
this.asExpr().(PropAccess).accesses(e, "body")
or
// `ctx.cookies.get(<name>)`
kind = "cookie" and
cookies.accesses(rh.getAContextExpr(), "cookies") and
this.asExpr().(MethodCallExpr).calls(cookies, "get")
)
or
exists(RequestHeaderAccess access | access = this |
rh = access.getRouteHandler() and
kind = "header"
exists(PropAccess cookies |
e instanceof ContextExpr and
kind = "cookie" and
cookies.accesses(e, "cookies") and
this = cookies.flow().(DataFlow::SourceNode).getAMethodCall("get")
)
or
exists(RequestHeaderAccess access | access = this |
rh = access.getRouteHandler() and
kind = "header"
)
)
}
@@ -199,8 +216,11 @@ module Koa {
}
private DataFlow::Node getAQueryParameterAccess(RouteHandler rh) {
// `ctx.request.query.name`
result.asExpr().(PropAccess).getBase().(PropAccess).accesses(rh.getARequestExpr(), "query")
// `ctx.query.name` or `ctx.request.query.name`
exists(PropAccess q |
q.accesses(rh.getARequestOrContextExpr(), "query") and
result = q.flow().(DataFlow::SourceNode).getAPropertyRead()
)
}
/**
@@ -210,18 +230,18 @@ module Koa {
RouteHandler rh;
RequestHeaderAccess() {
exists(Expr request | request = rh.getARequestExpr() |
exists(Expr e | e = rh.getARequestOrContextExpr() |
exists(string propName, PropAccess headers |
// `ctx.request.header.<name>`, `ctx.request.headers.<name>`
headers.accesses(request, propName) and
this.asExpr().(PropAccess).accesses(headers, _)
headers.accesses(e, propName) and
this = headers.flow().(DataFlow::SourceNode).getAPropertyRead()
|
propName = "header" or
propName = "headers"
)
or
// `ctx.request.get(<name>)`
this.asExpr().(MethodCallExpr).calls(request, "get")
this.asExpr().(MethodCallExpr).calls(e, "get")
)
}
@@ -264,10 +284,25 @@ module Koa {
ResponseSendArgument() {
exists(DataFlow::PropWrite pwn |
pwn.writes(DataFlow::valueNode(rh.getAResponseExpr()), "body", DataFlow::valueNode(this))
pwn
.writes(DataFlow::valueNode(rh.getAResponseOrContextExpr()), "body",
DataFlow::valueNode(this))
)
}
override RouteHandler getRouteHandler() { result = rh }
}
/**
* An invocation of the `redirect` method of an HTTP response object.
*/
private class RedirectInvocation extends HTTP::RedirectInvocation, MethodCallExpr {
RouteHandler rh;
RedirectInvocation() { this.(MethodCallExpr).calls(rh.getAResponseOrContextExpr(), "redirect") }
override Expr getUrlArgument() { result = getArgument(0) }
override RouteHandler getRouteHandler() { result = rh }
}
}

View File

@@ -0,0 +1,9 @@
import javascript
query predicate test_RedirectInvocation(
HTTP::RedirectInvocation redirect, Expr url, HTTP::RouteHandler rh
) {
redirect.getUrlArgument() = url and
redirect.getRouteHandler() = rh
}

View File

@@ -26,3 +26,31 @@ app2.use(function handler2(ctx){ // HTTP::RouteHandler
ctx.request.get('bar');
ctx.cookies.get('baz');
});
app2.use(async ctx => {
ctx.body = x;
ctx.body;
ctx.query.foo;
ctx.url;
ctx.originalUrl;
ctx.href;
ctx.header.bar;
ctx.headers.bar;
ctx.set('bar');
ctx.get('bar');
var url = ctx.query.target;
ctx.redirect(url);
ctx.response.redirect(url);
});
app2.use(async ctx => {
var cookies = ctx.cookies;
cookies.get();
var query = ctx.query;
query.foo;
var headers = ctx.headers;
headers.foo;
});

View File

@@ -1,6 +1,8 @@
test_RouteSetup
| src/koa.js:8:1:8:18 | app2.use(handler1) |
| src/koa.js:10:1:28:2 | app2.us ... z');\\n}) |
| src/koa.js:30:1:45:2 | app2.us ... rl);\\n}) |
| src/koa.js:47:1:56:2 | app2.us ... foo;\\n}) |
test_RequestInputAccess
| src/koa.js:19:3:19:18 | ctx.request.body | body | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:20:3:20:23 | ctx.req ... ery.foo | parameter | src/koa.js:10:10:28:1 | functio ... az');\\n} |
@@ -11,6 +13,17 @@ test_RequestInputAccess
| src/koa.js:25:3:25:25 | ctx.req ... ers.bar | header | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:26:3:26:24 | ctx.req ... ('bar') | header | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:27:3:27:24 | ctx.coo ... ('baz') | cookie | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:33:2:33:14 | ctx.query.foo | parameter | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:34:2:34:8 | ctx.url | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:35:2:35:16 | ctx.originalUrl | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:36:2:36:9 | ctx.href | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:37:2:37:15 | ctx.header.bar | header | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:38:2:38:16 | ctx.headers.bar | header | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:40:2:40:15 | ctx.get('bar') | header | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:42:12:42:27 | ctx.query.target | parameter | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:49:2:49:14 | cookies.get() | cookie | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:52:2:52:10 | query.foo | parameter | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:55:2:55:12 | headers.foo | header | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
test_RouteHandler_getAResponseHeader
| src/koa.js:10:10:28:1 | functio ... az');\\n} | header1 | src/koa.js:11:3:11:25 | this.se ... 1', '') |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | header2 | src/koa.js:12:3:12:37 | this.re ... 2', '') |
@@ -29,6 +42,7 @@ test_ResponseExpr
| src/koa.js:15:13:15:24 | ctx.response | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:16:3:16:5 | rsp | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:18:3:18:14 | ctx.response | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:44:2:44:13 | ctx.response | src/koa.js:30:10:45:1 | async c ... url);\\n} |
test_RouteHandler_getAContextExpr
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:11:3:11:6 | this |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:12:3:12:6 | this |
@@ -45,15 +59,34 @@ test_RouteHandler_getAContextExpr
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:25:3:25:5 | ctx |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:26:3:26:5 | ctx |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:27:3:27:5 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:31:2:31:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:32:2:32:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:33:2:33:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:34:2:34:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:35:2:35:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:36:2:36:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:37:2:37:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:38:2:38:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:39:2:39:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:40:2:40:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:42:12:42:14 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:43:2:43:4 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:44:2:44:4 | ctx |
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:48:16:48:18 | ctx |
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:51:14:51:16 | ctx |
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:54:16:54:18 | ctx |
test_HeaderDefinition
| src/koa.js:11:3:11:25 | this.se ... 1', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:12:3:12:37 | this.re ... 2', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:13:3:13:24 | ctx.set ... 3', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:14:3:14:36 | ctx.res ... 4', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:16:3:16:27 | rsp.hea ... 5', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:39:2:39:15 | ctx.set('bar') | src/koa.js:30:10:45:1 | async c ... url);\\n} |
test_RouteSetup_getServer
| src/koa.js:8:1:8:18 | app2.use(handler1) | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:10:1:28:2 | app2.us ... z');\\n}) | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:30:1:45:2 | app2.us ... rl);\\n}) | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:47:1:56:2 | app2.us ... foo;\\n}) | src/koa.js:5:12:5:20 | new Koa() |
test_HeaderDefinition_getAHeaderName
| src/koa.js:11:3:11:25 | this.se ... 1', '') | header1 |
| src/koa.js:12:3:12:37 | this.re ... 2', '') | header2 |
@@ -64,23 +97,33 @@ test_HeaderAccess
| src/koa.js:24:3:24:24 | ctx.req ... der.bar | bar |
| src/koa.js:25:3:25:25 | ctx.req ... ers.bar | bar |
| src/koa.js:26:3:26:24 | ctx.req ... ('bar') | bar |
| src/koa.js:37:2:37:15 | ctx.header.bar | bar |
| src/koa.js:38:2:38:16 | ctx.headers.bar | bar |
| src/koa.js:40:2:40:15 | ctx.get('bar') | bar |
| src/koa.js:55:2:55:12 | headers.foo | foo |
test_RouteHandler_getAResponseExpr
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:12:3:12:15 | this.response |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:14:3:14:14 | ctx.response |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:15:13:15:24 | ctx.response |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:16:3:16:5 | rsp |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:18:3:18:14 | ctx.response |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:44:2:44:13 | ctx.response |
test_ResponseSendArgument
| src/koa.js:18:23:18:23 | x | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:31:13:31:13 | x | src/koa.js:30:10:45:1 | async c ... url);\\n} |
test_RouteSetup_getARouteHandler
| src/koa.js:8:1:8:18 | app2.use(handler1) | src/koa.js:7:1:7:22 | functio ... r1() {} |
| src/koa.js:10:1:28:2 | app2.us ... z');\\n}) | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:30:1:45:2 | app2.us ... rl);\\n}) | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:47:1:56:2 | app2.us ... foo;\\n}) | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
test_AppDefinition
| src/koa.js:2:12:2:33 | new (re ... oa'))() |
| src/koa.js:5:12:5:20 | new Koa() |
test_RouteHandler
| src/koa.js:7:1:7:22 | functio ... r1() {} | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:5:12:5:20 | new Koa() |
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:5:12:5:20 | new Koa() |
test_RequestExpr
| src/koa.js:19:3:19:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:20:3:20:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} |
@@ -115,3 +158,22 @@ test_ContextExpr
| src/koa.js:25:3:25:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:26:3:26:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:27:3:27:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:31:2:31:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:32:2:32:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:33:2:33:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:34:2:34:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:35:2:35:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:36:2:36:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:37:2:37:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:38:2:38:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:39:2:39:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:40:2:40:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:42:12:42:14 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:43:2:43:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:44:2:44:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:48:16:48:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:51:14:51:16 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:54:16:54:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
test_RedirectInvocation
| src/koa.js:43:2:43:18 | ctx.redirect(url) | src/koa.js:43:15:43:17 | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:44:2:44:27 | ctx.res ... ct(url) | src/koa.js:44:24:44:26 | url | src/koa.js:30:10:45:1 | async c ... url);\\n} |

View File

@@ -16,3 +16,4 @@ import RouteHandler
import RequestExpr
import RouteHandler_getARequestExpr
import ContextExpr
import RedirectInvocation

View File

@@ -22,6 +22,12 @@ nodes
| express.js:135:23:135:37 | req.params.user |
| express.js:136:16:136:36 | 'u' + r ... ms.user |
| express.js:136:22:136:36 | req.params.user |
| koa.js:6:6:6:27 | url |
| koa.js:6:12:6:27 | ctx.query.target |
| koa.js:7:15:7:17 | url |
| koa.js:8:15:8:26 | `${url}${x}` |
| koa.js:8:18:8:20 | url |
| koa.js:14:16:14:18 | url |
| node.js:6:7:6:52 | target |
| node.js:6:16:6:39 | url.par ... , true) |
| node.js:6:16:6:45 | url.par ... ).query |
@@ -60,6 +66,24 @@ edges
| express.js:134:22:134:36 | req.params.user | express.js:134:16:134:36 | '/' + r ... ms.user |
| express.js:135:23:135:37 | req.params.user | express.js:135:16:135:37 | '//' + ... ms.user |
| express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user |
| koa.js:6:6:6:27 | url | koa.js:7:15:7:17 | url |
| koa.js:6:6:6:27 | url | koa.js:8:18:8:20 | url |
| koa.js:6:6:6:27 | url | koa.js:10:40:10:42 | url |
| koa.js:6:6:6:27 | url | koa.js:10:40:10:42 | url |
| koa.js:6:6:6:27 | url | koa.js:10:51:10:51 | url |
| koa.js:6:6:6:27 | url | koa.js:11:6:11:8 | url |
| koa.js:6:6:6:27 | url | koa.js:14:16:14:18 | url |
| koa.js:6:12:6:27 | ctx.query.target | koa.js:6:6:6:27 | url |
| koa.js:8:18:8:20 | url | koa.js:8:15:8:26 | `${url}${x}` |
| koa.js:10:40:10:42 | url | koa.js:10:51:10:51 | url |
| koa.js:10:40:10:42 | url | koa.js:10:51:10:51 | url |
| koa.js:10:40:10:42 | url | koa.js:11:6:11:8 | url |
| koa.js:10:40:10:42 | url | koa.js:11:6:11:8 | url |
| koa.js:10:40:10:42 | url | koa.js:14:16:14:18 | url |
| koa.js:10:40:10:42 | url | koa.js:14:16:14:18 | url |
| koa.js:10:51:10:51 | url | koa.js:11:6:11:8 | url |
| koa.js:10:51:10:51 | url | koa.js:14:16:14:18 | url |
| koa.js:11:6:11:8 | url | koa.js:14:16:14:18 | url |
| node.js:6:7:6:52 | target | node.js:7:34:7:39 | target |
| node.js:6:16:6:39 | url.par ... , true) | node.js:6:16:6:45 | url.par ... ).query |
| node.js:6:16:6:45 | url.par ... ).query | node.js:6:16:6:52 | url.par ... .target |
@@ -95,6 +119,9 @@ edges
| express.js:134:16:134:36 | '/' + r ... ms.user | express.js:134:22:134:36 | req.params.user | express.js:134:16:134:36 | '/' + r ... ms.user | Untrusted URL redirection due to $@. | express.js:134:22:134:36 | req.params.user | user-provided value |
| express.js:135:16:135:37 | '//' + ... ms.user | express.js:135:23:135:37 | req.params.user | express.js:135:16:135:37 | '//' + ... ms.user | Untrusted URL redirection due to $@. | express.js:135:23:135:37 | req.params.user | user-provided value |
| express.js:136:16:136:36 | 'u' + r ... ms.user | express.js:136:22:136:36 | req.params.user | express.js:136:16:136:36 | 'u' + r ... ms.user | Untrusted URL redirection due to $@. | express.js:136:22:136:36 | req.params.user | user-provided value |
| koa.js:7:15:7:17 | url | koa.js:6:12:6:27 | ctx.query.target | koa.js:7:15:7:17 | url | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value |
| koa.js:8:15:8:26 | `${url}${x}` | koa.js:6:12:6:27 | ctx.query.target | koa.js:8:15:8:26 | `${url}${x}` | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value |
| koa.js:14:16:14:18 | url | koa.js:6:12:6:27 | ctx.query.target | koa.js:14:16:14:18 | url | Untrusted URL redirection due to $@. | koa.js:6:12:6:27 | ctx.query.target | user-provided value |
| node.js:7:34:7:39 | target | node.js:6:26:6:32 | req.url | node.js:7:34:7:39 | target | Untrusted URL redirection due to $@. | node.js:6:26:6:32 | req.url | user-provided value |
| node.js:15:34:15:45 | '/' + target | node.js:11:26:11:32 | req.url | node.js:15:34:15:45 | '/' + target | Untrusted URL redirection due to $@. | node.js:11:26:11:32 | req.url | user-provided value |
| node.js:32:34:32:55 | target ... =" + me | node.js:29:26:29:32 | req.url | node.js:32:34:32:55 | target ... =" + me | Untrusted URL redirection due to $@. | node.js:29:26:29:32 | req.url | user-provided value |

View File

@@ -0,0 +1,24 @@
const Koa = require('koa');
const url = require('url');
const app = new Koa();
app.use(async ctx => {
var url = ctx.query.target;
ctx.redirect(url); // NOT OK
ctx.redirect(`${url}${x}`); // NOT OK
var isCrossDomainRedirect = url.parse(url || '', false, true).hostname;
if(!url || isCrossDomainRedirect) {
ctx.redirect('/'); // OK
} else {
ctx.redirect(url); // NOT OK
}
if(!url || isCrossDomainRedirect || ! url.match(VALID)) {
ctx.redirect('/'); // OK
} else {
ctx.redirect(url); // OK
}
});
app.listen(3000);