Comparing version 1.0.2 to 1.1.0
45
index.js
'use strict'; | ||
/** | ||
* Fi Aegis. | ||
* | ||
* @module fi-aegis | ||
* | ||
* @see module:fi-aegis | ||
*/ | ||
/** | ||
* Configures the module. | ||
* | ||
* @param {Object} options The options object. | ||
* @param {Object} options.csp The options for the `csp` module. | ||
* @param {Object} options.csrf The options for the `csrf` module. | ||
* @param {Object} options.hsts The options for the `hsts` module. | ||
* @param {Boolean} options.nosniff Whether to activate the `nosniff` module. | ||
* @param {String} options.p3p The header value for the `p3p` module. | ||
* @param {String} options.xframes The header value for the `xframe` module. | ||
* @param {Object} options.xssprotection The options for the `xssprotection` | ||
* module. | ||
* | ||
* @returns {Function} The Express middleware. | ||
*/ | ||
var aegis = module.exports = options => { | ||
var headers = []; | ||
const components = []; | ||
if (options) { | ||
Object.keys(aegis).forEach(key => { | ||
var config = options[key]; | ||
let config = options[key]; | ||
if (config) { | ||
headers.push(aegis[key](config)); | ||
components.push(aegis[key](config)); | ||
} | ||
@@ -26,11 +50,9 @@ }); | ||
headers.forEach(header => { | ||
chain = (next => { | ||
return err => { | ||
if (err) { | ||
return next(err); | ||
} | ||
components.forEach(component => { | ||
chain = (next => err => { | ||
if (err) { | ||
return next(err); | ||
} | ||
header(req, res, next); | ||
}; | ||
component(req, res, next); | ||
})(chain); | ||
@@ -43,2 +65,3 @@ }); | ||
return middleware; | ||
}; | ||
@@ -45,0 +68,0 @@ |
/** | ||
* Content Security Policy (CSP). | ||
* CSP module. | ||
* | ||
* @module fi-aegis/csp | ||
* | ||
* @see module:fi-aegis/csp | ||
* @see https://www.owasp.org/index.php/Content_Security_Policy | ||
@@ -27,13 +30,22 @@ */ | ||
/** | ||
* Module export function. | ||
* Configures the CSP module. | ||
* | ||
* @param {Object} options The CSP policy options. | ||
* @param {Boolean} options.reportOnly Whether to only work in report mode. | ||
* @param {String} options.reportUri The report URI. | ||
* @param {Object|Array|String} options.policy The CSP policy. | ||
* | ||
* @returns {Function} The CSP middleware. | ||
* @returns {Function} The CSP Express middleware. | ||
*/ | ||
function csp(options) { | ||
var isReportOnly = options && options.reportOnly; | ||
var reportUri = options && options.reportUri; | ||
var policyRules = options && options.policy; | ||
var reportUri, policyRules; | ||
var isReportOnly = false; | ||
if (options) { | ||
isReportOnly = options.reportOnly; | ||
reportUri = options.reportUri; | ||
policyRules = options.policy; | ||
} | ||
name = 'content-security-policy'; | ||
@@ -48,2 +60,4 @@ | ||
if (reportUri) { | ||
console.warn('The `report-uri` directive has been deprecated. See https://developer.mozilla.org/docs/Web/HTTP/Headers/Content-Security-Policy/report-uri'); | ||
if (value !== '') { | ||
@@ -50,0 +64,0 @@ value += '; '; |
105
lib/csrf.js
/** | ||
* CSRF | ||
* CSRF module. | ||
* | ||
* @module fi-aegis/csrf | ||
* | ||
* @see module:fi-aegis/csrf | ||
* @see https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF) | ||
@@ -13,4 +16,4 @@ */ | ||
var config = { | ||
safeVerbs: ['OPTIONS', 'HEAD', 'GET'], | ||
const CONFIG = { | ||
safeVerbs: null, | ||
cookie: null, | ||
@@ -32,4 +35,4 @@ header: null, | ||
function getCsrf(req, secret) { | ||
var csrf = config.impl.create(req, secret); | ||
var validate = config.impl.validate || csrf.validate; | ||
var csrf = CONFIG.impl.create(req, secret); | ||
var validate = CONFIG.impl.validate || csrf.validate; | ||
var token = csrf.token || csrf; | ||
@@ -53,6 +56,6 @@ | ||
function setToken(res, token) { | ||
res.locals[config.key] = token; | ||
res.locals[CONFIG.key] = token; | ||
if (config.cookie && config.cookie.name) { | ||
res.cookie(config.cookie.name, token, config.cookie.options); | ||
if (CONFIG.cookie && CONFIG.cookie.name) { | ||
res.cookie(CONFIG.cookie.name, token, CONFIG.cookie.options); | ||
} | ||
@@ -68,8 +71,8 @@ } | ||
* | ||
* @returns {undefined} | ||
* @returns {void} | ||
*/ | ||
function middleware(req, res, next) { | ||
var csrf = getCsrf(req, config.secret); | ||
var csrf = getCsrf(req, CONFIG.secret); | ||
var token, errmsg; | ||
var token; | ||
@@ -84,3 +87,3 @@ setToken(res, csrf.token); | ||
req.csrfToken = () => { | ||
var newCsrf = getCsrf(req, config.secret); | ||
var newCsrf = getCsrf(req, CONFIG.secret); | ||
@@ -99,3 +102,3 @@ if (csrf.secret && newCsrf.secret && csrf.secret === newCsrf.secret) { | ||
/* Move along for safe verbs */ | ||
if (config.safeVerbs.indexOf(req.method) >= 0) { | ||
if (CONFIG.safeVerbs.indexOf(req.method) >= 0) { | ||
return next(); | ||
@@ -105,4 +108,4 @@ } | ||
/* Validate token */ | ||
token = (req.body && req.body[config.key]) || | ||
req.headers[config.header.toLowerCase()]; | ||
token = (req.body && req.body[CONFIG.key]) || | ||
req.headers[CONFIG.header]; | ||
@@ -113,13 +116,27 @@ if (csrf.validate(req, token)) { | ||
res.statusCode = 403; | ||
console.log(req.body, req.headers[CONFIG.header]); | ||
if (!token) { | ||
errmsg = ERR.CSRF_TOKEN_MISSING; | ||
} else { | ||
errmsg = ERR.CSRF_TOKEN_MISMATCH; | ||
} | ||
res.status(403); | ||
next(new Error(errmsg)); | ||
next(new Error(!token ? ERR.CSRF_TOKEN_MISSING : ERR.CSRF_TOKEN_MISMATCH)); | ||
} | ||
/** | ||
* Confures the CSRF module. | ||
* | ||
* @param {Object} options The options object. | ||
* @param {Boolean} options.angular Whether to use AngularJS defaults. | ||
* @param {String} options.key The CSRF token key in res locals and req body. | ||
* @param {Function} options.impl The token generator implementation. | ||
* @param {String} options.header The HTTP header to use for the CSRF token. | ||
* @param {String} options.secret The secret property key name to use on the | ||
* CSRF token object. | ||
* @param {String|Object} options.cookie The CSRF cookie name or cookie options | ||
* to use. | ||
* @param {String} options.cookie.name The CSRF cookie name to use. | ||
* @param {Object} options.cookie.options A valid Express cookie options | ||
* object. | ||
* | ||
* @returns {Function} The CSRF Express middleware. | ||
*/ | ||
module.exports = options => { | ||
@@ -129,35 +146,25 @@ | ||
if (options.angular) { | ||
options.header = 'x-xsrf-token'; | ||
options.cookie = { | ||
name: 'XSRF-TOKEN' | ||
}; | ||
} | ||
config.key = options.key || '_csrf'; | ||
config.impl = options.impl || token; | ||
config.header = options.header || 'x-csrf-token'; | ||
config.secret = options.secret || '_csrfSecret'; | ||
config.cookie = { | ||
name: 'csrf-token', | ||
options: {} | ||
/* Initialize defaults */ | ||
CONFIG.header = (options.header || 'csrf-token').toLowerCase(); // https://stackoverflow.com/a/5259004/1970170 | ||
CONFIG.safeVerbs = options.safeVerbs || ['OPTIONS', 'HEAD', 'GET']; | ||
CONFIG.secret = options.secret || '_csrfSecret'; | ||
CONFIG.impl = options.impl || token; | ||
CONFIG.key = options.key || '_csrf'; | ||
CONFIG.cookie = { | ||
options: options.cookie && options.cookie.options, | ||
name: 'CSRF-TOKEN' | ||
}; | ||
/* Check if cookie is string or object */ | ||
if (typeof options.cookie === 'string') { | ||
config.cookie = { | ||
name: options.cookie, | ||
}; | ||
} else if (options && options.cookie) { | ||
config.cookie = { | ||
name: options.cookie.name | ||
}; | ||
/* Angular shorthand option should override other options */ | ||
if (options.angular) { | ||
CONFIG.cookie.name = 'XSRF-TOKEN'; | ||
CONFIG.header = 'x-xsrf-token'; | ||
} else if (typeof options.cookie === 'string') { | ||
CONFIG.cookie.name = options.cookie; | ||
} else if (options.cookie && typeof options.cookie.name === 'string') { | ||
CONFIG.cookie.name = options.cookie.name; | ||
} | ||
/* Set cookie options */ | ||
config.cookie.options = options.cookie && options.cookie.options ? | ||
options.cookie.options : {}; | ||
return middleware; | ||
}; |
/** | ||
* HSTS - Http Strict Transport Security. | ||
* HSTS module. | ||
* | ||
* @module fi-aegis/hsts | ||
* | ||
* @see module:fi-aegis/hsts | ||
* @see https://www.owasp.org/index.php/HTTP_Strict_Transport_Security | ||
@@ -28,2 +31,13 @@ */ | ||
/** | ||
* Configures the HSTS module. | ||
* | ||
* @param {Object} options The options object. | ||
* @param {Number} options.maxAge The `max-age` value. | ||
* @param {Boolean} options.includeSubDomains Whether to add the | ||
* `includeSubDomains` directive. | ||
* @param {Boolean} options.preload Whether to add the `preload` directive. | ||
* | ||
* @returns {Function} The Express middleware. | ||
*/ | ||
module.exports = options => { | ||
@@ -38,3 +52,3 @@ | ||
if (options.maxAge > -1) { | ||
value = `max-age=${ options.maxAge }`; | ||
value = 'max-age=' + options.maxAge; | ||
@@ -41,0 +55,0 @@ if (options.includeSubDomains) { |
/** | ||
* X-Content-Type-Options. | ||
* Nosniff module. | ||
* | ||
* @module fi-aegis/nosniff | ||
* | ||
* @see module:fi-aegis/nosniff | ||
* @see https://blogs.msdn.microsoft.com/ie/2008/09/02/ie8-security-part-vi-beta-2-update/ | ||
@@ -12,19 +15,20 @@ */ | ||
module.exports = () => { | ||
/** | ||
* Nosniff Middleware. | ||
* | ||
* @param {Object} req Express request object. | ||
* @param {Object} res Express response object. | ||
* @param {Function} next Express next callback. | ||
*/ | ||
function middleware(req, res, next) { | ||
res.header(HEADER, VALUE); | ||
/** | ||
* No Sniff Middleware. | ||
* | ||
* @param {Object} req Express request object. | ||
* @param {Object} res Express response object. | ||
* @param {Function} next Express next callback. | ||
*/ | ||
function middleware(req, res, next) { | ||
res.header(HEADER, VALUE); | ||
next(); | ||
} | ||
next(); | ||
} | ||
return middleware; | ||
}; | ||
/** | ||
* Configures the Nosniff module. | ||
* | ||
* @returns {Function} The Express middleware. | ||
*/ | ||
module.exports = () => middleware; |
/** | ||
* P3P - Platform for Privacy Preferences Project | ||
* P3P module. | ||
* | ||
* @module fi-aegis/p3p | ||
* | ||
* @see module:fi-aegis/p3p | ||
* @see https://www.w3.org/P3P/Overview.html | ||
@@ -13,23 +16,34 @@ * | ||
module.exports = value => { | ||
var value; | ||
/** | ||
* P3P middleware. | ||
* | ||
* @param {String} req Express request object. | ||
* @param {String} res Express response object. | ||
* @param {Function} next Express next middleware callback. | ||
* | ||
* @deprecated | ||
*/ | ||
function middleware(req, res, next) { | ||
if (value) { | ||
res.header(HEADER, value); | ||
} | ||
next(); | ||
/** | ||
* P3P middleware. | ||
* | ||
* @param {String} req Express request object. | ||
* @param {String} res Express response object. | ||
* @param {Function} next Express next middleware callback. | ||
* | ||
* @deprecated | ||
*/ | ||
function middleware(req, res, next) { | ||
if (value) { | ||
res.header(HEADER, value); | ||
} | ||
next(); | ||
} | ||
/** | ||
* Configures the P3P module. | ||
* | ||
* @param {String} val The value for the `p3p` header. | ||
* | ||
* @returns {Function} The Express middleware. | ||
*/ | ||
module.exports = val => { | ||
value = val; | ||
return middleware; | ||
}; |
/** | ||
* X-Frame-Options | ||
* XFRAMES module. | ||
* | ||
* @module fi-aegis/xframes | ||
* | ||
* @see module:fi-aegis/xframes | ||
* @see https://www.owasp.org/index.php/Clickjacking | ||
@@ -28,2 +31,9 @@ */ | ||
/** | ||
* Configures the XFRAMES module. | ||
* | ||
* @param {String} val The value for the `xframes` header. | ||
* | ||
* @returns {Function} The Express middleware. | ||
*/ | ||
module.exports = val => { | ||
@@ -30,0 +40,0 @@ |
/** | ||
* X-XSS-Protection | ||
* HSTS module. | ||
* | ||
* @module fi-aegis/hsts | ||
* | ||
* @see module:fi-aegis/hsts | ||
* @see http://blogs.msdn.com/b/ie/archive/2008/07/02/ie8-security-part-iv-the-xss-filter.aspx | ||
@@ -28,2 +31,13 @@ */ | ||
/** | ||
* Configures the XSSPROTECTION module. | ||
* | ||
* @param {Boolean|Object} options Whether to enable this module or an options | ||
* object. | ||
* @param {Number|String|Boolean} options.enabled The header value clamped to 1 | ||
* or 0. | ||
* @param {String} options.mode The mode directive value. | ||
* | ||
* @returns {Function} The Express middleware. | ||
*/ | ||
module.exports = options => { | ||
@@ -44,8 +58,12 @@ | ||
if (value) { | ||
value += `; mode=${ typeof options.mode === 'string' ? options.mode : 'block' }`; | ||
var mode = 'block'; | ||
if (options && options.mode && typeof options.mode === 'string') { | ||
mode = options.mode; | ||
} | ||
value += '; mode=' + mode; | ||
return middleware; | ||
}; |
{ | ||
"name": "fi-aegis", | ||
"version": "1.0.2", | ||
"version": "1.1.0", | ||
"description": "Web Application Security Middleware.", | ||
@@ -10,3 +10,3 @@ "author": "Jeff Harrell <jeharrell@paypal.com>", | ||
"scripts": { | ||
"test": "node_modules/.bin/mocha test" | ||
"test": "node_modules/.bin/mocha" | ||
}, | ||
@@ -13,0 +13,0 @@ "repository": { |
@@ -100,10 +100,11 @@ # Fi Aegis | ||
|-------|------|----------|---------|-------------| | ||
| `key` | `String` | No | `_csrf` | The name of the CSRF token added to the model. | | ||
| `key` | `String` | No | `_csrf` | The name of the property to store the CSRF token into `res.locals` and retrieve from the `req.body`. | | ||
| `secret` | `String` | No | `_csrfSecret` | The key to place on the session object which maps to the server side token. | | ||
| `impl` | `Function` | No | See [lib/token.js](https://github.com/FinalDevStudio/fi-aegis/blob/master/lib/token.js). | Custom implementation to generate a token. | ||
| `angular` | `Boolean` | No | `false` | Shorthand setting to set **Fi Aegis** up to use the default settings for CSRF validation according to the [AngularJS docs](https://docs.angularjs.org/api/ng/service/$http#cross-site-request-forgery-xsrf-protection). | | ||
| `cookie` | `String` or `Object` | Yes (if `angular` is `false`) | None | If set, a cookie with the name you provide will be set with the CSRF token. | | ||
| `cookie.name` | `String` | Yes (if `angular` is `false` and cookie is `Object`) | None | The name you provide will be set as the cookie with the CSRF token. | | ||
| `cookie.options` | `Object` | No | None | A valid Express cookie options object. See [Express response cookies](http://expressjs.com/en/4x/api.html#res.cookie) for more information. | | ||
| `header` | `String` | Yes (if `angular` is `false`) | None | If set, the header name you provide will be set with the CSRF token. | | ||
| `cookie` | `String` or `Object` | No | `Object` | If set, a cookie with the name you provide will be set with the CSRF token. | | ||
| `cookie.name` | `String` | No | `CSRF-TOKEN` | The name you provide will be set as the cookie with the CSRF token. | | ||
| `cookie.options` | `Object` | No | `{}` | A valid Express cookie options object. See [Express' res.cookie](http://expressjs.com/en/4x/api.html#res.cookie) API docs for more information. | | ||
| `header` | `String` | No | `csrf-token` | If set, the header name you provide will be expected to have the CSRF token. | | ||
| `safeVerbs` | `[String]` | No | `['OPTIONS', 'HEAD', 'GET']` | A list of HTTP verbs cosidered `safe` that will skip CSRF token validation. | ||
@@ -110,0 +111,0 @@ --- |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
28905
595
291