Skip to content

Commit 43e5c02

Browse files
committed
add basic support for indirect route handlers
1 parent 3d07ba9 commit 43e5c02

File tree

4 files changed

+51
-10
lines changed

4 files changed

+51
-10
lines changed

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,12 @@ private DataFlow::SourceNode getARouteUsingCookies(DataFlow::TypeTracker t) {
4646
t.start() and
4747
isRouteHandlerUsingCookies(result)
4848
or
49-
exists(DataFlow::TypeTracker t2 | result = getARouteUsingCookies(t2).track(t2, t))
49+
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = getARouteUsingCookies(t2) |
50+
result = pred.track(t2, t)
51+
or
52+
t = t2 and
53+
Express::routeHandlerStep(pred, result)
54+
)
5055
}
5156

5257
/** Gets a data flow node referring to a route handler that uses cookies. */

javascript/ql/src/semmle/javascript/frameworks/Express.qll

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,27 @@ module Express {
7272
result = "del"
7373
}
7474

75+
/**
76+
* Holds if there exists a step from `pred` to `succ` for a RouteHandler - beyond the usual steps defined by TypeTracking.
77+
*/
78+
predicate routeHandlerStep(DataFlow::SourceNode pred, DataFlow::SourceNode succ) {
79+
// indirect route-handler `result` is given to function `outer`, which returns function `inner` which calls the function `pred`.
80+
exists(int i, DataFlow::CallNode call, Function outer, Function inner | call = succ |
81+
pred = call.getArgument(i).getALocalSource() and
82+
outer = call.getACallee() and
83+
inner = outer.getAReturnedExpr() and
84+
exists(DataFlow::CallNode innerCall |
85+
innerCall = DataFlow::parameterNode(outer.getParameter(i)).getACall() and
86+
forall(int arg | arg = [0, 1] |
87+
DataFlow::parameterNode(inner.getParameter(arg)).flowsTo(innerCall.getArgument(arg))
88+
)
89+
)
90+
)
91+
or
92+
// a container containing route-handlers.
93+
exists(HTTP::RouteHandlerCandidateContainer container | pred = container.getRouteHandler(succ))
94+
}
95+
7596
/**
7697
* A call to an Express router method that sets up a route.
7798
*/
@@ -125,9 +146,7 @@ module Express {
125146
exists(DataFlow::TypeBackTracker t2, DataFlow::SourceNode succ | succ = getARouteHandler(t2) |
126147
result = succ.backtrack(t2, t)
127148
or
128-
exists(HTTP::RouteHandlerCandidateContainer container |
129-
result = container.getRouteHandler(succ)
130-
) and
149+
routeHandlerStep(result, succ) and
131150
t = t2
132151
)
133152
}

javascript/ql/test/query-tests/Security/CWE-352/MissingCsrfMiddleware.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
| 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 |
23
| 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 |
34
| 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 |
45
| 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 |
Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
1-
var express = require('express')
2-
var cookieParser = require('cookie-parser')
3-
var passport = require('passport')
1+
var express = require('express');
2+
var cookieParser = require('cookie-parser');
3+
var passport = require('passport');
44

5-
var app = express()
5+
var app = express();
66

7-
app.use(cookieParser())
8-
app.use(passport.authorize({ session: true }))
7+
app.use(cookieParser());
8+
app.use(passport.authorize({ session: true }));
99

1010
app.post('/changeEmail', function (req, res) {
1111
let newEmail = req.cookies["newEmail"];
12+
});
13+
14+
(function () {
15+
var app = express();
16+
17+
app.use(cookieParser());
18+
app.use(passport.authorize({ session: true }));
19+
20+
const errorCatch = (fn) =>
21+
(req, res, next) => {
22+
fn(req, res, next).catch((e) => console.log("Caught " + e));
23+
};
24+
25+
app.post('/changeEmail', errorCatch(async function (req, res) {
26+
let newEmail = req.cookies["newEmail"];
27+
}));
1228
})

0 commit comments

Comments
 (0)