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 = "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
+
10
91
/**
11
92
* Gets the parameter of kind `kind` of a Connect/Express route handler function.
12
93
*
13
94
* `kind` is one of: "error", "request", "response", "next".
14
95
*/
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 )
28
108
}
29
109
30
110
/**
@@ -34,39 +114,16 @@ module ConnectExpressShared {
34
114
*/
35
115
class RouteHandlerCandidate extends HTTP:: RouteHandlerCandidate {
36
116
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 )
70
127
)
71
128
}
72
129
}
0 commit comments