Skip to content

Commit b732193

Browse files
committed
JS: Improve precision of missing CSRF middleware
1 parent d0e94e6 commit b732193

File tree

2 files changed

+45
-22
lines changed

2 files changed

+45
-22
lines changed

javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,37 @@ DataFlow::SourceNode csrfMiddlewareCreation() {
7272
result = Fastify::server().getAPropertyRead("csrfProtection")
7373
}
7474

75+
/** Holds if the given property has a name indicating that it refers to a CSRF token. */
76+
pragma[nomagic]
77+
private predicate isCsrfProperty(DataFlow::PropRef ref) {
78+
ref.getPropertyName().regexpMatch("(?i).*(csrf|xsrf).*")
79+
}
80+
7581
/**
7682
* Gets a data flow node that flows to the base of a reference to `cookies`, `session`, or `user`,
77-
* where the referenced property has `csrf` or `xsrf` in its name,
78-
* and a property is either written or part of a comparison.
83+
* of which a property appears to be used in a CSRF token check.
7984
*/
8085
private DataFlow::SourceNode nodeLeadingToCsrfWriteOrCheck(DataFlow::TypeBackTracker t) {
8186
t.start() and
8287
exists(DataFlow::PropRef ref |
83-
ref = result.getAPropertyRead(cookieProperty()).getAPropertyReference() and
84-
ref.getPropertyName().regexpMatch("(?i).*(csrf|xsrf).*")
88+
ref = result.getAPropertyRead(cookieProperty()).getAPropertyReference()
8589
|
86-
ref instanceof DataFlow::PropWrite or
87-
ref.(DataFlow::PropRead).asExpr() = any(EqualityTest c).getAnOperand()
90+
// Assignment to property with csrf/xsrf in the name
91+
ref instanceof DataFlow::PropWrite and
92+
isCsrfProperty(ref)
93+
or
94+
// Comparison where one of the properties has csrf/xsrf in the name
95+
exists(EqualityTest test |
96+
test.getAnOperand().flow().getALocalSource() = ref and
97+
isCsrfProperty(test.getAnOperand().flow().getALocalSource())
98+
)
99+
or
100+
// Comparison via a call, where one of the properties has csrf/xsrf in the name
101+
exists(DataFlow::CallNode call |
102+
call.getCalleeName().regexpMatch("(?i).*(check|verify|valid|equal).*") and
103+
call.getAnArgument().getALocalSource() = ref and
104+
isCsrfProperty(call.getAnArgument().getALocalSource())
105+
)
88106
)
89107
or
90108
exists(DataFlow::TypeBackTracker t2 | result = nodeLeadingToCsrfWriteOrCheck(t2).backtrack(t2, t))
@@ -128,21 +146,26 @@ predicate hasCsrfMiddleware(Routing::RouteHandler handler) {
128146
handler.isGuardedByNode(getACsrfMiddleware())
129147
}
130148

131-
from Routing::RouteSetup setup, Routing::RouteHandler handler, HTTP::CookieMiddlewareInstance cookie
149+
from
150+
Routing::RouteSetup setup, Routing::Node setupArg, Routing::RouteHandler handler,
151+
HTTP::CookieMiddlewareInstance cookie
132152
where
133153
// Require that the handler uses cookies and has cookie middleware.
134154
//
135155
// In practice, handlers that use cookies always have the cookie middleware or
136156
// the handler wouldn't work. However, if we can't find the cookie middleware, it
137157
// indicates that our middleware model is too incomplete, so in that case we
138158
// don't trust it to detect the presence of CSRF middleware either.
139-
setup.getAChild*() = handler and
159+
setup.getAChild() = setupArg and
160+
setupArg.getAChild*() = handler and
140161
isRouteHandlerUsingCookies(handler) and
141162
hasCookieMiddleware(handler, cookie) and
142163
// Only flag the cookie parser registered first.
143164
not hasCookieMiddleware(Routing::getNode(cookie), _) and
144165
not hasCsrfMiddleware(handler) and
166+
// Sometimes the CSRF protection comes later in the same route setup.
167+
not setup.getAChild*() = getACsrfMiddleware() and
145168
// Only warn for dangerous handlers, such as for POST and PUT.
146169
setup.getOwnHttpMethod().isUnsafe()
147170
select cookie, "This cookie middleware is serving a request handler $@ without CSRF protection.",
148-
setup, "here"
171+
setupArg, "here"
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
| MissingCsrfMiddlewareBad.js:7:9:7:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:10:1:12:2 | app.pos ... l"];\\n}) | here |
2-
| MissingCsrfMiddlewareBad.js:17:13:17:26 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:25:5:27:7 | app.pos ... })) | here |
3-
| MissingCsrfMiddlewareBad.js:33:13:33:26 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:41:5:43:7 | app.pos ... })) | here |
4-
| MissingCsrfMiddlewareBad.js:33:13:33:26 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:45:5:47:7 | app.pos ... })) | here |
5-
| csurf_api_example.js:42:37:42:50 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | csurf_api_example.js:42:3:45:4 | router. ... ')\\n }) | here |
6-
| csurf_example.js:18:9:18:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | csurf_example.js:31:1:34:2 | app.pos ... ed')\\n}) | here |
7-
| fastify.js:5:14:5:38 | require ... ookie') | This cookie middleware is serving a request handler $@ without CSRF protection. | fastify.js:17:1:24:2 | app.rou ... \\n }\\n}) | here |
8-
| lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:26:1:29:2 | app.pos ... ed')\\n}) | here |
9-
| lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:31:1:34:2 | app.pos ... ed')\\n}) | here |
10-
| tst.js:6:9:6:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | tst.js:8:1:10:2 | app.pos ... s.x;\\n}) | here |
11-
| unused_cookies.js:6:9:6:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | unused_cookies.js:8:1:13:2 | app.pos ... k');\\n}) | here |
12-
| unused_cookies.js:6:9:6:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | unused_cookies.js:29:1:32:2 | app.pos ... k');\\n}) | here |
13-
| unused_cookies.js:6:9:6:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | unused_cookies.js:34:1:37:2 | app.pos ... k');\\n}) | here |
1+
| MissingCsrfMiddlewareBad.js:7:9:7:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:10:26:12:1 | functio ... il"];\\n} | here |
2+
| MissingCsrfMiddlewareBad.js:17:13:17:26 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:25:30:27:6 | errorCa ... \\n }) | here |
3+
| MissingCsrfMiddlewareBad.js:33:13:33:26 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:41:30:43:6 | errorCa ... \\n }) | here |
4+
| MissingCsrfMiddlewareBad.js:33:13:33:26 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | MissingCsrfMiddlewareBad.js:45:31:47:6 | errorCa ... \\n }) | here |
5+
| csurf_api_example.js:42:37:42:50 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | csurf_api_example.js:42:53:45:3 | functio ... e')\\n } | here |
6+
| csurf_example.js:18:9:18:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | csurf_example.js:31:40:34:1 | functio ... sed')\\n} | here |
7+
| fastify.js:5:14:5:38 | require ... ookie') | This cookie middleware is serving a request handler $@ without CSRF protection. | fastify.js:20:12:23:3 | async ( ... ody\\n } | here |
8+
| lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:26:42:29:1 | functio ... sed')\\n} | here |
9+
| lusca_example.js:9:9:9:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | lusca_example.js:31:40:34:1 | functio ... sed')\\n} | here |
10+
| tst.js:6:9:6:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | tst.js:8:21:10:1 | (req, r ... es.x;\\n} | here |
11+
| unused_cookies.js:6:9:6:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | unused_cookies.js:8:34:13:1 | (req, r ... Ok');\\n} | here |
12+
| unused_cookies.js:6:9:6:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | unused_cookies.js:29:19:32:1 | (req, r ... Ok');\\n} | here |
13+
| unused_cookies.js:6:9:6:22 | cookieParser() | This cookie middleware is serving a request handler $@ without CSRF protection. | unused_cookies.js:34:22:37:1 | (req, r ... Ok');\\n} | here |

0 commit comments

Comments
 (0)