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 =
21+ [ "request,response" , "request,response,next" , "request,response,next,parameter" ,
22+ "error,request,response,next" ]
23+ }
24+
25+ /** Gets the index of the parameter corresonding to the given `kind`, if any. */
26+ pragma [ noinline]
27+ int getParameterIndex ( string kind ) { this .splitAt ( "," , result ) = kind }
28+
29+ /** Gets the number of parameters taken by this signature. */
30+ pragma [ noinline]
31+ int getArity ( ) { result = count ( getParameterIndex ( _) ) }
32+
33+ /** Holds if this signature takes a parameter of the given kind. */
34+ predicate has ( string kind ) { exists ( getParameterIndex ( kind ) ) }
35+ }
36+
37+ private module RouteHandlerSignature {
38+ /** Gets the signature corresonding to `(req, res, next, param) => {...}`. */
39+ RouteHandlerSignature requestResponseNextParameter ( ) {
40+ result = "request,response,next,parameter"
41+ }
42+
43+ /** Gets the signature corresonding to `(err, req, res, next) => {...}`. */
44+ RouteHandlerSignature errorRequestResponseNext ( ) { result = "error,request,response,next" }
45+ }
46+
47+ /**
48+ * Holds if `fun` appears to match the given signature based on parameter naming.
49+ */
50+ private predicate matchesSignature ( Function function , RouteHandlerSignature sig ) {
51+ function .getNumParameter ( ) = sig .getArity ( ) and
52+ function .getParameter ( sig .getParameterIndex ( "request" ) ) .getName ( ) = [ "req" , "request" ] and
53+ function .getParameter ( sig .getParameterIndex ( "response" ) ) .getName ( ) = [ "res" , "response" ] and
54+ (
55+ sig .has ( "next" )
56+ implies
57+ function .getParameter ( sig .getParameterIndex ( "next" ) ) .getName ( ) = "next"
58+ )
59+ }
60+
61+ /**
62+ * Gets the parameter corresonding to the given `kind`, where `routeHandler` is interpreted as a
63+ * route handler with the signature `sig`.
64+ *
65+ * This does not check if the function is actually a route handler or matches the signature in any way,
66+ * so the caller should restrict the function accordingly.
67+ */
68+ pragma [ inline]
69+ private Parameter getRouteHandlerParameter (
70+ Function routeHandler , RouteHandlerSignature sig , string kind
71+ ) {
72+ result = routeHandler .getParameter ( sig .getParameterIndex ( kind ) )
73+ }
74+
75+ /**
76+ * Gets the parameter of kind `kind` of a Connect/Express route parameter handler function.
77+ *
78+ * `kind` is one of: "error", "request", "response", "next".
79+ */
80+ Parameter getRouteParameterHandlerParameter ( Function routeHandler , string kind ) {
81+ result =
82+ getRouteHandlerParameter ( routeHandler , RouteHandlerSignature:: requestResponseNextParameter ( ) ,
83+ kind )
84+ }
85+
1086 /**
1187 * Gets the parameter of kind `kind` of a Connect/Express route handler function.
1288 *
1389 * `kind` is one of: "error", "request", "response", "next".
1490 */
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- )
91+ Parameter getRouteHandlerParameter ( Function routeHandler , string kind ) {
92+ if routeHandler .getNumParameter ( ) = 4
93+ then
94+ // For arity 4 there is ambiguity between (err, req, res, next) and (req, res, next, param)
95+ // This predicate favors the 'err' signature whereas getRouteParameterHandlerParameter favors the other.
96+ result =
97+ getRouteHandlerParameter ( routeHandler , RouteHandlerSignature:: errorRequestResponseNext ( ) ,
98+ kind )
99+ else
100+ result =
101+ getRouteHandlerParameter ( routeHandler ,
102+ RouteHandlerSignature:: requestResponseNextParameter ( ) , kind )
28103 }
29104
30105 /**
@@ -34,39 +109,16 @@ module ConnectExpressShared {
34109 */
35110 class RouteHandlerCandidate extends HTTP:: RouteHandlerCandidate {
36111 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- )
112+ matchesSignature ( astNode , _) and
113+ not (
114+ // heuristic: not a class method (the server invokes this with a function call)
115+ astNode = any ( MethodDefinition def ) .getBody ( )
116+ or
117+ // heuristic: does not return anything (the server will not use the return value)
118+ exists ( astNode .getAReturnStmt ( ) .getExpr ( ) )
119+ or
120+ // heuristic: is not invoked (the server invokes this at a call site we cannot reason precisely about)
121+ exists ( DataFlow:: InvokeNode cs | cs .getACallee ( ) = astNode )
70122 )
71123 }
72124 }
0 commit comments