Skip to content

Commit ad12f38

Browse files
committed
JS: Reduce reliance on RouteHandler in Express model
1 parent 1ab75eb commit ad12f38

File tree

1 file changed

+84
-94
lines changed
  • javascript/ql/src/semmle/javascript/frameworks

1 file changed

+84
-94
lines changed

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

Lines changed: 84 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ module Express {
390390
}
391391

392392
/** An Express response source. */
393-
abstract private class ResponseSource extends HTTP::Servers::ResponseSource { }
393+
abstract class ResponseSource extends HTTP::Servers::ResponseSource { }
394394

395395
/**
396396
* An Express response source, that is, the response parameter of a
@@ -421,7 +421,7 @@ module Express {
421421
}
422422

423423
/** An Express request source. */
424-
abstract private class RequestSource extends HTTP::Servers::RequestSource { }
424+
abstract class RequestSource extends HTTP::Servers::RequestSource { }
425425

426426
/**
427427
* An Express request source, that is, the request parameter of a
@@ -465,73 +465,98 @@ module Express {
465465
* Gets a reference to the "query" object from a request-object originating from route-handler `rh`.
466466
*/
467467
DataFlow::SourceNode getAQueryObjectReference(DataFlow::TypeTracker t, RouteHandler rh) {
468-
t.startInProp("query") and
469-
result = rh.getARequestSource()
470-
or
471-
exists(DataFlow::TypeTracker t2 | result = getAQueryObjectReference(t2, rh).track(t2, t))
468+
result = queryRef(rh.getARequestSource(), t)
472469
}
473470

474471
/**
475472
* Gets a reference to the "params" object from a request-object originating from route-handler `rh`.
476473
*/
477474
DataFlow::SourceNode getAParamsObjectReference(DataFlow::TypeTracker t, RouteHandler rh) {
478-
t.startInProp("params") and
479-
result = rh.getARequestSource()
475+
result = paramsRef(rh.getARequestSource(), t)
476+
}
477+
478+
/** The input parameter to an `app.param()` route handler. */
479+
private class ParamHandlerInputAccess extends HTTP::RequestInputAccess {
480+
RouteHandler rh;
481+
482+
ParamHandlerInputAccess() {
483+
exists(RouteSetup setup | rh = setup.getARouteHandler() |
484+
this = DataFlow::parameterNode(rh.getRouteHandlerParameter("parameter"))
485+
)
486+
}
487+
488+
override HTTP::RouteHandler getRouteHandler() { result = rh }
489+
490+
override string getKind() { result = "parameter" }
491+
}
492+
493+
/** Gets a data flow node referring to `req.query`. */
494+
private DataFlow::SourceNode queryRef(RequestSource req, DataFlow::TypeTracker t) {
495+
t.start() and
496+
result = req.ref().getAPropertyRead("query")
497+
or
498+
exists(DataFlow::TypeTracker t2 | result = queryRef(req, t2).track(t2, t))
499+
}
500+
501+
/** Gets a data flow node referring to `req.query`. */
502+
private DataFlow::SourceNode queryRef(RequestSource req) {
503+
result = queryRef(req, DataFlow::TypeTracker::end())
504+
}
505+
506+
/** Gets a data flow node referring to `req.params`. */
507+
private DataFlow::SourceNode paramsRef(RequestSource req, DataFlow::TypeTracker t) {
508+
t.start() and
509+
result = req.ref().getAPropertyRead("params")
480510
or
481-
exists(DataFlow::TypeTracker t2 | result = getAParamsObjectReference(t2, rh).track(t2, t))
511+
exists(DataFlow::TypeTracker t2 | result = paramsRef(req, t2).track(t2, t))
512+
}
513+
514+
/** Gets a data flow node referring to `req.params`. */
515+
private DataFlow::SourceNode paramsRef(RequestSource req) {
516+
result = paramsRef(req, DataFlow::TypeTracker::end())
482517
}
483518

484519
/**
485520
* An access to a user-controlled Express request input.
486521
*/
487522
class RequestInputAccess extends HTTP::RequestInputAccess {
488-
RouteHandler rh;
523+
RequestSource request;
489524
string kind;
490525

491526
RequestInputAccess() {
492527
kind = "parameter" and
493-
this =
494-
[
495-
getAQueryObjectReference(DataFlow::TypeTracker::end(), rh),
496-
getAParamsObjectReference(DataFlow::TypeTracker::end(), rh)
497-
].getAPropertyRead()
528+
this = [queryRef(request), paramsRef(request)].getAPropertyRead()
498529
or
499-
exists(DataFlow::SourceNode request | request = rh.getARequestSource().ref() |
530+
exists(DataFlow::SourceNode ref | ref = request.ref() |
500531
kind = "parameter" and
501-
this = request.getAMethodCall("param")
532+
this = ref.getAMethodCall("param")
502533
or
503534
// `req.originalUrl`
504535
kind = "url" and
505-
this = request.getAPropertyRead("originalUrl")
536+
this = ref.getAPropertyRead("originalUrl")
506537
or
507538
// `req.cookies`
508539
kind = "cookie" and
509-
this = request.getAPropertyRead("cookies")
540+
this = ref.getAPropertyRead("cookies")
510541
or
511542
// `req.files`, treated the same as `req.body`.
512543
// `express-fileupload` uses .files, and `multer` uses .files or .file
513544
kind = "body" and
514-
this = request.getAPropertyRead(["files", "file"])
515-
)
516-
or
517-
kind = "body" and
518-
this.asExpr() = rh.getARequestBodyAccess()
519-
or
520-
// `value` in `router.param('foo', (req, res, next, value) => { ... })`
521-
kind = "parameter" and
522-
exists(RouteSetup setup | rh = setup.getARouteHandler() |
523-
this = DataFlow::parameterNode(rh.getRouteHandlerParameter("parameter"))
545+
this = ref.getAPropertyRead(["files", "file"])
546+
or
547+
kind = "body" and
548+
this = ref.getAPropertyRead("body")
524549
)
525550
}
526551

527-
override RouteHandler getRouteHandler() { result = rh }
552+
override RouteHandler getRouteHandler() { result = request.getRouteHandler() }
528553

529554
override string getKind() { result = kind }
530555

531556
override predicate isUserControlledObject() {
532557
kind = "body" and
533558
exists(ExpressLibraries::BodyParser bodyParser, RouteHandlerExpr expr |
534-
expr.getBody() = rh and
559+
expr.getBody() = request.getRouteHandler() and
535560
bodyParser.producesUserControlledObjects() and
536561
bodyParser.flowsToExpr(expr.getAMatchingAncestor())
537562
)
@@ -542,43 +567,26 @@ module Express {
542567
forall(ExpressLibraries::BodyParser bodyParser | bodyParser.producesUserControlledObjects())
543568
or
544569
kind = "parameter" and
545-
exists(DataFlow::Node request | request = DataFlow::valueNode(rh.getARequestExpr()) |
546-
this.(DataFlow::MethodCallNode).calls(request, "param")
547-
)
570+
this = request.ref().getAMethodCall("param")
548571
or
549572
// `req.query.name`
550573
kind = "parameter" and
551-
this = getAQueryObjectReference(DataFlow::TypeTracker::end(), rh).getAPropertyRead()
574+
this = queryRef(request).getAPropertyRead()
552575
}
553576
}
554577

555578
/**
556579
* An access to a header on an Express request.
557580
*/
558581
private class RequestHeaderAccess extends HTTP::RequestHeaderAccess {
559-
RouteHandler rh;
582+
RequestSource request;
560583

561584
RequestHeaderAccess() {
562-
exists(DataFlow::Node request | request = DataFlow::valueNode(rh.getARequestExpr()) |
563-
exists(string methodName |
564-
// `req.get(...)` or `req.header(...)`
565-
this.(DataFlow::MethodCallNode).calls(request, methodName)
566-
|
567-
methodName = "get" or
568-
methodName = "header"
569-
)
570-
or
571-
exists(DataFlow::PropRead headers |
572-
// `req.headers.name`
573-
headers.accesses(request, "headers") and
574-
this = headers.getAPropertyRead()
575-
)
576-
or
577-
exists(string propName | propName = "host" or propName = "hostname" |
578-
// `req.host` and `req.hostname` are derived from headers
579-
this.(DataFlow::PropRead).accesses(request, propName)
580-
)
581-
)
585+
this = request.ref().getAMethodCall(["get", "header"])
586+
or
587+
this = request.ref().getAPropertyRead("headers").getAPropertyRead()
588+
or
589+
this = request.ref().getAPropertyRead(["host", "hostname"])
582590
}
583591

584592
override string getAHeaderName() {
@@ -591,22 +599,15 @@ module Express {
591599
)
592600
}
593601

594-
override RouteHandler getRouteHandler() { result = rh }
602+
override RouteHandler getRouteHandler() { result = request.getRouteHandler() }
595603

596604
override string getKind() { result = "header" }
597605
}
598606

599607
/**
600608
* HTTP headers created by Express calls
601609
*/
602-
abstract private class ExplicitHeader extends HTTP::ExplicitHeaderDefinition {
603-
Expr response;
604-
605-
/**
606-
* Gets the response expression that this header is set on.
607-
*/
608-
Expr getResponse() { result = response }
609-
}
610+
abstract private class ExplicitHeader extends HTTP::ExplicitHeaderDefinition { }
610611

611612
/**
612613
* Holds if `e` is an HTTP request object.
@@ -635,16 +636,13 @@ module Express {
635636
* An invocation of the `redirect` method of an HTTP response object.
636637
*/
637638
private class RedirectInvocation extends HTTP::RedirectInvocation, MethodCallExpr {
638-
RouteHandler rh;
639+
ResponseSource response;
639640

640-
RedirectInvocation() {
641-
getReceiver() = rh.getAResponseExpr() and
642-
getMethodName() = "redirect"
643-
}
641+
RedirectInvocation() { this = response.ref().getAMethodCall("redirect").asExpr() }
644642

645643
override Expr getUrlArgument() { result = getLastArgument() }
646644

647-
override RouteHandler getRouteHandler() { result = rh }
645+
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
648646
}
649647

650648
/**
@@ -662,21 +660,18 @@ module Express {
662660
* An invocation of the `set` or `header` method on an HTTP response object that
663661
* sets multiple headers.
664662
*/
665-
class SetMultipleHeaders extends ExplicitHeader, DataFlow::ValueNode {
666-
override MethodCallExpr astNode;
667-
RouteHandler rh;
663+
class SetMultipleHeaders extends ExplicitHeader, DataFlow::MethodCallNode {
664+
ResponseSource response;
668665

669666
SetMultipleHeaders() {
670-
astNode.getReceiver() = rh.getAResponseExpr() and
671-
response = astNode.getReceiver() and
672-
astNode.getMethodName() = any(string n | n = "set" or n = "header") and
673-
astNode.getNumArgument() = 1
667+
this = response.ref().getAMethodCall(["set", "header"]) and
668+
getNumArgument() = 1
674669
}
675670

676671
/**
677672
* Gets a reference to the multiple headers object that is to be set.
678673
*/
679-
private DataFlow::SourceNode getAHeaderSource() { result.flowsToExpr(astNode.getArgument(0)) }
674+
private DataFlow::SourceNode getAHeaderSource() { result.flowsTo(getArgument(0)) }
680675

681676
override predicate definesExplicitly(string headerName, Expr headerValue) {
682677
exists(string header |
@@ -685,7 +680,7 @@ module Express {
685680
)
686681
}
687682

688-
override RouteHandler getRouteHandler() { result = rh }
683+
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
689684

690685
override Expr getNameExpr() {
691686
exists(DataFlow::PropWrite write | getAHeaderSource().getAPropertyWrite() = write |
@@ -705,31 +700,26 @@ module Express {
705700
* An argument passed to the `send` or `end` method of an HTTP response object.
706701
*/
707702
private class ResponseSendArgument extends HTTP::ResponseSendArgument {
708-
RouteHandler rh;
703+
ResponseSource response;
709704

710-
ResponseSendArgument() {
711-
exists(MethodCallExpr mce |
712-
mce.calls(rh.getAResponseExpr(), "send") and
713-
this = mce.getArgument(0)
714-
)
715-
}
705+
ResponseSendArgument() { this = response.ref().getAMethodCall("send").getArgument(0).asExpr() }
716706

717-
override RouteHandler getRouteHandler() { result = rh }
707+
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
718708
}
719709

720710
/**
721711
* An invocation of the `cookie` method on an HTTP response object.
722712
*/
723713
class SetCookie extends HTTP::CookieDefinition, MethodCallExpr {
724-
RouteHandler rh;
714+
ResponseSource response;
725715

726-
SetCookie() { calls(rh.getAResponseExpr(), "cookie") }
716+
SetCookie() { this = response.ref().getAMethodCall("cookie").asExpr() }
727717

728718
override Expr getNameArgument() { result = getArgument(0) }
729719

730720
override Expr getValueArgument() { result = getArgument(1) }
731721

732-
override RouteHandler getRouteHandler() { result = rh }
722+
override RouteHandler getRouteHandler() { result = response.getRouteHandler() }
733723
}
734724

735725
/**
@@ -750,19 +740,19 @@ module Express {
750740
* An object passed to the `render` method of an HTTP response object.
751741
*/
752742
class TemplateObjectInput extends DataFlow::Node {
753-
RouteHandler rh;
743+
ResponseSource response;
754744

755745
TemplateObjectInput() {
756746
exists(DataFlow::MethodCallNode render |
757-
render.calls(rh.getAResponseExpr().flow(), "render") and
747+
render = response.ref().getAMethodCall("render") and
758748
this = render.getArgument(1)
759749
)
760750
}
761751

762752
/**
763753
* Gets the route handler that uses this object.
764754
*/
765-
RouteHandler getRouteHandler() { result = rh }
755+
RouteHandler getRouteHandler() { result = response.getRouteHandler() }
766756
}
767757

768758
/**

0 commit comments

Comments
 (0)