77import javascript
88
99module ConnectExpressShared {
10+ /**
11+ * String representing the signature of a route handler, that is,
12+ * the list of parameters taken by the route handler.
13+ *
14+ * Concretely this is a comma-separated list of parameter kinds, which can be either
15+ * `request`, `response`, `next`, `error`, or `parameter`, but this is considered an
16+ * implementation detail.
17+ */
18+ private class RouteHandlerSignature extends string {
19+ RouteHandlerSignature ( ) {
20+ this = "request,response" or
21+ this = "request,response,next" or
22+ this = "request,response,next,parameter" or
23+ this = "error,request,response,next"
24+ }
25+
26+ /** Gets the index of the parameter corresonding to the given `kind`, if any. */
27+ pragma [ noinline]
28+ int getParameterIndex ( string kind ) { this .splitAt ( "," , result ) = kind }
29+
30+ /** Gets the number of parameters taken by this signature. */
31+ pragma [ noinline]
32+ int getArity ( ) { result = count ( getParameterIndex ( _) ) }
33+
34+ /** Holds if this signature takes a parameter of the given kind. */
35+ predicate has ( string kind ) { exists ( getParameterIndex ( kind ) ) }
36+ }
37+
38+ private module RouteHandlerSignature {
39+ /** Gets the signature corresonding to `(req, res, next, param) => {...}`. */
40+ RouteHandlerSignature requestResponseNextParameter ( ) {
41+ result = "request,response,next,parameter"
42+ }
43+
44+ /** Gets the signature corresonding to `(req, res, next) => {...}`. */
45+ RouteHandlerSignature requestResponseNext ( ) { result = "request,response,next" }
46+
47+ /** Gets the signature corresonding to `(err, req, res, next) => {...}`. */
48+ RouteHandlerSignature errorRequestResponseNext ( ) { result = "error,request,response,next" }
49+ }
50+
51+ /**
52+ * Holds if `fun` appears to match the given signature based on parameter naming.
53+ */
54+ private predicate matchesSignature ( Function function , RouteHandlerSignature sig ) {
55+ function .getNumParameter ( ) = sig .getArity ( ) and
56+ function .getParameter ( sig .getParameterIndex ( "request" ) ) .getName ( ) = [ "req" , "request" ] and
57+ function .getParameter ( sig .getParameterIndex ( "response" ) ) .getName ( ) = [ "res" , "response" ] and
58+ (
59+ sig .has ( "next" )
60+ implies
61+ function .getParameter ( sig .getParameterIndex ( "next" ) ) .getName ( ) = [ "next" , "cb" ]
62+ )
63+ }
64+
65+ /**
66+ * Gets the parameter corresonding to the given `kind`, where `routeHandler` is interpreted as a
67+ * route handler with the signature `sig`.
68+ *
69+ * This does not check if the function is actually a route handler or matches the signature in any way,
70+ * so the caller should restrict the function accordingly.
71+ */
72+ pragma [ inline]
73+ private Parameter getRouteHandlerParameter (
74+ Function routeHandler , RouteHandlerSignature sig , string kind
75+ ) {
76+ result = routeHandler .getParameter ( sig .getParameterIndex ( kind ) )
77+ }
78+
79+ /**
80+ * Gets the parameter of kind `kind` of a Connect/Express route parameter handler function.
81+ *
82+ * `kind` is one of: "error", "request", "response", "next".
83+ */
84+ pragma [ inline]
85+ Parameter getRouteParameterHandlerParameter ( Function routeHandler , string kind ) {
86+ result =
87+ getRouteHandlerParameter ( routeHandler , RouteHandlerSignature:: requestResponseNextParameter ( ) ,
88+ kind )
89+ }
90+
1091 /**
1192 * Gets the parameter of kind `kind` of a Connect/Express route handler function.
1293 *
1394 * `kind` is one of: "error", "request", "response", "next".
1495 */
15- SimpleParameter getRouteHandlerParameter ( Function routeHandler , string kind ) {
16- exists ( int index , int offset |
17- result = routeHandler .getParameter ( index + offset ) and
18- ( if routeHandler .getNumParameter ( ) = 4 then offset = 0 else offset = - 1 )
19- |
20- kind = "error" and index = 0
21- or
22- kind = "request" and index = 1
23- or
24- kind = "response" and index = 2
25- or
26- kind = "next" and index = 3
27- )
96+ pragma [ inline]
97+ Parameter getRouteHandlerParameter ( Function routeHandler , string kind ) {
98+ if routeHandler .getNumParameter ( ) = 4
99+ then
100+ // For arity 4 there is ambiguity between (err, req, res, next) and (req, res, next, param)
101+ // This predicate favors the 'err' signature whereas getRouteParameterHandlerParameter favors the other.
102+ result =
103+ getRouteHandlerParameter ( routeHandler , RouteHandlerSignature:: errorRequestResponseNext ( ) ,
104+ kind )
105+ else
106+ result =
107+ getRouteHandlerParameter ( routeHandler , RouteHandlerSignature:: requestResponseNext ( ) , kind )
28108 }
29109
30110 /**
@@ -34,39 +114,16 @@ module ConnectExpressShared {
34114 */
35115 class RouteHandlerCandidate extends HTTP:: RouteHandlerCandidate {
36116 RouteHandlerCandidate ( ) {
37- exists ( string request , string response , string next , string error |
38- ( request = "request" or request = "req" ) and
39- ( response = "response" or response = "res" ) and
40- next = "next" and
41- ( error = "error" or error = "err" )
42- |
43- // heuristic: parameter names match the documentation
44- astNode .getNumParameter ( ) >= 2 and
45- getRouteHandlerParameter ( astNode , "request" ) .getName ( ) = request and
46- getRouteHandlerParameter ( astNode , "response" ) .getName ( ) = response and
47- (
48- astNode .getNumParameter ( ) >= 3
49- implies
50- getRouteHandlerParameter ( astNode , "next" ) .getName ( ) = next
51- ) and
52- (
53- astNode .getNumParameter ( ) = 4
54- implies
55- getRouteHandlerParameter ( astNode , "error" ) .getName ( ) = error
56- ) and
57- not (
58- // heuristic: max four parameters (the server will only supply four arguments)
59- astNode .getNumParameter ( ) > 4
60- or
61- // heuristic: not a class method (the server invokes this with a function call)
62- astNode = any ( MethodDefinition def ) .getBody ( )
63- or
64- // heuristic: does not return anything (the server will not use the return value)
65- exists ( astNode .getAReturnStmt ( ) .getExpr ( ) )
66- or
67- // heuristic: is not invoked (the server invokes this at a call site we cannot reason precisely about)
68- exists ( DataFlow:: InvokeNode cs | cs .getACallee ( ) = astNode )
69- )
117+ matchesSignature ( astNode , _) and
118+ not (
119+ // heuristic: not a class method (the server invokes this with a function call)
120+ astNode = any ( MethodDefinition def ) .getBody ( )
121+ or
122+ // heuristic: does not return anything (the server will not use the return value)
123+ exists ( astNode .getAReturnStmt ( ) .getExpr ( ) )
124+ or
125+ // heuristic: is not invoked (the server invokes this at a call site we cannot reason precisely about)
126+ exists ( DataFlow:: InvokeNode cs | cs .getACallee ( ) = astNode )
70127 )
71128 }
72129 }
0 commit comments