convert the remaining Koa models to DataFlow nodes

This commit is contained in:
Erik Krogh Kristensen
2022-03-30 19:48:23 +02:00
committed by erik-krogh
parent fc54ba823b
commit 136124fbaa
9 changed files with 99 additions and 57 deletions

View File

@@ -24,7 +24,7 @@ module Koa {
HeaderDefinition() {
// ctx.set('Cache-Control', 'no-cache');
this.calls(rh.getAResponseOrContextExpr().flow(), "set")
this.calls(rh.getAResponseOrContextNode(), "set")
or
// ctx.response.header('Cache-Control', 'no-cache')
this.calls(rh.getAResponseNode(), "header")
@@ -40,10 +40,17 @@ module Koa {
/**
* Gets the parameter of the route handler that contains the context object.
*/
Parameter getContextParameter() {
result = this.getAFunctionValue().getFunction().getParameter(0)
DataFlow::ParameterNode getContextParameter() {
result = this.getAFunctionValue().getParameter(0)
}
/**
* DEPRECATED: Use `getAContextNode` instead.
* Gets an expression that contains the "context" object of
* a route handler invocation.
*/
deprecated Expr getAContextExpr() { result.(ContextExpr).getRouteHandler() = this }
/**
* Gets an expression that contains the "context" object of
* a route handler invocation.
@@ -52,24 +59,40 @@ module Koa {
* `this` or `ctx`, given as the first and only argument to the
* route handler.
*/
Expr getAContextExpr() { result.(ContextExpr).getRouteHandler() = this }
DataFlow::Node getAContextNode() { result.(ContextNode).getRouteHandler() = this }
/**
* DEPRECATED: Use `getAResponseOrContextNode` instead.
* Gets an expression that contains the context or response
* object of a route handler invocation.
*/
deprecated Expr getAResponseOrContextExpr() {
result = this.getAResponseNode().asExpr() or result = this.getAContextExpr()
}
/**
* Gets an expression that contains the context or response
* object of a route handler invocation.
*/
Expr getAResponseOrContextExpr() {
// TODO: DataFlow::Node
result = this.getAResponseNode().asExpr() or result = this.getAContextExpr()
DataFlow::Node getAResponseOrContextNode() {
result = this.getAResponseNode() or result = this.getAContextNode()
}
/**
* DEPRECATED: Use `getARequestOrContextNode` instead.
* Gets an expression that contains the context or request
* object of a route handler invocation.
*/
deprecated Expr getARequestOrContextExpr() {
result = this.getARequestNode().asExpr() or result = this.getAContextExpr()
}
/**
* Gets an expression that contains the context or request
* object of a route handler invocation.
*/
Expr getARequestOrContextExpr() {
// TODO: DataFlow::Node
result = this.getARequestNode().asExpr() or result = this.getAContextExpr()
DataFlow::Node getARequestOrContextNode() {
result = this.getARequestNode() or result = this.getAContextNode()
}
/**
@@ -110,7 +133,7 @@ module Koa {
RouteHandler rh;
ContextSource() {
this = DataFlow::parameterNode(rh.getContextParameter())
this = rh.getContextParameter()
or
this.(DataFlow::ThisNode).getBinder() = rh
}
@@ -206,10 +229,10 @@ module Koa {
* A Koa request source, that is, an access to the `request` property
* of a context object.
*/
private class RequestSource extends HTTP::Servers::RequestSource {
ContextExpr ctx;
private class RequestSource extends HTTP::Servers::RequestSource instanceof DataFlow::PropRead {
ContextNode ctx;
RequestSource() { this.asExpr().(PropAccess).accesses(ctx, "request") }
RequestSource() { super.accesses(ctx, "request") }
/**
* Gets the route handler that provides this response.
@@ -241,10 +264,10 @@ module Koa {
* A Koa response source, that is, an access to the `response` property
* of a context object.
*/
private class ResponseSource extends HTTP::Servers::ResponseSource {
ContextExpr ctx;
private class ResponseSource extends HTTP::Servers::ResponseSource instanceof DataFlow::PropRead {
ContextNode ctx;
ResponseSource() { this.asExpr().(PropAccess).accesses(ctx, "response") }
ResponseSource() { super.accesses(ctx, "response") }
/**
* Gets the route handler that provides this response.
@@ -253,12 +276,25 @@ module Koa {
}
/**
* DEPRECATED: Use `ContextNode` instead.
* An expression that may hold a Koa context object.
*/
class ContextExpr extends Expr {
deprecated class ContextExpr extends Expr {
ContextNode node;
ContextExpr() { node.asExpr() = this }
/** Gets the route handler that provides this response. */
deprecated RouteHandler getRouteHandler() { result = node.getRouteHandler() }
}
/**
* An expression that may hold a Koa context object.
*/
class ContextNode extends DataFlow::Node {
ContextSource src;
ContextExpr() { src.ref().flowsTo(DataFlow::valueNode(this)) }
ContextNode() { src.ref().flowsTo(this) }
/**
* Gets the route handler that provides this response.
@@ -310,11 +346,11 @@ module Koa {
kind = "parameter" and
this = rh.getARequestParameterAccess()
or
exists(Expr e | rh.getARequestOrContextExpr() = e |
exists(DataFlow::Node e | rh.getARequestOrContextNode() = e |
// `ctx.request.url`, `ctx.request.originalUrl`, or `ctx.request.href`
exists(string propName |
kind = "url" and
this.asExpr().(PropAccess).accesses(e, propName)
this.(DataFlow::PropRead).accesses(e, propName)
|
propName = "url"
or
@@ -325,19 +361,19 @@ module Koa {
or
// params, when handler is registered by `koa-router` or similar.
kind = "parameter" and
this.asExpr().(PropAccess).accesses(e, "params")
this.(DataFlow::PropRead).accesses(e, "params")
or
// `ctx.request.body`
e.flow() instanceof RequestNode and
e instanceof RequestNode and
kind = "body" and
this.asExpr().(PropAccess).accesses(e, "body")
this.(DataFlow::PropRead).accesses(e, "body")
or
// `ctx.cookies.get(<name>)`
exists(PropAccess cookies |
e instanceof ContextExpr and
exists(DataFlow::PropRead cookies |
e instanceof ContextNode and
kind = "cookie" and
cookies.accesses(e, "cookies") and
this = cookies.flow().(DataFlow::SourceNode).getAMethodCall("get")
this = cookies.getAMethodCall("get")
)
or
exists(RequestHeaderAccess access | access = this |
@@ -356,9 +392,9 @@ module Koa {
private DataFlow::Node getAQueryParameterAccess(RouteHandler rh) {
// `ctx.query.name` or `ctx.request.query.name`
exists(PropAccess q |
q.accesses(rh.getARequestOrContextExpr(), "query") and
result = q.flow().(DataFlow::SourceNode).getAPropertyRead()
exists(DataFlow::PropRead q |
q.accesses(rh.getARequestOrContextNode(), "query") and
result = q.getAPropertyRead()
)
}
@@ -369,18 +405,18 @@ module Koa {
RouteHandler rh;
RequestHeaderAccess() {
exists(Expr e | e = rh.getARequestOrContextExpr() |
exists(string propName, PropAccess headers |
exists(DataFlow::Node e | e = rh.getARequestOrContextNode() |
exists(string propName, DataFlow::PropRead headers |
// `ctx.request.header.<name>`, `ctx.request.headers.<name>`
headers.accesses(e, propName) and
this = headers.flow().(DataFlow::SourceNode).getAPropertyRead()
this = headers.getAPropertyRead()
|
propName = "header" or
propName = "headers"
)
or
// `ctx.request.get(<name>)`
this.asExpr().(MethodCallExpr).calls(e, "get")
this.(DataFlow::MethodCallNode).calls(e, "get")
)
}
@@ -427,9 +463,7 @@ module Koa {
RouteHandler rh;
ResponseSendArgument() {
exists(DataFlow::PropWrite pwn |
pwn.writes(DataFlow::valueNode(rh.getAResponseOrContextExpr()), "body", this)
)
exists(DataFlow::PropWrite pwn | pwn.writes(rh.getAResponseOrContextNode(), "body", this))
}
override RouteHandler getRouteHandler() { result = rh }
@@ -438,12 +472,10 @@ module Koa {
/**
* An invocation of the `redirect` method of an HTTP response object.
*/
private class RedirectInvocation extends HTTP::RedirectInvocation, DataFlow::MethodCallNode {
private class RedirectInvocation extends HTTP::RedirectInvocation instanceof DataFlow::MethodCallNode {
RouteHandler rh;
RedirectInvocation() {
this.asExpr().(MethodCallExpr).calls(rh.getAResponseOrContextExpr(), "redirect")
} // TODO: Improve this.
RedirectInvocation() { super.calls(rh.getAResponseOrContextNode(), "redirect") }
override DataFlow::Node getUrlArgument() { result = this.getArgument(0) }

View File

@@ -49,8 +49,7 @@ module NodeJSLib {
/**
* Holds if `call` is an invocation of `http.createServer` or `https.createServer`.
*/
predicate isCreateServer(CallExpr call) {
// TODO: DataFlow::Node
predicate isCreateServer(DataFlow::CallNode call) {
exists(string pkg, string fn |
pkg = "http" and fn = "createServer"
or
@@ -61,7 +60,7 @@ module NodeJSLib {
or
pkg = "http2" and fn = "createSecureServer"
|
call = DataFlow::moduleMember(pkg, fn).getAnInvocation().asExpr()
call = DataFlow::moduleMember(pkg, fn).getAnInvocation()
)
}
@@ -423,7 +422,7 @@ module NodeJSLib {
* An expression that creates a new Node.js server.
*/
class ServerDefinition extends HTTP::Servers::StandardServerDefinition {
ServerDefinition() { isCreateServer(this.asExpr()) }
ServerDefinition() { isCreateServer(this) }
}
/** An expression that is passed as `http.request({ auth: <expr> }, ...)`. */

View File

@@ -61,9 +61,7 @@ module CleartextStorage {
/**
* An expression set as a value of localStorage or sessionStorage.
*/
class WebStorageSink extends Sink {
WebStorageSink() { this.asExpr() instanceof WebStorageWrite }
}
class WebStorageSink extends Sink instanceof WebStorageWrite { }
/**
* An expression stored by AngularJS.

View File

@@ -104,7 +104,9 @@ module ClientSideUrlRedirect {
xss = true
or
// An assignment to `location`
exists(Assignment assgn | isLocation(assgn.getTarget()) and astNode = assgn.getRhs()) and
exists(Assignment assgn |
isLocationNode(assgn.getTarget().flow()) and astNode = assgn.getRhs()
) and
xss = true
or
// An assignment to `location.href`, `location.protocol` or `location.hostname`

View File

@@ -143,18 +143,17 @@ class DomPropWriteNode extends Assignment {
/**
* A value written to web storage, like `localStorage` or `sessionStorage`.
*/
class WebStorageWrite extends Expr {
// TODO: DataFlow::Node
class WebStorageWrite extends DataFlow::Node {
WebStorageWrite() {
exists(DataFlow::SourceNode webStorage |
webStorage = DataFlow::globalVarRef("localStorage") or
webStorage = DataFlow::globalVarRef("sessionStorage")
|
// an assignment to `window.localStorage[someProp]`
this = webStorage.getAPropertyWrite().getRhs().asExpr()
this = webStorage.getAPropertyWrite().getRhs()
or
// an invocation of `window.localStorage.setItem`
this = webStorage.getAMethodCall("setItem").getArgument(1).asExpr()
this = webStorage.getAMethodCall("setItem").getArgument(1)
)
}
}

View File

@@ -1,3 +1,3 @@
import javascript
query predicate test_isCreateServer(CallExpr e) { NodeJSLib::isCreateServer(e) }
query predicate test_isCreateServer(DataFlow::CallNode e) { NodeJSLib::isCreateServer(e) }

View File

@@ -1,5 +1,5 @@
import javascript
query predicate test_ContextExpr(Koa::ContextExpr e, Koa::RouteHandler res) {
query predicate test_ContextExpr(Koa::ContextNode e, Koa::RouteHandler res) {
res = e.getRouteHandler()
}

View File

@@ -1,5 +1,5 @@
import semmle.javascript.frameworks.Express
query predicate test_RouteHandler_getAContextExpr(Koa::RouteHandler rh, Expr res) {
res = rh.getAContextExpr()
query predicate test_RouteHandler_getAContextExpr(Koa::RouteHandler rh, DataFlow::Node res) {
res = rh.getAContextNode()
}

View File

@@ -47,6 +47,9 @@ test_ResponseExpr
| 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:7:1:7:22 | functio ... r1() {} | src/koa.js:7:1:7:0 | this |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:10:10:10:9 | this |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:10:28:10:30 | ctx |
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:10:28:10:30 | ctx |
| 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 |
@@ -64,6 +67,7 @@ test_RouteHandler_getAContextExpr
| 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:30:16:30:18 | ctx |
| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:30:16:30:18 | 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 |
@@ -78,9 +82,11 @@ test_RouteHandler_getAContextExpr
| 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:47:16:47:18 | ctx |
| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:47:16:47:18 | 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 |
| src/koa.js:59:10:61:1 | functio ... .url;\\n} | src/koa.js:59:10:59:9 | this |
| src/koa.js:59:10:61:1 | functio ... .url;\\n} | src/koa.js:60:2:60:5 | this |
test_HeaderDefinition
| src/koa.js:11:3:11:25 | this.se ... 1', '') | src/koa.js:10:10:28:1 | functio ... az');\\n} |
@@ -157,6 +163,9 @@ test_RouteHandler_getARequestExpr
| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:26:3:26:13 | ctx.request |
| src/koa.js:59:10:61:1 | functio ... .url;\\n} | src/koa.js:60:2:60:13 | this.request |
test_ContextExpr
| src/koa.js:7:1:7:0 | this | src/koa.js:7:1:7:22 | functio ... r1() {} |
| src/koa.js:10:10:10:9 | this | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:10:28:10:30 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} |
| src/koa.js:10:28:10:30 | ctx | 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 | src/koa.js:10:10:28:1 | functio ... az');\\n} |
@@ -174,6 +183,7 @@ test_ContextExpr
| 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:30:16:30:18 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} |
| src/koa.js:30:16:30:18 | 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} |
@@ -188,9 +198,11 @@ test_ContextExpr
| 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:47:16:47:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:47:16:47:18 | 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 | src/koa.js:47:10:56:1 | async c ... .foo;\\n} |
| src/koa.js:59:10:59:9 | this | src/koa.js:59:10:61:1 | functio ... .url;\\n} |
| src/koa.js:60:2:60:5 | this | src/koa.js:59:10:61:1 | functio ... .url;\\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} |