Merge pull request #7049 from asgerf/js/routing-trees

Approved by erik-krogh
This commit is contained in:
CodeQL CI
2021-12-17 12:26:38 +00:00
committed by GitHub
43 changed files with 2185 additions and 248 deletions

View File

@@ -0,0 +1,3 @@
import javascript
import semmle.javascript.security.dataflow.TemplateObjectInjectionQuery
import testUtilities.ConsistencyChecking

View File

@@ -1,4 +1,7 @@
nodes
| routes.js:2:23:2:30 | req.body |
| routes.js:2:23:2:30 | req.body |
| routes.js:2:23:2:30 | req.body |
| tst2.js:6:9:6:46 | bodyParameter |
| tst2.js:6:25:6:32 | req.body |
| tst2.js:6:25:6:32 | req.body |
@@ -29,32 +32,33 @@ nodes
| tst2.js:51:25:51:46 | req.bod ... rameter |
| tst2.js:52:28:52:40 | bodyParameter |
| tst2.js:52:28:52:40 | bodyParameter |
| tst.js:5:9:5:46 | bodyParameter |
| tst.js:5:25:5:32 | req.body |
| tst.js:5:25:5:32 | req.body |
| tst.js:5:25:5:46 | req.bod ... rameter |
| tst.js:6:9:6:49 | queryParameter |
| tst.js:6:9:6:49 | queryParameter |
| tst.js:6:26:6:49 | req.que ... rameter |
| tst.js:6:26:6:49 | req.que ... rameter |
| tst.js:6:26:6:49 | req.que ... rameter |
| tst.js:8:28:8:40 | bodyParameter |
| tst.js:8:28:8:40 | bodyParameter |
| tst.js:9:28:9:41 | queryParameter |
| tst.js:9:28:9:41 | queryParameter |
| tst.js:18:19:18:32 | queryParameter |
| tst.js:18:19:18:32 | queryParameter |
| tst.js:21:24:21:26 | obj |
| tst.js:21:24:21:26 | obj |
| tst.js:22:28:22:30 | obj |
| tst.js:22:28:22:30 | obj |
| tst.js:24:11:24:24 | str |
| tst.js:24:17:24:19 | obj |
| tst.js:24:17:24:24 | obj + "" |
| tst.js:27:28:27:42 | JSON.parse(str) |
| tst.js:27:28:27:42 | JSON.parse(str) |
| tst.js:27:39:27:41 | str |
| tst.js:7:9:7:46 | bodyParameter |
| tst.js:7:25:7:32 | req.body |
| tst.js:7:25:7:32 | req.body |
| tst.js:7:25:7:46 | req.bod ... rameter |
| tst.js:8:9:8:49 | queryParameter |
| tst.js:8:9:8:49 | queryParameter |
| tst.js:8:26:8:49 | req.que ... rameter |
| tst.js:8:26:8:49 | req.que ... rameter |
| tst.js:8:26:8:49 | req.que ... rameter |
| tst.js:10:28:10:40 | bodyParameter |
| tst.js:10:28:10:40 | bodyParameter |
| tst.js:11:28:11:41 | queryParameter |
| tst.js:11:28:11:41 | queryParameter |
| tst.js:20:19:20:32 | queryParameter |
| tst.js:20:19:20:32 | queryParameter |
| tst.js:23:24:23:26 | obj |
| tst.js:23:24:23:26 | obj |
| tst.js:24:28:24:30 | obj |
| tst.js:24:28:24:30 | obj |
| tst.js:26:11:26:24 | str |
| tst.js:26:17:26:19 | obj |
| tst.js:26:17:26:24 | obj + "" |
| tst.js:29:28:29:42 | JSON.parse(str) |
| tst.js:29:28:29:42 | JSON.parse(str) |
| tst.js:29:39:29:41 | str |
edges
| routes.js:2:23:2:30 | req.body | routes.js:2:23:2:30 | req.body |
| tst2.js:6:9:6:46 | bodyParameter | tst2.js:7:28:7:40 | bodyParameter |
| tst2.js:6:9:6:46 | bodyParameter | tst2.js:7:28:7:40 | bodyParameter |
| tst2.js:6:25:6:32 | req.body | tst2.js:6:25:6:46 | req.bod ... rameter |
@@ -80,36 +84,37 @@ edges
| tst2.js:51:25:51:32 | req.body | tst2.js:51:25:51:46 | req.bod ... rameter |
| tst2.js:51:25:51:32 | req.body | tst2.js:51:25:51:46 | req.bod ... rameter |
| tst2.js:51:25:51:46 | req.bod ... rameter | tst2.js:51:9:51:46 | bodyParameter |
| tst.js:5:9:5:46 | bodyParameter | tst.js:8:28:8:40 | bodyParameter |
| tst.js:5:9:5:46 | bodyParameter | tst.js:8:28:8:40 | bodyParameter |
| tst.js:5:25:5:32 | req.body | tst.js:5:25:5:46 | req.bod ... rameter |
| tst.js:5:25:5:32 | req.body | tst.js:5:25:5:46 | req.bod ... rameter |
| tst.js:5:25:5:46 | req.bod ... rameter | tst.js:5:9:5:46 | bodyParameter |
| tst.js:6:9:6:49 | queryParameter | tst.js:9:28:9:41 | queryParameter |
| tst.js:6:9:6:49 | queryParameter | tst.js:9:28:9:41 | queryParameter |
| tst.js:6:9:6:49 | queryParameter | tst.js:18:19:18:32 | queryParameter |
| tst.js:6:9:6:49 | queryParameter | tst.js:18:19:18:32 | queryParameter |
| tst.js:6:26:6:49 | req.que ... rameter | tst.js:6:9:6:49 | queryParameter |
| tst.js:6:26:6:49 | req.que ... rameter | tst.js:6:9:6:49 | queryParameter |
| tst.js:6:26:6:49 | req.que ... rameter | tst.js:6:9:6:49 | queryParameter |
| tst.js:6:26:6:49 | req.que ... rameter | tst.js:6:9:6:49 | queryParameter |
| tst.js:18:19:18:32 | queryParameter | tst.js:21:24:21:26 | obj |
| tst.js:18:19:18:32 | queryParameter | tst.js:21:24:21:26 | obj |
| tst.js:21:24:21:26 | obj | tst.js:22:28:22:30 | obj |
| tst.js:21:24:21:26 | obj | tst.js:22:28:22:30 | obj |
| tst.js:21:24:21:26 | obj | tst.js:24:17:24:19 | obj |
| tst.js:24:11:24:24 | str | tst.js:27:39:27:41 | str |
| tst.js:24:17:24:19 | obj | tst.js:24:17:24:24 | obj + "" |
| tst.js:24:17:24:24 | obj + "" | tst.js:24:11:24:24 | str |
| tst.js:27:39:27:41 | str | tst.js:27:28:27:42 | JSON.parse(str) |
| tst.js:27:39:27:41 | str | tst.js:27:28:27:42 | JSON.parse(str) |
| tst.js:7:9:7:46 | bodyParameter | tst.js:10:28:10:40 | bodyParameter |
| tst.js:7:9:7:46 | bodyParameter | tst.js:10:28:10:40 | bodyParameter |
| tst.js:7:25:7:32 | req.body | tst.js:7:25:7:46 | req.bod ... rameter |
| tst.js:7:25:7:32 | req.body | tst.js:7:25:7:46 | req.bod ... rameter |
| tst.js:7:25:7:46 | req.bod ... rameter | tst.js:7:9:7:46 | bodyParameter |
| tst.js:8:9:8:49 | queryParameter | tst.js:11:28:11:41 | queryParameter |
| tst.js:8:9:8:49 | queryParameter | tst.js:11:28:11:41 | queryParameter |
| tst.js:8:9:8:49 | queryParameter | tst.js:20:19:20:32 | queryParameter |
| tst.js:8:9:8:49 | queryParameter | tst.js:20:19:20:32 | queryParameter |
| tst.js:8:26:8:49 | req.que ... rameter | tst.js:8:9:8:49 | queryParameter |
| tst.js:8:26:8:49 | req.que ... rameter | tst.js:8:9:8:49 | queryParameter |
| tst.js:8:26:8:49 | req.que ... rameter | tst.js:8:9:8:49 | queryParameter |
| tst.js:8:26:8:49 | req.que ... rameter | tst.js:8:9:8:49 | queryParameter |
| tst.js:20:19:20:32 | queryParameter | tst.js:23:24:23:26 | obj |
| tst.js:20:19:20:32 | queryParameter | tst.js:23:24:23:26 | obj |
| tst.js:23:24:23:26 | obj | tst.js:24:28:24:30 | obj |
| tst.js:23:24:23:26 | obj | tst.js:24:28:24:30 | obj |
| tst.js:23:24:23:26 | obj | tst.js:26:17:26:19 | obj |
| tst.js:26:11:26:24 | str | tst.js:29:39:29:41 | str |
| tst.js:26:17:26:19 | obj | tst.js:26:17:26:24 | obj + "" |
| tst.js:26:17:26:24 | obj + "" | tst.js:26:11:26:24 | str |
| tst.js:29:39:29:41 | str | tst.js:29:28:29:42 | JSON.parse(str) |
| tst.js:29:39:29:41 | str | tst.js:29:28:29:42 | JSON.parse(str) |
#select
| routes.js:2:23:2:30 | req.body | routes.js:2:23:2:30 | req.body | routes.js:2:23:2:30 | req.body | Template object injection due to $@. | routes.js:2:23:2:30 | req.body | user-provided value |
| tst2.js:7:28:7:40 | bodyParameter | tst2.js:6:25:6:32 | req.body | tst2.js:7:28:7:40 | bodyParameter | Template object injection due to $@. | tst2.js:6:25:6:32 | req.body | user-provided value |
| tst2.js:27:28:27:40 | bodyParameter | tst2.js:26:25:26:32 | req.body | tst2.js:27:28:27:40 | bodyParameter | Template object injection due to $@. | tst2.js:26:25:26:32 | req.body | user-provided value |
| tst2.js:35:28:35:40 | bodyParameter | tst2.js:34:25:34:32 | req.body | tst2.js:35:28:35:40 | bodyParameter | Template object injection due to $@. | tst2.js:34:25:34:32 | req.body | user-provided value |
| tst2.js:43:28:43:40 | bodyParameter | tst2.js:42:25:42:32 | req.body | tst2.js:43:28:43:40 | bodyParameter | Template object injection due to $@. | tst2.js:42:25:42:32 | req.body | user-provided value |
| tst2.js:52:28:52:40 | bodyParameter | tst2.js:51:25:51:32 | req.body | tst2.js:52:28:52:40 | bodyParameter | Template object injection due to $@. | tst2.js:51:25:51:32 | req.body | user-provided value |
| tst.js:8:28:8:40 | bodyParameter | tst.js:5:25:5:32 | req.body | tst.js:8:28:8:40 | bodyParameter | Template object injection due to $@. | tst.js:5:25:5:32 | req.body | user-provided value |
| tst.js:9:28:9:41 | queryParameter | tst.js:6:26:6:49 | req.que ... rameter | tst.js:9:28:9:41 | queryParameter | Template object injection due to $@. | tst.js:6:26:6:49 | req.que ... rameter | user-provided value |
| tst.js:22:28:22:30 | obj | tst.js:6:26:6:49 | req.que ... rameter | tst.js:22:28:22:30 | obj | Template object injection due to $@. | tst.js:6:26:6:49 | req.que ... rameter | user-provided value |
| tst.js:27:28:27:42 | JSON.parse(str) | tst.js:6:26:6:49 | req.que ... rameter | tst.js:27:28:27:42 | JSON.parse(str) | Template object injection due to $@. | tst.js:6:26:6:49 | req.que ... rameter | user-provided value |
| tst.js:10:28:10:40 | bodyParameter | tst.js:7:25:7:32 | req.body | tst.js:10:28:10:40 | bodyParameter | Template object injection due to $@. | tst.js:7:25:7:32 | req.body | user-provided value |
| tst.js:11:28:11:41 | queryParameter | tst.js:8:26:8:49 | req.que ... rameter | tst.js:11:28:11:41 | queryParameter | Template object injection due to $@. | tst.js:8:26:8:49 | req.que ... rameter | user-provided value |
| tst.js:24:28:24:30 | obj | tst.js:8:26:8:49 | req.que ... rameter | tst.js:24:28:24:30 | obj | Template object injection due to $@. | tst.js:8:26:8:49 | req.que ... rameter | user-provided value |
| tst.js:29:28:29:42 | JSON.parse(str) | tst.js:8:26:8:49 | req.que ... rameter | tst.js:29:28:29:42 | JSON.parse(str) | Template object injection due to $@. | tst.js:8:26:8:49 | req.que ... rameter | user-provided value |

View File

@@ -0,0 +1,3 @@
exports.foo = function(req, res) {
res.render('foo', req.body); // NOT OK
}

View File

@@ -1,6 +1,8 @@
var app = require('express')();
app.set('view engine', 'hbs');
app.use(require('body-parser').json());
app.use(require('body-parser').urlencoded({ extended: false }));
app.post('/path', function(req, res) {
var bodyParameter = req.body.bodyParameter;
var queryParameter = req.query.queryParameter;
@@ -25,4 +27,7 @@ function indirect(res, obj) {
res.render("template", str); // OK
res.render("template", JSON.parse(str)); // NOT OK
}
}
let routes = require('./routes');
app.post('/foo', routes.foo);

View File

@@ -2,27 +2,27 @@ const handlebars = require("express-handlebars");
var app = require('express')();
app.engine( '.hbs', handlebars({ defaultLayout: 'main', extname: '.hbs' }) );
app.set('view engine', '.hbs')
app.post('/path', function(req, res) {
app.post('/path', require('body-parser').json(), function(req, res) {
var bodyParameter = req.body.bodyParameter;
res.render('template', bodyParameter); // NOT OK
});
var app2 = require('express')();
app2.post('/path', function(req, res) {
app2.post('/path', require('body-parser').json(), function(req, res) {
var bodyParameter = req.body.bodyParameter;
res.render('template', bodyParameter); // OK
});
var app3 = require('express')();
app3.set('view engine', 'pug');
app3.post('/path', function(req, res) {
app3.post('/path', require('body-parser').json(), function(req, res) {
var bodyParameter = req.body.bodyParameter;
res.render('template', bodyParameter); // OK
});
var app4 = require('express')();
app4.set('view engine', 'ejs');
app4.post('/path', function(req, res) {
app4.post('/path', require('body-parser').json(), function(req, res) {
var bodyParameter = req.body.bodyParameter;
res.render('template', bodyParameter); // NOT OK
});
@@ -30,7 +30,7 @@ app4.post('/path', function(req, res) {
var app5 = require('express')();
app5.engine("foobar", require("consolidate").whiskers);
app5.set('view engine', 'foobar');
app5.post('/path', function(req, res) {
app5.post('/path', require('body-parser').json(), function(req, res) {
var bodyParameter = req.body.bodyParameter;
res.render('template', bodyParameter); // NOT OK
});
@@ -38,7 +38,7 @@ app5.post('/path', function(req, res) {
var app6 = require('express')();
app6.register(".html", require("consolidate").whiskers);
app6.set('view engine', 'html');
app6.post('/path', function(req, res) {
app6.post('/path', require('body-parser').json(), function(req, res) {
var bodyParameter = req.body.bodyParameter;
res.render('template', bodyParameter); // NOT OK
});
@@ -47,7 +47,7 @@ const express = require('express');
var router = express.Router();
var app7 = express();
app7.set('view engine', 'ejs');
router.post('/path', function(req, res) {
router.post('/path', require('body-parser').json(), function(req, res) {
var bodyParameter = req.body.bodyParameter;
res.render('template', bodyParameter); // NOT OK
});

View File

@@ -4,8 +4,11 @@
| 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 |
| 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 |
| 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 |
| fastify2.js:7:16:7:40 | require ... ookie') | This cookie middleware is serving a request handler $@ without CSRF protection. | fastify2.js:24:12:27:3 | async ( ... ody\\n } | here |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |

View File

@@ -98,10 +98,11 @@ var passport = require('passport');
app.use(cookieParser())
app.use(passport.authorize({ session: true }))
function checkToken(req) {
function checkToken(req, res, next) {
if (req.headers.xsrfToken !== req.session.xsrfToken) {
throw new Error("Halt and catch fire!")
}
next();
}
function setCsrfToken(req, response, next) {

View File

@@ -0,0 +1,34 @@
const fastify = require('fastify')
const app = fastify();
app.register(require('fastify-cookie'));
app.register(require('fastify-csrf'));
app.route({
method: 'GET',
path: '/getter',
handler: async (req, reply) => { // OK
return 'hello';
}
})
// unprotected route
app.route({
method: 'POST',
path: '/',
handler: async (req, reply) => { // NOT OK - lacks CSRF protection
req.session.blah;
return req.body
}
})
app.route({
method: 'POST',
path: '/',
onRequest: app.csrfProtection,
handler: async (req, reply) => { // OK - has CSRF protection
return req.body
}
})

View File

@@ -0,0 +1,38 @@
const fastify = require('fastify')
const fp = require('fastify-plugin');
const app = fastify();
function plugin(app) {
app.register(require('fastify-cookie'));
app.register(require('fastify-csrf'));
}
app.register(fp(plugin));
app.route({
method: 'GET',
path: '/getter',
handler: async (req, reply) => { // OK
return 'hello';
}
})
// unprotected route
app.route({
method: 'POST',
path: '/',
handler: async (req, reply) => { // NOT OK - lacks CSRF protection
req.session.blah;
return req.body
}
})
app.route({
method: 'POST',
path: '/',
onRequest: app.csrfProtection,
handler: async (req, reply) => { // OK - has CSRF protection
return req.body
}
})

View File

@@ -0,0 +1,22 @@
const express = require('express')
const cookieParser = require('cookie-parser')
const csrf = require('csurf')
const app = express()
app.use(cookieParser())
app.post('/unsafe', (req, res) => { // NOT OK
req.cookies.x;
});
function middlewares() {
return express.Router()
.use(csrf({ cookie: true}))
.use('/', express.bodyParser());
}
app.use(middlewares());
app.post('/safe', (req, res) => { // OK
req.cookies.x;
});

View File

@@ -1,4 +1,4 @@
| MissingRateLimiting.js:4:19:4:38 | functio ... ath);\\n} | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:7:5:7:22 | res.sendFile(path) | a file system access |
| MissingRateLimiting.js:4:19:8:1 | functio ... ath);\\n} | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:7:5:7:22 | res.sendFile(path) | a file system access |
| MissingRateLimiting.js:25:19:25:20 | f1 | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:13:5:13:22 | res.sendFile(path) | a file system access |
| MissingRateLimiting.js:25:27:25:28 | f3 | This route handler performs $@, but is not rate-limited. | MissingRateLimiting.js:22:5:22:22 | res.sendFile(path) | a file system access |
| tst.js:22:24:22:40 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
@@ -8,3 +8,4 @@
| tst.js:38:20:38:36 | expensiveHandler4 | This route handler performs $@, but is not rate-limited. | tst.js:17:40:17:83 | connect ... ution') | a database access |
| tst.js:64:25:64:63 | functio ... req); } | This route handler performs $@, but is not rate-limited. | tst.js:64:46:64:60 | verifyUser(req) | authorization |
| tst.js:76:25:76:53 | catchAs ... ndler1) | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |
| tst.js:88:24:88:40 | expensiveHandler1 | This route handler performs $@, but is not rate-limited. | tst.js:14:40:14:46 | login() | authorization |

View File

@@ -58,7 +58,7 @@ app2.get('/:path', bruteforce.prevent, expensiveHandler1); // OK
// rate limiting using express-limiter
var app3 = express();
var limiter = require('express-limiter')(app3);
require('express-limiter')(app3)({ method: 'get', path: '/' });
app3.get('/:path', expensiveHandler1); // OK
express().get('/:path', function(req, res) { verifyUser(req); }); // NOT OK
@@ -77,3 +77,14 @@ express().get('/:path', catchAsync(expensiveHandler1)); // NOT OK
express().get('/:path', rateLimiterMiddleware, catchAsync(expensiveHandler1)); // OK
express().get('/:path', catchAsync(rateLimiterMiddleware), expensiveHandler1); // OK
express().get('/:path', catchAsync(rateLimiterMiddleware), catchAsync(expensiveHandler1)); // OK
function errorHandler(req, res, next) {
next(makeOAuthError(req, res));
}
express().use(errorHandler); // OK - does not perform authentication
const fastifyApp = require('fastify')();
fastifyApp.get('/foo', expensiveHandler1); // NOT OK
fastifyApp.register(require('fastify-rate-limit'));
fastifyApp.get('/bar', expensiveHandler1); // OK