Skip to content

Commit

Permalink
Implement onlyAllowOrigins
Browse files Browse the repository at this point in the history
This funnels into the socket.io `origins` config, but only supports using an array of strings.  It's also stricter in how it matches the origin against the whitelist.
  • Loading branch information
sgress454 committed Nov 3, 2016
1 parent 50bc415 commit 9450c96
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 0 deletions.
21 changes: 21 additions & 0 deletions lib/configure.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
var _ = require('lodash');
var Urls = require('machinepack-urls');
var ERRORPACK = require('./errors');
var checkOriginUrl = require('./util/check-origin-url');



Expand Down Expand Up @@ -47,6 +48,26 @@ module.exports = function ToConfigure(app) {
);
}

// config.sockets.onlyAllowOrigins must be an array.
if (!_.isUndefined(app.config.sockets.onlyAllowOrigins) && !_.isArray(app.config.sockets.onlyAllowOrigins)) {
throw new Error('If `sails.config.sockets.onlyAllowOrigins` is defined, it must be an array of origins.');
}

// Warn if config.sockets.onlyAllowOrigins is not defined in production.
if (_.isUndefined(app.config.sockets.onlyAllowOrigins) && process.env.NODE_ENV==='production') {
app.log.warn('No `sails.config.sockets.onlyAllowOrigins` setting was detected.');
app.log.warn('In a production environment, this setting is recommended for');
app.log.warn('security reasons.\n');
}

// Validate all origins in config.sockets.onlyAllowOrigins.
if (_.isArray(app.config.sockets.onlyAllowOrigins)) {
_.each(app.config.sockets.onlyAllowOrigins, function(origin) {
checkOriginUrl(origin);
});
}


// Adapter options
// =================================

Expand Down
9 changes: 9 additions & 0 deletions lib/initialize.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/

var SocketIO = require('socket.io');
var _ = require('lodash');
var parseSdkMetadata = require('./parse-sdk-metadata');
var ToHandleNewConnection = require('./on-connect');
var ToBuildSocketsMethods = require('./sails.sockets');
Expand Down Expand Up @@ -95,6 +96,14 @@ module.exports = function ToInitialize(app) {
if (app.config.sockets.cookie) {
opts.cookie = app.config.sockets.cookie;
}
if (app.config.sockets.onlyAllowOrigins) {
opts.origins = function(origin, cb) {
if (_.contains(app.config.sockets.onlyAllowOrigins, origin)) {
return cb(null, true);
}
return cb(null, false);
};
}
return opts;
})());

Expand Down
55 changes: 55 additions & 0 deletions lib/util/check-origin-url.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Module dependencies
*/

var _ = require('lodash');
var url = require('url');
var flaverr = require('flaverr');


/**
* checkOriginUrl()
*
* @param {String} originUrl
* The origin URL to check.
* (This is used when parsing the relevant config from within `sails.config.security`
* or `sails.config.sockets`.)
*
* @throws {Error} if not valid
* @property {String} code (==='E_INVALID')
*/

module.exports = function checkOriginUrl(originUrl) {

if (!_.isString(originUrl) || originUrl === '') {
throw flaverr('E_INVALID', new Error('Must specify a non-empty string, but instead got: '+util.inspect(originUrl, {depth: null})));
}

if (!originUrl.match(/^https?:\/\//)) {
throw flaverr('E_INVALID', new Error('Must specify a protocol like http:// or https://, but instead got: '+originUrl));
}

// Now do a mostly-correct parse of the URL.
var parsedOriginUrl = url.parse(originUrl);

var isHttps = parsedOriginUrl.protocol === 'https:';

if (isHttps && parsedOriginUrl.port === '443') {
throw flaverr('E_INVALID', new Error('Should not explicitly specify port 443 with https:// (it is implied). But instead got: '+originUrl));
}
if (!isHttps && parsedOriginUrl.port === '80') {
throw flaverr('E_INVALID', new Error('Should not explicitly specify port 80 with https:// (it is implied). But instead got: '+originUrl));
}

// Ensure there is no path or query string or fragment or anything like that.
if (parsedOriginUrl.pathname !== '/' || parsedOriginUrl.path !== '/') {
throw flaverr('E_INVALID', new Error('Should not specify a path, query string, URL fragment, or anything like that (but instead, got `'+originUrl+'`)'));
}

// Ensure there is no trailing slice
var lastCharacter = originUrl.slice(-1);
if (lastCharacter === '/') {
throw flaverr('E_INVALID', new Error('Should not specify a trailing slash, but instead got: '+originUrl));
}

};
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
],
"dependencies": {
"async": "2.0.1",
"flaverr": "^1.0.0",
"lodash": "3.10.1",
"machinepack-urls": "^3.1.1",
"semver": "4.3.6",
Expand Down

0 comments on commit 9450c96

Please sign in to comment.