@@ -139,6 +139,8 @@ module Routing {
139139 predicate mayResumeDispatch ( ) {
140140 this .getLastChild ( ) .mayResumeDispatch ( )
141141 or
142+ isInMiddlewareSetup ( this )
143+ or
142144 exists ( this .( RouteHandler ) .getAContinuationInvocation ( ) )
143145 or
144146 // Leaf nodes that aren't functions are assumed to invoke their continuation
@@ -155,6 +157,8 @@ module Routing {
155157 predicate definitelyResumesDispatch ( ) {
156158 this .getLastChild ( ) .definitelyResumesDispatch ( )
157159 or
160+ isInMiddlewareSetup ( this )
161+ or
158162 exists ( this .( RouteHandler ) .getAContinuationInvocation ( ) )
159163 or
160164 this instanceof MkRouter
@@ -325,6 +329,19 @@ module Routing {
325329 DataFlow:: Node getValueImplicitlyStoredInAccessPath ( int n , string path ) { none ( ) }
326330 }
327331
332+ /**
333+ * Holds if `node` is installed at a route handler that is declared to be a middleware setup,
334+ * and is therefore assume to resume dispatch.
335+ */
336+ private predicate isInMiddlewareSetup ( Node node ) {
337+ exists ( RouteSetup:: Range range |
338+ node = getRouteSetupNode ( range ) and
339+ range .isMiddlewareSetup ( )
340+ )
341+ or
342+ isInMiddlewareSetup ( node .getParent ( ) )
343+ }
344+
328345 /** Holds if `pred` and `succ` are adjacent siblings and `succ` is installed after `pred`. */
329346 private predicate areSiblings ( Node pred , Node succ ) {
330347 exists ( ValueNode:: Range base , int n |
@@ -612,6 +629,20 @@ module Routing {
612629 * Holds if this route setup targets `router` and occurs at the given `cfgNode`.
613630 */
614631 abstract predicate isInstalledAt ( Router:: Range router , ControlFlowNode cfgNode ) ;
632+
633+ /**
634+ * Holds if this is a middleware setup, meaning dispatch will resume after the
635+ * route handlers in this route setup have completed (usually meaning that they have returned a promise, which has resolved).
636+ *
637+ * This should only be overridden when the route setup itself determines whether subsequent
638+ * route handlers are invoked afterwards.
639+ * - For Express-like libraries, the route _handler_ determines whether to resume dispatch,
640+ * based on whether the `next` callback is invoked. For such libraries, do not override `isMiddlewareSetup`.
641+ * - For Fastify-like libraries, the route _setup_ determines whether to resume dispatch.
642+ * For example, `.addHook()` will resume dispatch whereas `.get()` will not. `isMiddlewareSetup()` should thus
643+ * hold for `.addHook()` but not for `.get()` calls.
644+ */
645+ predicate isMiddlewareSetup ( ) { none ( ) }
615646 }
616647
617648 /**
@@ -892,10 +923,14 @@ module Routing {
892923 * based on `Node::Range::getValueAtAccessPath`.
893924 */
894925 private DataFlow:: Node getAnAccessPathRhs ( Node base , int n , string path ) {
895- // Assigned in the body of a route handler function, whi
926+ // Assigned in the body of a route handler function, which is a middleware
896927 exists ( RouteHandler handler | base = handler |
897928 result = AccessPath:: getAnAssignmentTo ( handler .getParameter ( n ) .ref ( ) , path ) and
898- exists ( handler .getAContinuationInvocation ( ) )
929+ (
930+ exists ( handler .getAContinuationInvocation ( ) )
931+ or
932+ isInMiddlewareSetup ( handler )
933+ )
899934 )
900935 or
901936 // Implicit assignment contributed by framework model
0 commit comments