JS: Reduce reliance on RouteHandler in Express model

This commit is contained in:
Asger Feldthaus
2021-04-15 16:54:00 +01:00
parent 1ab75eb6f4
commit ad12f383d9

View File

@@ -390,7 +390,7 @@ module Express {
}
/** An Express response source. */
abstract private class ResponseSource extends HTTP::Servers::ResponseSource { }
abstract class ResponseSource extends HTTP::Servers::ResponseSource { }
/**
* An Express response source, that is, the response parameter of a
@@ -421,7 +421,7 @@ module Express {
}
/** An Express request source. */
abstract private class RequestSource extends HTTP::Servers::RequestSource { }
abstract class RequestSource extends HTTP::Servers::RequestSource { }
/**
* An Express request source, that is, the request parameter of a
@@ -465,73 +465,98 @@ module Express {
* Gets a reference to the "query" object from a request-object originating from route-handler `rh`.
*/
DataFlow::SourceNode getAQueryObjectReference(DataFlow::TypeTracker t, RouteHandler rh) {
t.startInProp("query") and
result = rh.getARequestSource()
or
exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh).track(t2, t))
result = queryRef(rh.getARequestSource(), t)
}
/**
* Gets a reference to the "params" object from a request-object originating from route-handler `rh`.
*/
DataFlow::SourceNode getAParamsObjectReference(DataFlow::TypeTracker t, RouteHandler rh) {
t.startInProp("params") and
result = rh.getARequestSource()
result = paramsRef(rh.getARequestSource(), t)
}
/** The input parameter to an `app.param()` route handler. */
private class ParamHandlerInputAccess extends HTTP::RequestInputAccess {
RouteHandler rh;
ParamHandlerInputAccess() {
exists(RouteSetup setup | rh = setup.getARouteHandler() |
this = DataFlow::parameterNode(rh.getRouteHandlerParameter("parameter"))
)
}
override HTTP::RouteHandler getRouteHandler() { result = rh }
override string getKind() { result = "parameter" }
}
/** Gets a data flow node referring to `req.query`. */
private DataFlow::SourceNode queryRef(RequestSource req, DataFlow::TypeTracker t) {
t.start() and
result = req.ref().getAPropertyRead("query")
or
exists(DataFlow::TypeTracker t2 | result = getAParamsObjectReference(t2, rh).track(t2, t))
exists(DataFlow::TypeTracker t2 | result = queryRef(req, t2).track(t2, t))
}
/** Gets a data flow node referring to `req.query`. */
private DataFlow::SourceNode queryRef(RequestSource req) {
result = queryRef(req, DataFlow::TypeTracker::end())
}
/** Gets a data flow node referring to `req.params`. */
private DataFlow::SourceNode paramsRef(RequestSource req, DataFlow::TypeTracker t) {
t.start() and
result = req.ref().getAPropertyRead("params")
or
exists(DataFlow::TypeTracker t2 | result = paramsRef(req, t2).track(t2, t))
}
/** Gets a data flow node referring to `req.params`. */
private DataFlow::SourceNode paramsRef(RequestSource req) {
result = paramsRef(req, DataFlow::TypeTracker::end())
}
/**
* An access to a user-controlled Express request input.
*/
class RequestInputAccess extends HTTP::RequestInputAccess {
RouteHandler rh;
RequestSource request;
string kind;
RequestInputAccess() {
kind = "parameter" and
this =
[
getAQueryObjectReference(DataFlow::TypeTracker::end(), rh),
getAParamsObjectReference(DataFlow::TypeTracker::end(), rh)
].getAPropertyRead()
this = [queryRef(request), paramsRef(request)].getAPropertyRead()
or
exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() |
exists(DataFlow::SourceNode ref | ref = request.ref() |
kind = "parameter" and
this = request.getAMethodCall("param")
this = ref.getAMethodCall("param")
or
// `req.originalUrl`
kind = "url" and
this = request.getAPropertyRead("originalUrl")
this = ref.getAPropertyRead("originalUrl")
or
// `req.cookies`
kind = "cookie" and
this = request.getAPropertyRead("cookies")
this = ref.getAPropertyRead("cookies")
or
// `req.files`, treated the same as `req.body`.
// `express-fileupload` uses .files, and `multer` uses .files or .file
kind = "body" and
this = request.getAPropertyRead(["files", "file"])
)
or
kind = "body" and
this.asExpr() = rh.getARequestBodyAccess()
or
// `value` in `router.param('foo', (req, res, next, value) => { ... })`
kind = "parameter" and
exists(RouteSetup setup | rh = setup.getARouteHandler() |
this = DataFlow::parameterNode(rh.getRouteHandlerParameter("parameter"))
this = ref.getAPropertyRead(["files", "file"])
or
kind = "body" and
this = ref.getAPropertyRead("body")
)
}
override RouteHandler getRouteHandler() { result = rh }
override RouteHandler getRouteHandler() { result = request.getRouteHandler() }
override string getKind() { result = kind }
override predicate isUserControlledObject() {
kind = "body" and
exists(ExpressLibraries::BodyParser bodyParser, RouteHandlerExpr expr |
expr.getBody() = rh and
expr.getBody() = request.getRouteHandler() and
bodyParser.producesUserControlledObjects() and
bodyParser.flowsToExpr(expr.getAMatchingAncestor())
)
@@ -542,13 +567,11 @@ module Express {
forall(ExpressLibraries::BodyParser bodyParser | bodyParser.producesUserControlledObjects())
or
kind = "parameter" and
exists(DataFlow::Node request | request = DataFlow::valueNode(rh.getARequestExpr()) |
this.(DataFlow::MethodCallNode).calls(request, "param")
)
this = request.ref().getAMethodCall("param")
or
// `req.query.name`
kind = "parameter" and
this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh).getAPropertyRead()
this = queryRef(request).getAPropertyRead()
}
}
@@ -556,29 +579,14 @@ module Express {
* An access to a header on an Express request.
*/
private class RequestHeaderAccess extends HTTP::RequestHeaderAccess {
RouteHandler rh;
RequestSource request;
RequestHeaderAccess() {
exists(DataFlow::Node request | request = DataFlow::valueNode(rh.getARequestExpr()) |
exists(string methodName |
// `req.get(...)` or `req.header(...)`
this.(DataFlow::MethodCallNode).calls(request, methodName)
|
methodName = "get" or
methodName = "header"
)
or
exists(DataFlow::PropRead headers |
// `req.headers.name`
headers.accesses(request, "headers") and
this = headers.getAPropertyRead()
)
or
exists(string propName | propName = "host" or propName = "hostname" |
// `req.host` and `req.hostname` are derived from headers
this.(DataFlow::PropRead).accesses(request, propName)
)
)
this = request.ref().getAMethodCall(["get", "header"])
or
this = request.ref().getAPropertyRead("headers").getAPropertyRead()
or
this = request.ref().getAPropertyRead(["host", "hostname"])
}
override string getAHeaderName() {
@@ -591,7 +599,7 @@ module Express {
)
}
override RouteHandler getRouteHandler() { result = rh }
override RouteHandler getRouteHandler() { result = request.getRouteHandler() }
override string getKind() { result = "header" }
}
@@ -599,14 +607,7 @@ module Express {
/**
* HTTP headers created by Express calls
*/
abstract private class ExplicitHeader extends HTTP::ExplicitHeaderDefinition {
Expr response;
/**
* Gets the response expression that this header is set on.
*/
Expr getResponse() { result = response }
}
abstract private class ExplicitHeader extends HTTP::ExplicitHeaderDefinition { }
/**
* Holds if `e` is an HTTP request object.
@@ -635,16 +636,13 @@ module Express {
* An invocation of the `redirect` method of an HTTP response object.
*/
private class RedirectInvocation extends HTTP::RedirectInvocation, MethodCallExpr {
RouteHandler rh;
ResponseSource response;
RedirectInvocation() {
getReceiver() = rh.getAResponseExpr() and
getMethodName() = "redirect"
}
RedirectInvocation() { this = response.ref().getAMethodCall("redirect").asExpr() }
override Expr getUrlArgument() { result = getLastArgument() }
override RouteHandler getRouteHandler() { result = rh }
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
}
/**
@@ -662,21 +660,18 @@ module Express {
* An invocation of the `set` or `header` method on an HTTP response object that
* sets multiple headers.
*/
class SetMultipleHeaders extends ExplicitHeader, DataFlow::ValueNode {
override MethodCallExpr astNode;
RouteHandler rh;
class SetMultipleHeaders extends ExplicitHeader, DataFlow::MethodCallNode {
ResponseSource response;
SetMultipleHeaders() {
astNode.getReceiver() = rh.getAResponseExpr() and
response = astNode.getReceiver() and
astNode.getMethodName() = any(string n | n = "set" or n = "header") and
astNode.getNumArgument() = 1
this = response.ref().getAMethodCall(["set", "header"]) and
getNumArgument() = 1
}
/**
* Gets a reference to the multiple headers object that is to be set.
*/
private DataFlow::SourceNode getAHeaderSource() { result.flowsToExpr(astNode.getArgument(0)) }
private DataFlow::SourceNode getAHeaderSource() { result.flowsTo(getArgument(0)) }
override predicate definesExplicitly(string headerName, Expr headerValue) {
exists(string header |
@@ -685,7 +680,7 @@ module Express {
)
}
override RouteHandler getRouteHandler() { result = rh }
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
override Expr getNameExpr() {
exists(DataFlow::PropWrite write | getAHeaderSource().getAPropertyWrite() = write |
@@ -705,31 +700,26 @@ module Express {
* An argument passed to the `send` or `end` method of an HTTP response object.
*/
private class ResponseSendArgument extends HTTP::ResponseSendArgument {
RouteHandler rh;
ResponseSource response;
ResponseSendArgument() {
exists(MethodCallExpr mce |
mce.calls(rh.getAResponseExpr(), "send") and
this = mce.getArgument(0)
)
}
ResponseSendArgument() { this = response.ref().getAMethodCall("send").getArgument(0).asExpr() }
override RouteHandler getRouteHandler() { result = rh }
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
}
/**
* An invocation of the `cookie` method on an HTTP response object.
*/
class SetCookie extends HTTP::CookieDefinition, MethodCallExpr {
RouteHandler rh;
ResponseSource response;
SetCookie() { calls(rh.getAResponseExpr(), "cookie") }
SetCookie() { this = response.ref().getAMethodCall("cookie").asExpr() }
override Expr getNameArgument() { result = getArgument(0) }
override Expr getValueArgument() { result = getArgument(1) }
override RouteHandler getRouteHandler() { result = rh }
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
}
/**
@@ -750,11 +740,11 @@ module Express {
* An object passed to the `render` method of an HTTP response object.
*/
class TemplateObjectInput extends DataFlow::Node {
RouteHandler rh;
ResponseSource response;
TemplateObjectInput() {
exists(DataFlow::MethodCallNode render |
render.calls(rh.getAResponseExpr().flow(), "render") and
render = response.ref().getAMethodCall("render") and
this = render.getArgument(1)
)
}
@@ -762,7 +752,7 @@ module Express {
/**
* Gets the route handler that uses this object.
*/
RouteHandler getRouteHandler() { result = rh }
RouteHandler getRouteHandler() { result = response.getRouteHandler() }
}
/**