7
7
import javascript
8
8
9
9
module 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
+
10
86
/**
11
87
* Gets the parameter of kind `kind` of a Connect/Express route handler function.
12
88
*
13
89
* `kind` is one of: "error", "request", "response", "next".
14
90
*/
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 )
28
103
}
29
104
30
105
/**
@@ -34,39 +109,16 @@ module ConnectExpressShared {
34
109
*/
35
110
class RouteHandlerCandidate extends HTTP:: RouteHandlerCandidate {
36
111
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 )
70
122
)
71
123
}
72
124
}
0 commit comments