|
12 | 12 |
|
13 | 13 | import javascript |
14 | 14 |
|
| 15 | +/** Gets a data flow node that flows to the base of an access to `cookies` or `session`. */ |
| 16 | +private DataFlow::SourceNode nodeLeadingToCookieAccess(DataFlow::TypeBackTracker t) { |
| 17 | + t.start() and |
| 18 | + exists(string name | name = "session" or name = "cookies" | |
| 19 | + exists(result.getAPropertyRead(name)) |
| 20 | + ) |
| 21 | + or |
| 22 | + exists(DataFlow::TypeBackTracker t2 | |
| 23 | + result = nodeLeadingToCookieAccess(t2).backtrack(t2, t) |
| 24 | + ) |
| 25 | +} |
| 26 | + |
| 27 | +/** Gets a data flow node that flows to the base of an access to `cookies` or `session`. */ |
| 28 | +DataFlow::SourceNode nodeLeadingToCookieAccess() { |
| 29 | + result = nodeLeadingToCookieAccess(DataFlow::TypeBackTracker::end()) |
| 30 | +} |
| 31 | + |
| 32 | +/** |
| 33 | + * Holds if `handler` uses cookies. |
| 34 | + */ |
| 35 | +predicate isRouteHandlerUsingCookies(Express::RouteHandler handler) { |
| 36 | + DataFlow::parameterNode(handler.getRequestParameter()) = nodeLeadingToCookieAccess() |
| 37 | +} |
| 38 | + |
| 39 | +/** Gets a data flow node referring to a route handler that uses cookies. */ |
| 40 | +private DataFlow::SourceNode getARouteUsingCookies(DataFlow::TypeTracker t) { |
| 41 | + t.start() and |
| 42 | + isRouteHandlerUsingCookies(result) |
| 43 | + or |
| 44 | + exists(DataFlow::TypeTracker t2 | |
| 45 | + result = getARouteUsingCookies(t2).track(t2, t) |
| 46 | + ) |
| 47 | +} |
| 48 | + |
| 49 | +/** Gets a data flow node referring to a route handler that uses cookies. */ |
| 50 | +DataFlow::SourceNode getARouteUsingCookies() { |
| 51 | + result = getARouteUsingCookies(DataFlow::TypeTracker::end()) |
| 52 | +} |
| 53 | + |
15 | 54 | /** |
16 | 55 | * Checks if `expr` is preceded by the cookie middleware `cookie`. |
17 | 56 | * |
@@ -63,11 +102,23 @@ from |
63 | 102 | where |
64 | 103 | router = setup.getRouter() and |
65 | 104 | handler = setup.getARouteHandlerExpr() and |
| 105 | + |
| 106 | + // Require that the handler uses cookies and has cookie middleware. |
| 107 | + // |
| 108 | + // In practice, handlers that use cookies always have the cookie middleware or |
| 109 | + // the handler wouldn't work. However, if we can't find the cookie middleware, it |
| 110 | + // indicates that our middleware model is too incomplete, so in that case we |
| 111 | + // don't trust it to detect the presence of CSRF middleware either. |
| 112 | + getARouteUsingCookies().flowsToExpr(handler.getPreviousMiddleware*()) and |
66 | 113 | hasCookieMiddleware(handler, cookie) and |
| 114 | + |
| 115 | + // Only flag the first cookie parser registered first. |
| 116 | + not hasCookieMiddleware(cookie, _) and |
| 117 | + |
67 | 118 | not hasCsrfMiddleware(handler) and |
68 | 119 | // Only warn for the last handler in a chain. |
69 | 120 | handler.isLastHandler() and |
70 | | - // Only warn for dangerous for handlers, such as for POST and PUT. |
| 121 | + // Only warn for dangerous handlers, such as for POST and PUT. |
71 | 122 | not setup.getRequestMethod().isSafe() |
72 | 123 | select cookie, "This cookie middleware is serving a request handler $@ without CSRF protection.", |
73 | 124 | handler, "here" |
0 commit comments