@@ -233,6 +233,53 @@ module HTTP {
233233 ResponseExpr getAResponseExpr ( ) { result .getRouteHandler ( ) = this }
234234 }
235235
236+ /**
237+ * Holds if `call` decorates the function `pred`.
238+ * This means that `call` returns a function that forwards its arguments to `pred`.
239+ * Only holds when the decorator looks like it is decorating a route-handler.
240+ */
241+ private predicate isDecoratedCall ( DataFlow:: CallNode call , DataFlow:: FunctionNode decoratee ) {
242+ // indirect route-handler `result` is given to function `outer`, which returns function `inner` which calls the function `pred`.
243+ exists ( int i , Function outer , Function inner |
244+ decoratee = call .getArgument ( i ) .getALocalSource ( ) and
245+ outer = call .getACallee ( ) and
246+ inner = outer .getAReturnedExpr ( ) and
247+ isAForwardingRouteHandlerCall ( DataFlow:: parameterNode ( outer .getParameter ( i ) ) , inner .flow ( ) )
248+ )
249+ }
250+
251+ /**
252+ * Holds if `f` looks like a route-handler and a call to `callee` inside `f` forwards all of the parameters from `f` to that call.
253+ */
254+ private predicate isAForwardingRouteHandlerCall (
255+ DataFlow:: SourceNode callee , HTTP:: RouteHandlerCandidate f
256+ ) {
257+ exists ( DataFlow:: CallNode call | call = callee .getACall ( ) |
258+ forall ( int arg | arg = [ 0 .. f .getNumParameter ( ) - 1 ] |
259+ f .getParameter ( arg ) .flowsTo ( call .getArgument ( arg ) )
260+ ) and
261+ call .getContainer ( ) = f .getFunction ( )
262+ )
263+ }
264+
265+ /**
266+ * Holds if there exists a step from `pred` to `succ` for a RouteHandler - beyond the usual steps defined by TypeTracking.
267+ */
268+ predicate routeHandlerStep ( DataFlow:: SourceNode pred , DataFlow:: SourceNode succ ) {
269+ isDecoratedCall ( succ , pred )
270+ or
271+ // A forwarding call
272+ isAForwardingRouteHandlerCall ( pred , succ )
273+ or
274+ // a container containing route-handlers.
275+ exists ( HTTP:: RouteHandlerCandidateContainer container | pred = container .getRouteHandler ( succ ) )
276+ or
277+ // (function (req, res) {}).bind(this);
278+ exists ( DataFlow:: PartialInvokeNode call |
279+ succ = call .getBoundFunction ( any ( DataFlow:: Node n | pred .flowsTo ( n ) ) , 0 )
280+ )
281+ }
282+
236283 /**
237284 * An expression that sets up a route on a server.
238285 */
@@ -555,26 +602,51 @@ module HTTP {
555602 create .getArgument ( 0 ) .asExpr ( ) instanceof NullLiteral
556603 )
557604 ) and
558- exists ( RouteHandlerCandidate candidate | candidate .flowsTo ( getAPropertyWrite ( ) .getRhs ( ) ) )
605+ exists ( RouteHandlerCandidate candidate |
606+ getAPossiblyDecoratedHandler ( candidate ) .flowsTo ( getAPropertyWrite ( ) .getRhs ( ) )
607+ )
559608 }
560609
561610 override DataFlow:: SourceNode getRouteHandler ( DataFlow:: SourceNode access ) {
562611 result instanceof RouteHandlerCandidate and
563612 exists ( DataFlow:: PropWrite write , DataFlow:: PropRead read |
564613 access = read and
565614 ref ( this ) .getAPropertyRead ( ) = read and
566- result .flowsTo ( write .getRhs ( ) ) and
615+ getAPossiblyDecoratedHandler ( result ) .flowsTo ( write .getRhs ( ) ) and
567616 write = this .getAPropertyWrite ( )
568617 |
569618 write .getPropertyName ( ) = read .getPropertyName ( )
570619 or
571620 exists ( EnumeratedPropName prop | access = prop .getASourceProp ( ) )
572621 or
573622 read = DataFlow:: lvalueNode ( any ( ForOfStmt stmt ) .getLValue ( ) )
623+ or
624+ // for forwarding calls to an element where the key is determined by the request.
625+ getRequestParameterRead ( ) .flowsToExpr ( read .getPropertyNameExpr ( ) )
574626 )
575627 }
576628 }
577629
630+ /**
631+ * Gets a (chained) property-read/method-call on the request parameter of the route-handler `f`.
632+ */
633+ private DataFlow:: SourceNode getRequestParameterRead ( ) {
634+ result = any ( RouteHandlerCandidate f ) .getParameter ( 0 )
635+ or
636+ result = getRequestParameterRead ( ) .getAPropertyRead ( )
637+ or
638+ result = getRequestParameterRead ( ) .getAMethodCall ( )
639+ }
640+
641+ /**
642+ * Gets a node that is either `candidate`, or a call that decorates `candidate`.
643+ */
644+ DataFlow:: SourceNode getAPossiblyDecoratedHandler ( RouteHandlerCandidate candidate ) {
645+ result = candidate
646+ or
647+ isDecoratedCall ( result , candidate )
648+ }
649+
578650 /**
579651 * A collection that contains one or more route potential handlers.
580652 */
@@ -592,13 +664,14 @@ module HTTP {
592664 }
593665
594666 override DataFlow:: SourceNode getRouteHandler ( DataFlow:: SourceNode access ) {
667+ result instanceof RouteHandlerCandidate and
595668 exists (
596669 DataFlow:: Node input , TypeTrackingPseudoProperty key , CollectionFlowStep store ,
597670 CollectionFlowStep load , DataFlow:: Node storeTo , DataFlow:: Node loadFrom
598671 |
599672 this .flowsTo ( storeTo ) and
600673 store .store ( input , storeTo , key ) and
601- result . ( RouteHandlerCandidate ) .flowsTo ( input ) and
674+ getAPossiblyDecoratedHandler ( result ) .flowsTo ( input ) and
602675 ref ( this ) .flowsTo ( loadFrom ) and
603676 load .load ( loadFrom , access , key )
604677 )
0 commit comments