Skip to content

Commit

Permalink
sails.config.csrf.routesDisabled support RegExp and express-style path
Browse files Browse the repository at this point in the history
  • Loading branch information
bolasblack committed May 10, 2016
1 parent cee2e64 commit d3f634c
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 4 deletions.
26 changes: 24 additions & 2 deletions lib/hooks/csrf/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ module.exports = function(sails) {
*/

var _ = require('lodash'),
util = require('sails-util');
util = require('sails-util'),
pathToRegexp = require('path-to-regexp');

/**
* Expose hook definition
Expand Down Expand Up @@ -69,14 +70,35 @@ module.exports = function(sails) {
// Quick trim function--could move this into sails.util at some point
function trim (str) {return str.trim();}

var disabledRoutes;
if (Array.isArray(sails.config.csrf.routesDisabled)) {
disabledRoutes = sails.config.csrf.routesDisabled;
} else if (_.isRegExp(sails.config.csrf.routesDisabled)) {
disabledRoutes = [sails.config.csrf.routesDisabled];
} else {
disabledRoutes = sails.config.csrf.routesDisabled.split(',').map(trim);
}

var disabledRouteCheckers = disabledRoutes.map(function(route) {
var parsedRegexp;
if (_.isString(route)) {
parsedRegexp = pathToRegexp(route, []);
return function(req) { return parsedRegexp.exec(req.path) };
} else if (_.isRegExp(route)) {
return function(req) { return route.test(req.path) };
} else {
return function() { return false };
}
});

// Add res.view() method to compatible middleware
sails.on('router:before', function () {

sails.router.bind('/*', function(req, res, next) {

var allowCrossOriginCSRF = sails.config.csrf.origin.split(',').map(trim).indexOf(req.headers.origin) > -1;

var isRouteDisabled = sails.config.csrf.routesDisabled.split(',').map(trim).indexOf(req.path) > -1;
var isRouteDisabled = disabledRouteCheckers.some(function(checker) { return checker(req); })

// Start with a clear _csrf template token
res.locals._csrf = null;
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
"mock-req": "0.2.0",
"mock-res": "0.3.0",
"parseurl": "1.3.1",
"path-to-regexp": "1.2.1",
"pluralize": "1.2.1",
"prompt": "0.2.14",
"rc": "1.0.1",
Expand Down
138 changes: 136 additions & 2 deletions test/integration/hook.cors_csrf.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1101,10 +1101,10 @@ describe('CORS and CSRF ::', function() {

});

describe("with CSRF set to {protectionEnabled: true, routesDisabled: '/foo, /user'}", function() {
describe("with CSRF set to {protectionEnabled: true, routesDisabled: '/foo/:id, /user'}", function() {

before(function() {
fs.writeFileSync(path.resolve('../', appName, 'config/csrf.js'), "module.exports.csrf = {protectionEnabled: true, routesDisabled: '/user'};");
fs.writeFileSync(path.resolve('../', appName, 'config/csrf.js'), "module.exports.csrf = {protectionEnabled: true, routesDisabled: '/foo/:id, /user'};");
});

it("a POST request on /user without a CSRF token should result in a 201 response", function(done) {
Expand All @@ -1115,7 +1115,16 @@ describe('CORS and CSRF ::', function() {
assert.equal(response.statusCode, 201);
done();
});
});

it("a POST request on /foo/12 without a CSRF token should result in a 404 response", function(done) {
httpHelper.testRoute("post", 'foo/12', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 404);
done();
});
});

it("a POST request on /test without a CSRF token should result in a 403 response", function(done) {
Expand All @@ -1126,7 +1135,132 @@ describe('CORS and CSRF ::', function() {
assert.equal(response.statusCode, 403);
done();
});
});

it("a POST request on /foo without a CSRF token should result in a 403 response", function(done) {
httpHelper.testRoute("post", 'foo', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 403);
done();
});
});

});

describe("with CSRF set to {protectionEnabled: true, routesDisabled: /user\\/\\d+/}", function() {

before(function() {
fs.writeFileSync(path.resolve('../', appName, 'config/csrf.js'), "module.exports.csrf = {protectionEnabled: true, routesDisabled: /user\\/\\d+/};");
});

it("a POST request on /user/1 without a CSRF token should result in a 200 response", function(done) {
httpHelper.testRoute("post", 'user/1', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 200);
done();
});
});

it("a POST request on /user/a without a CSRF token should result in a 403 response", function(done) {
httpHelper.testRoute("post", 'user/a', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 403);
done();
});
});

it("a POST request on /user without a CSRF token should result in a 403 response", function(done) {
httpHelper.testRoute("post", 'user', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 403);
done();
});
});

});

describe("with CSRF set to {protectionEnabled: true, routesDisabled: ['/foo/:id', '/bar/foo', /user\\/\\d+/]}", function() {

before(function() {
fs.writeFileSync(path.resolve('../', appName, 'config/csrf.js'), "module.exports.csrf = {protectionEnabled: true, routesDisabled: ['/foo/:id', '/bar/foo', /user\\/\\d+/]};");
});

it("a POST request on /foo/12 without a CSRF token should result in a 404 response", function(done) {
httpHelper.testRoute("post", 'foo/12', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 404);
done();
});
});

it("a POST request on /bar/foo without a CSRF token should result in a 404 response", function(done) {
httpHelper.testRoute("post", 'bar/foo', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 404);
done();
});
});

it("a POST request on /user/1 without a CSRF token should result in a 200 response", function(done) {
httpHelper.testRoute("post", 'user/1', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 200);
done();
});
});

it("a POST request on /user/a without a CSRF token should result in a 403 response", function(done) {
httpHelper.testRoute("post", 'user/a', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 403);
done();
});
});

it("a POST request on /foo without a CSRF token should result in a 403 response", function(done) {
httpHelper.testRoute("post", 'foo', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 403);
done();
});
});

it("a POST request on /user without a CSRF token should result in a 403 response", function(done) {
httpHelper.testRoute("post", 'user', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 403);
done();
});
});

it("a POST request on /test without a CSRF token should result in a 403 response", function(done) {
httpHelper.testRoute("post", 'test', function(err, response) {
if (err) {
return done(err);
}
assert.equal(response.statusCode, 403);
done();
});
});

});
Expand Down

0 comments on commit d3f634c

Please sign in to comment.