Project rename
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2018 Evan Hahn, Adam Baldwin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
Content Security Policy middleware
|
||||
==================================
|
||||
[](https://travis-ci.org/helmetjs/csp)
|
||||
[](http://standardjs.com/)
|
||||
|
||||
[_Looking for a changelog?_](https://github.com/helmetjs/helmet/blob/master/HISTORY.md)
|
||||
|
||||
Content Security Policy helps prevent unwanted content being injected into your webpages; this can mitigate cross-site scripting (XSS) vulnerabilities, malicious frames, unwanted trackers, and more. If you want to learn how CSP works, check out the fantastic [HTML5 Rocks guide](http://www.html5rocks.com/en/tutorials/security/content-security-policy/), the [Content Security Policy Reference](http://content-security-policy.com/), and the [Content Security Policy specification](http://www.w3.org/TR/CSP/). This module helps set Content Security Policies.
|
||||
|
||||
Usage:
|
||||
|
||||
```javascript
|
||||
var csp = require('helmet-csp')
|
||||
|
||||
app.use(csp({
|
||||
// Specify directives as normal.
|
||||
directives: {
|
||||
defaultSrc: ["'self'", 'default.com'],
|
||||
scriptSrc: ["'self'", "'unsafe-inline'"],
|
||||
styleSrc: ['style.com'],
|
||||
fontSrc: ["'self'", 'fonts.com'],
|
||||
imgSrc: ['img.com', 'data:'],
|
||||
sandbox: ['allow-forms', 'allow-scripts'],
|
||||
reportUri: '/report-violation',
|
||||
objectSrc: ["'none'"],
|
||||
upgradeInsecureRequests: true,
|
||||
workerSrc: false // This is not set.
|
||||
},
|
||||
|
||||
// This module will detect common mistakes in your directives and throw errors
|
||||
// if it finds any. To disable this, enable "loose mode".
|
||||
loose: false,
|
||||
|
||||
// Set to true if you only want browsers to report errors, not block them.
|
||||
// You may also set this to a function(req, res) in order to decide dynamically
|
||||
// whether to use reportOnly mode, e.g., to allow for a dynamic kill switch.
|
||||
reportOnly: false,
|
||||
|
||||
// Set to true if you want to blindly set all headers: Content-Security-Policy,
|
||||
// X-WebKit-CSP, and X-Content-Security-Policy.
|
||||
setAllHeaders: false,
|
||||
|
||||
// Set to true if you want to disable CSP on Android where it can be buggy.
|
||||
disableAndroid: false,
|
||||
|
||||
// Set to false if you want to completely disable any user-agent sniffing.
|
||||
// This may make the headers less compatible but it will be much faster.
|
||||
// This defaults to `true`.
|
||||
browserSniff: true
|
||||
}))
|
||||
```
|
||||
|
||||
There are a lot of inconsistencies in how browsers implement CSP. Helmet looks at the user-agent of the browser and sets the appropriate header and value for that browser. If no user-agent is matched, it will set _all_ the headers with the 2.0 spec.
|
||||
|
||||
Supported directives
|
||||
--------------------
|
||||
|
||||
Directives can be kebab-cased (like `script-src`) or camel-cased (like `scriptSrc`); they are equivalent.
|
||||
|
||||
The following directives are supported:
|
||||
|
||||
* `base-uri` or `baseUri`
|
||||
* `block-all-mixed-content` or `blockAllMixedContent`
|
||||
* `child-src` or `childSrc`
|
||||
* `connect-src` or `connectSrc`
|
||||
* `default-src` or `defaultSrc`
|
||||
* `font-src` or `fontSrc`
|
||||
* `form-action` or `formAction`
|
||||
* `frame-ancestors` or `frameAncestors`
|
||||
* `frame-src` or `frameSrc`
|
||||
* `img-src` or `imgSrc`
|
||||
* `manifest-src` or `manifestSrc`
|
||||
* `media-src` or `mediaSrc`
|
||||
* `object-src` or `objectSrc`
|
||||
* `plugin-types` or `pluginTypes`
|
||||
* `prefetch-src` or `prefetchSrc`
|
||||
* `report-to` or `reportTo`
|
||||
* `report-uri` or `reportUri`
|
||||
* `require-sri-for` or `requireSriFor`
|
||||
* `sandbox` or `sandbox`
|
||||
* `script-src` or `scriptSrc`
|
||||
* `style-src` or `styleSrc`
|
||||
* `upgrade-insecure-requests` or `upgradeInsecureRequests`
|
||||
* `worker-src` or `workerSrc`
|
||||
|
||||
Handling CSP violations
|
||||
-----------------------
|
||||
|
||||
If you've specified a `reportUri`, browsers will POST any CSP violations to your server. Here's a simple example of a route that handles those reports:
|
||||
|
||||
```js
|
||||
// You need a JSON parser first.
|
||||
app.use(bodyParser.json({
|
||||
type: ['json', 'application/csp-report']
|
||||
}))
|
||||
|
||||
app.post('/report-violation', function (req, res) {
|
||||
if (req.body) {
|
||||
console.log('CSP Violation: ', req.body)
|
||||
} else {
|
||||
console.log('CSP Violation: No data received!')
|
||||
}
|
||||
res.status(204).end()
|
||||
})
|
||||
```
|
||||
|
||||
Not all browsers send CSP violations in the same way, so this might require a little work.
|
||||
|
||||
*Note*: If you're using a CSRF module like [csurf](https://github.com/expressjs/csurf), you might have problems handling these violations without a valid CSRF token. The fix is to put your CSP report route *above* csurf middleware.
|
||||
|
||||
Generating nonces
|
||||
-----------------
|
||||
|
||||
You can dynamically generate nonces to allow inline `<script>` tags to be safely evaluated. Here's a simple example:
|
||||
|
||||
```js
|
||||
var uuidv4 = require('uuid/v4')
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
res.locals.nonce = uuidv4()
|
||||
next()
|
||||
})
|
||||
|
||||
app.use(csp({
|
||||
directives: {
|
||||
scriptSrc: [
|
||||
"'self'",
|
||||
function (req, res) {
|
||||
return "'nonce-" + res.locals.nonce + "'" // 'nonce-614d9122-d5b0-4760-aecf-3a5d17cf0ac9'
|
||||
}
|
||||
]
|
||||
}
|
||||
}))
|
||||
|
||||
app.use(function (req, res) {
|
||||
res.end('<script nonce="' + res.locals.nonce + '">alert(1 + 1);</script>')
|
||||
})
|
||||
```
|
||||
|
||||
Using CSP with a CDN
|
||||
--------------------
|
||||
|
||||
The default behavior of CSP is generate headers tailored for the browser that's requesting your page. If you have a CDN in front of your application, the CDN may cache the wrong headers, rendering your CSP useless. Make sure to eschew a CDN when using this module or set the `browserSniff` option to `false`.
|
||||
|
||||
See also
|
||||
--------
|
||||
|
||||
* [GitHub's CSP journey](http://githubengineering.com/githubs-csp-journey/)
|
||||
* [Content Security Policy for Single Page Web Apps](https://corner.squareup.com/2016/05/content-security-policy-single-page-app.html)
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
var camelize = require('camelize')
|
||||
var cspBuilder = require('content-security-policy-builder')
|
||||
var isFunction = require('./lib/is-function')
|
||||
var platform = require('platform')
|
||||
var checkOptions = require('./lib/check-options')
|
||||
var containsFunction = require('./lib/contains-function')
|
||||
var getHeaderKeysForBrowser = require('./lib/get-header-keys-for-browser')
|
||||
var transformDirectivesForBrowser = require('./lib/transform-directives-for-browser')
|
||||
var parseDynamicDirectives = require('./lib/parse-dynamic-directives')
|
||||
var config = require('./lib/config')
|
||||
|
||||
module.exports = function csp (options) {
|
||||
checkOptions(options)
|
||||
|
||||
var originalDirectives = camelize(options.directives || {})
|
||||
var directivesAreDynamic = containsFunction(originalDirectives)
|
||||
var shouldBrowserSniff = options.browserSniff !== false
|
||||
var reportOnlyIsFunction = isFunction(options.reportOnly)
|
||||
|
||||
if (shouldBrowserSniff) {
|
||||
return function csp (req, res, next) {
|
||||
var userAgent = req.headers['user-agent']
|
||||
|
||||
var browser
|
||||
if (userAgent) {
|
||||
browser = platform.parse(userAgent)
|
||||
} else {
|
||||
browser = {}
|
||||
}
|
||||
|
||||
var headerKeys
|
||||
if (options.setAllHeaders || !userAgent) {
|
||||
headerKeys = config.allHeaders
|
||||
} else {
|
||||
headerKeys = getHeaderKeysForBrowser(browser, options)
|
||||
}
|
||||
|
||||
if (headerKeys.length === 0) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
var directives = transformDirectivesForBrowser(browser, originalDirectives)
|
||||
|
||||
if (directivesAreDynamic) {
|
||||
directives = parseDynamicDirectives(directives, [req, res])
|
||||
}
|
||||
|
||||
var policyString = cspBuilder({ directives: directives })
|
||||
|
||||
headerKeys.forEach(function (headerKey) {
|
||||
if ((reportOnlyIsFunction && options.reportOnly(req, res)) ||
|
||||
(!reportOnlyIsFunction && options.reportOnly)) {
|
||||
headerKey += '-Report-Only'
|
||||
}
|
||||
res.setHeader(headerKey, policyString)
|
||||
})
|
||||
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
var headerKeys
|
||||
if (options.setAllHeaders) {
|
||||
headerKeys = config.allHeaders
|
||||
} else {
|
||||
headerKeys = ['Content-Security-Policy']
|
||||
}
|
||||
|
||||
return function csp (req, res, next) {
|
||||
var directives = parseDynamicDirectives(originalDirectives, [req, res])
|
||||
var policyString = cspBuilder({ directives: directives })
|
||||
|
||||
if ((reportOnlyIsFunction && options.reportOnly(req, res)) ||
|
||||
(!reportOnlyIsFunction && options.reportOnly)) {
|
||||
headerKeys.forEach(function (headerKey) {
|
||||
res.setHeader(headerKey + '-Report-Only', policyString)
|
||||
})
|
||||
} else {
|
||||
headerKeys.forEach(function (headerKey) {
|
||||
res.setHeader(headerKey, policyString)
|
||||
})
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
var isBoolean = require('../../is-boolean')
|
||||
|
||||
module.exports = function (key, value) {
|
||||
if (!isBoolean(value)) {
|
||||
throw new Error('"' + value + '" is not a valid value for ' + key + '. Use `true` or `false`.')
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
var config = require('../../config')
|
||||
var checkers = {
|
||||
sourceList: require('./source-list'),
|
||||
pluginTypes: require('./plugin-types'),
|
||||
sandbox: require('./sandbox'),
|
||||
reportUri: require('./report-uri'),
|
||||
requireSriFor: require('./require-sri-for'),
|
||||
boolean: require('./boolean')
|
||||
}
|
||||
|
||||
module.exports = function (key, value, options) {
|
||||
if (options.loose) { return }
|
||||
|
||||
if (!config.directives.hasOwnProperty(key)) {
|
||||
throw new Error('"' + key + '" is an invalid directive. See the documentation for the supported list. Force this by enabling loose mode.')
|
||||
}
|
||||
|
||||
var directiveType = config.directives[key].type
|
||||
checkers[directiveType](key, value, options)
|
||||
}
|
||||
Generated
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
var config = require('../../config')
|
||||
var isFunction = require('../../is-function')
|
||||
|
||||
var notAllowed = ['self', "'self'"].concat(config.unsafes)
|
||||
|
||||
module.exports = function pluginTypesCheck (key, value, options) {
|
||||
if (!Array.isArray(value) && (value !== false)) {
|
||||
throw new Error('"' + value + '" is not a valid value for ' + key + '. Use an array of strings.')
|
||||
}
|
||||
|
||||
if (value.length === 0) {
|
||||
throw new Error(key + ' must have at least one value. To block everything, set ' + key + ' to ["\'none\'"].')
|
||||
}
|
||||
|
||||
value.forEach(function (pluginType) {
|
||||
if (!pluginType) {
|
||||
throw new Error('"' + pluginType + '" is not a valid plugin type. Only non-empty strings are allowed.')
|
||||
}
|
||||
|
||||
if (isFunction(pluginType)) { return }
|
||||
|
||||
pluginType = pluginType.valueOf()
|
||||
|
||||
if ((typeof pluginType !== 'string') || (pluginType.length === 0)) {
|
||||
throw new Error('"' + pluginType + '" is not a valid plugin type. Only non-empty strings are allowed.')
|
||||
}
|
||||
|
||||
if (notAllowed.indexOf(pluginType) !== -1) {
|
||||
throw new Error('"' + pluginType + '" does not make sense in ' + key + '. Remove it.')
|
||||
}
|
||||
|
||||
if (config.mustQuote.indexOf(pluginType) !== -1) {
|
||||
throw new Error('"' + pluginType + '" must be quoted in ' + key + '. Change it to "\'' + pluginType + '\'" in your source list. Force this by enabling loose mode.')
|
||||
}
|
||||
})
|
||||
}
|
||||
Generated
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
var isFunction = require('../../is-function')
|
||||
var isString = require('../../is-string')
|
||||
|
||||
module.exports = function (key, value) {
|
||||
if (value === false) { return }
|
||||
if (isFunction(value)) { return }
|
||||
|
||||
if (!isString(value) || (value.length === 0)) {
|
||||
throw new Error('"' + value + '" is not a valid value for ' + key + '. Use a non-empty string.')
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
var isFunction = require('../../is-function')
|
||||
var config = require('../../config')
|
||||
|
||||
module.exports = function requireSriForCheck (key, value) {
|
||||
if (!Array.isArray(value)) {
|
||||
throw new Error('"' + value + '" is not a valid value for ' + key + '. Use an array of strings.')
|
||||
}
|
||||
|
||||
if (value.length === 0) {
|
||||
throw new Error(key + ' must have at least one value. To require nothing, omit the directive.')
|
||||
}
|
||||
|
||||
value.forEach(function (expression) {
|
||||
if (isFunction(expression)) { return }
|
||||
|
||||
if (config.requireSriForValues.indexOf(expression) === -1) {
|
||||
throw new Error('"' + expression + '" is not a valid ' + key + ' value. Remove it.')
|
||||
}
|
||||
})
|
||||
}
|
||||
Generated
Vendored
+23
@@ -0,0 +1,23 @@
|
||||
var isFunction = require('../../is-function')
|
||||
var config = require('../../config')
|
||||
|
||||
module.exports = function sandboxCheck (key, value) {
|
||||
if (value === false) { return }
|
||||
if (value === true) { return }
|
||||
|
||||
if (!Array.isArray(value)) {
|
||||
throw new Error('"' + value + '" is not a valid value for ' + key + '. Use an array of strings or `true`.')
|
||||
}
|
||||
|
||||
if (value.length === 0) {
|
||||
throw new Error(key + ' must have at least one value. To block everything, set ' + key + ' to `true`.')
|
||||
}
|
||||
|
||||
value.forEach(function (expression) {
|
||||
if (isFunction(expression)) { return }
|
||||
|
||||
if (config.sandboxDirectives.indexOf(expression) === -1) {
|
||||
throw new Error('"' + expression + '" is not a valid ' + key + ' directive. Remove it.')
|
||||
}
|
||||
})
|
||||
}
|
||||
Generated
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
var isFunction = require('../../is-function')
|
||||
var config = require('../../config')
|
||||
|
||||
module.exports = function sourceListCheck (key, value, options) {
|
||||
var directiveInfo = config.directives[key]
|
||||
|
||||
if (value === false) { return }
|
||||
|
||||
if (!Array.isArray(value)) {
|
||||
throw new Error('"' + value + '" is not a valid value for ' + key + '. Use an array of strings.')
|
||||
}
|
||||
|
||||
if (value.length === 0) {
|
||||
throw new Error(key + ' must have at least one value. To block everything, set ' + key + ' to ["\'none\'"].')
|
||||
}
|
||||
|
||||
value.forEach(function (sourceExpression) {
|
||||
if (!sourceExpression) {
|
||||
throw new Error('"' + sourceExpression + '" is not a valid source expression. Only non-empty strings are allowed.')
|
||||
}
|
||||
|
||||
if (isFunction(sourceExpression)) { return }
|
||||
|
||||
sourceExpression = sourceExpression.valueOf()
|
||||
|
||||
if ((typeof sourceExpression !== 'string') || (sourceExpression.length === 0)) {
|
||||
throw new Error('"' + sourceExpression + '" is not a valid source expression. Only non-empty strings are allowed.')
|
||||
}
|
||||
|
||||
if ((!directiveInfo.hasUnsafes && (config.unsafes.indexOf(sourceExpression) !== -1)) ||
|
||||
(!directiveInfo.hasStrictDynamic && (config.strictDynamics.indexOf(sourceExpression) !== -1))) {
|
||||
throw new Error('"' + sourceExpression + '" does not make sense in ' + key + '. Remove it.')
|
||||
}
|
||||
|
||||
if (config.mustQuote.indexOf(sourceExpression) !== -1) {
|
||||
throw new Error('"' + sourceExpression + '" must be quoted in ' + key + '. Change it to "\'' + sourceExpression + '\'" in your source list. Force this by enabling loose mode.')
|
||||
}
|
||||
})
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
var checkDirective = require('./check-directive')
|
||||
var dasherize = require('dasherize')
|
||||
|
||||
module.exports = function (options) {
|
||||
if (!isObject(options)) {
|
||||
throw new Error('csp must be called with an object argument. See the documentation.')
|
||||
}
|
||||
|
||||
var directives = options.directives
|
||||
|
||||
var directivesExist = isObject(directives)
|
||||
if (!directivesExist || Object.keys(directives).length === 0) {
|
||||
throw new Error('csp must have at least one directive under the "directives" key. See the documentation.')
|
||||
}
|
||||
|
||||
Object.keys(directives).forEach(function (directiveKey) {
|
||||
checkDirective(dasherize(directiveKey), directives[directiveKey], options)
|
||||
})
|
||||
}
|
||||
|
||||
function isObject (value) {
|
||||
return Object.prototype.toString.call(value) === '[object Object]'
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
module.exports = {
|
||||
directives: {
|
||||
'base-uri': { type: 'sourceList' },
|
||||
'block-all-mixed-content': { type: 'boolean' },
|
||||
'child-src': { type: 'sourceList' },
|
||||
'connect-src': { type: 'sourceList' },
|
||||
'default-src': {
|
||||
type: 'sourceList',
|
||||
hasStrictDynamic: true
|
||||
},
|
||||
'font-src': { type: 'sourceList' },
|
||||
'form-action': { type: 'sourceList' },
|
||||
'frame-ancestors': { type: 'sourceList' },
|
||||
'frame-src': { type: 'sourceList' },
|
||||
'img-src': { type: 'sourceList' },
|
||||
'manifest-src': { type: 'sourceList' },
|
||||
'media-src': { type: 'sourceList' },
|
||||
'object-src': { type: 'sourceList' },
|
||||
'script-src': {
|
||||
type: 'sourceList',
|
||||
hasUnsafes: true,
|
||||
hasStrictDynamic: true
|
||||
},
|
||||
'style-src': {
|
||||
type: 'sourceList',
|
||||
hasUnsafes: true
|
||||
},
|
||||
'prefetch-src': { type: 'sourceList' },
|
||||
'plugin-types': { type: 'pluginTypes' },
|
||||
'sandbox': { type: 'sandbox' },
|
||||
'report-to': { type: 'reportUri' },
|
||||
'report-uri': { type: 'reportUri' },
|
||||
'require-sri-for': { type: 'requireSriFor' },
|
||||
'upgrade-insecure-requests': { type: 'boolean' },
|
||||
'worker-src': {
|
||||
type: 'sourceList',
|
||||
hasUnsafes: true
|
||||
}
|
||||
},
|
||||
allHeaders: [
|
||||
'Content-Security-Policy',
|
||||
'X-Content-Security-Policy',
|
||||
'X-WebKit-CSP'
|
||||
],
|
||||
mustQuote: ['none', 'self', 'unsafe-inline', 'unsafe-eval', 'strict-dynamic'],
|
||||
unsafes: ["'unsafe-inline'", 'unsafe-inline', "'unsafe-eval'", 'unsafe-eval'],
|
||||
strictDynamics: ["'strict-dynamic'", 'strict-dynamic'],
|
||||
requireSriForValues: ['script', 'style'],
|
||||
sandboxDirectives: [
|
||||
'allow-forms',
|
||||
'allow-modals',
|
||||
'allow-orientation-lock',
|
||||
'allow-pointer-lock',
|
||||
'allow-popups',
|
||||
'allow-popups-to-escape-sandbox',
|
||||
'allow-presentation',
|
||||
'allow-same-origin',
|
||||
'allow-scripts',
|
||||
'allow-top-navigation'
|
||||
]
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
var isFunction = require('./is-function')
|
||||
|
||||
module.exports = function containsFunction (obj) {
|
||||
for (var key in obj) {
|
||||
if (!obj.hasOwnProperty(key)) { continue }
|
||||
|
||||
var value = obj[key]
|
||||
|
||||
if (!Array.isArray(value)) {
|
||||
value = [value]
|
||||
}
|
||||
|
||||
if (value.some(isFunction)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
var config = require('./config')
|
||||
|
||||
function goodBrowser () {
|
||||
return ['Content-Security-Policy']
|
||||
}
|
||||
|
||||
var handlers = {
|
||||
'Android Browser': function (browser, options) {
|
||||
if (parseFloat(browser.os.version) < 4.4 || options.disableAndroid) {
|
||||
return []
|
||||
} else {
|
||||
return ['Content-Security-Policy']
|
||||
}
|
||||
},
|
||||
|
||||
Chrome: function (browser) {
|
||||
var version = parseFloat(browser.version)
|
||||
|
||||
if (version >= 14 && version < 25) {
|
||||
return ['X-WebKit-CSP']
|
||||
} else if (version >= 25) {
|
||||
return ['Content-Security-Policy']
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
'Chrome Mobile': function (browser) {
|
||||
if (browser.os.family === 'iOS') {
|
||||
return ['Content-Security-Policy']
|
||||
} else {
|
||||
return handlers['Android Browser'].apply(this, arguments)
|
||||
}
|
||||
},
|
||||
|
||||
Firefox: function (browser) {
|
||||
var version = parseFloat(browser.version)
|
||||
|
||||
if (version >= 23) {
|
||||
return ['Content-Security-Policy']
|
||||
} else if (version >= 4 && version < 23) {
|
||||
return ['X-Content-Security-Policy']
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
'Firefox Mobile': function (browser) {
|
||||
// Handles both Firefox for Android and Firefox OS
|
||||
var family = browser.os.family
|
||||
var version = parseFloat(browser.version)
|
||||
|
||||
if (family === 'Firefox OS') {
|
||||
if (version >= 32) {
|
||||
return ['Content-Security-Policy']
|
||||
} else {
|
||||
return ['X-Content-Security-Policy']
|
||||
}
|
||||
} else if (family === 'Android') {
|
||||
if (version >= 25) {
|
||||
return ['Content-Security-Policy']
|
||||
} else {
|
||||
return ['X-Content-Security-Policy']
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
},
|
||||
|
||||
'Firefox for iOS': goodBrowser,
|
||||
|
||||
IE: function (browser) {
|
||||
var version = parseFloat(browser.version)
|
||||
var header = version < 12 ? 'X-Content-Security-Policy' : 'Content-Security-Policy'
|
||||
|
||||
return [header]
|
||||
},
|
||||
|
||||
'Microsoft Edge': goodBrowser,
|
||||
|
||||
'Microsoft Edge Mobile': goodBrowser,
|
||||
|
||||
Opera: function (browser) {
|
||||
if (parseFloat(browser.version) >= 15) {
|
||||
return ['Content-Security-Policy']
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
Safari: function (browser) {
|
||||
var version = parseFloat(browser.version)
|
||||
|
||||
if (version >= 7) {
|
||||
return ['Content-Security-Policy']
|
||||
} else if (version >= 6) {
|
||||
return ['X-WebKit-CSP']
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handlers['IE Mobile'] = handlers.IE
|
||||
|
||||
module.exports = function getHeaderKeysForBrowser (browser, options) {
|
||||
var handler = handlers[browser.name]
|
||||
|
||||
if (handler) {
|
||||
return handler(browser, options)
|
||||
} else {
|
||||
return config.allHeaders
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module.exports = function isBoolean (value) {
|
||||
return Object.prototype.toString.call(value) === '[object Boolean]'
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module.exports = function isFunction (value) {
|
||||
return value instanceof Function
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
module.exports = function isString (value) {
|
||||
return Object.prototype.toString.call(value) === '[object String]'
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
var isFunction = require('./is-function')
|
||||
|
||||
module.exports = function parseDynamicDirectives (directives, functionArgs) {
|
||||
var result = {}
|
||||
|
||||
Object.keys(directives).forEach(function (key) {
|
||||
var value = directives[key]
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
result[key] = value.map(function (element) {
|
||||
if (isFunction(element)) {
|
||||
return element.apply(null, functionArgs)
|
||||
} else {
|
||||
return element
|
||||
}
|
||||
})
|
||||
} else if (isFunction(value)) {
|
||||
result[key] = value.apply(null, functionArgs)
|
||||
} else if (value !== false) {
|
||||
result[key] = value
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
Generated
Vendored
+76
@@ -0,0 +1,76 @@
|
||||
function createFirefoxPreCSP10Directives (directives, basePolicy) {
|
||||
var result = Object.assign({}, basePolicy)
|
||||
|
||||
Object.keys(directives).forEach(function (key) {
|
||||
var value = directives[key]
|
||||
|
||||
if (key === 'connectSrc') {
|
||||
result.xhrSrc = value
|
||||
} else {
|
||||
result[key] = value
|
||||
}
|
||||
|
||||
if (key === 'scriptSrc') {
|
||||
var optionsValues = []
|
||||
|
||||
if (value.indexOf("'unsafe-inline'") !== -1) {
|
||||
optionsValues.push('inline-script')
|
||||
}
|
||||
if (value.indexOf("'unsafe-eval'") !== -1) {
|
||||
optionsValues.push('eval-script')
|
||||
}
|
||||
|
||||
if (optionsValues.length !== 0) {
|
||||
result.options = optionsValues
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
var handlers = {
|
||||
Firefox: function (browser, directives) {
|
||||
var version = parseFloat(browser.version)
|
||||
|
||||
if (version >= 4 && version < 23) {
|
||||
var basePolicy = {}
|
||||
if (version < 5) {
|
||||
basePolicy.allow = ['*']
|
||||
|
||||
if (directives.defaultSrc) {
|
||||
basePolicy.allow = directives.defaultSrc
|
||||
delete directives.defaultSrc
|
||||
}
|
||||
} else {
|
||||
basePolicy.defaultSrc = ['*']
|
||||
}
|
||||
|
||||
return createFirefoxPreCSP10Directives(directives, basePolicy)
|
||||
} else {
|
||||
return directives
|
||||
}
|
||||
},
|
||||
|
||||
'Firefox Mobile': function (browser, directives) {
|
||||
// Handles both Firefox for Android and Firefox OS
|
||||
var family = browser.os.family
|
||||
var version = parseFloat(browser.version)
|
||||
|
||||
if ((family === 'Firefox OS' && version < 32) || (family === 'Android' && version < 25)) {
|
||||
return createFirefoxPreCSP10Directives(directives, { defaultSrc: ['*'] })
|
||||
} else {
|
||||
return directives
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function transformDirectivesForBrowser (browser, directives) {
|
||||
var handler = handlers[browser.name]
|
||||
|
||||
if (handler) {
|
||||
return handler(browser, directives)
|
||||
} else {
|
||||
return directives
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
{
|
||||
"raw": "helmet-csp@2.7.1",
|
||||
"scope": null,
|
||||
"escapedName": "helmet-csp",
|
||||
"name": "helmet-csp",
|
||||
"rawSpec": "2.7.1",
|
||||
"spec": "2.7.1",
|
||||
"type": "version"
|
||||
},
|
||||
"/Users/gerrit/Documents/dev/nodejs/rentfor.camp/html/RentForCamp/node_modules/helmet"
|
||||
]
|
||||
],
|
||||
"_from": "helmet-csp@2.7.1",
|
||||
"_id": "helmet-csp@2.7.1",
|
||||
"_inCache": true,
|
||||
"_location": "/helmet-csp",
|
||||
"_nodeVersion": "10.5.0",
|
||||
"_npmOperationalInternal": {
|
||||
"host": "s3://npm-registry-packages",
|
||||
"tmp": "tmp/helmet-csp_2.7.1_1532118830676_0.2757220233905715"
|
||||
},
|
||||
"_npmUser": {
|
||||
"name": "evanhahn",
|
||||
"email": "me@evanhahn.com"
|
||||
},
|
||||
"_npmVersion": "6.2.0",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"raw": "helmet-csp@2.7.1",
|
||||
"scope": null,
|
||||
"escapedName": "helmet-csp",
|
||||
"name": "helmet-csp",
|
||||
"rawSpec": "2.7.1",
|
||||
"spec": "2.7.1",
|
||||
"type": "version"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/helmet"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.7.1.tgz",
|
||||
"_shasum": "e8e0b5186ffd4db625cfcce523758adbfadb9dca",
|
||||
"_shrinkwrap": null,
|
||||
"_spec": "helmet-csp@2.7.1",
|
||||
"_where": "/Users/gerrit/Documents/dev/nodejs/rentfor.camp/html/RentForCamp/node_modules/helmet",
|
||||
"author": {
|
||||
"name": "Adam Baldwin",
|
||||
"email": "baldwin@andyet.net",
|
||||
"url": "http://andyet.net/team/baldwin"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/helmetjs/csp/issues"
|
||||
},
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Evan Hahn",
|
||||
"email": "me@evanhahn.com",
|
||||
"url": "https://evanhahn.com"
|
||||
},
|
||||
{
|
||||
"name": "Ryan Cannon",
|
||||
"email": "ryan@ryancannon.com",
|
||||
"url": "https://ryancannon.com"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"camelize": "1.0.0",
|
||||
"content-security-policy-builder": "2.0.0",
|
||||
"dasherize": "2.0.0",
|
||||
"platform": "1.3.5"
|
||||
},
|
||||
"description": "Content Security Policy middleware.",
|
||||
"devDependencies": {
|
||||
"content-security-policy-parser": "^0.1.1",
|
||||
"express": "^4.16.3",
|
||||
"lodash": "^4.17.10",
|
||||
"mocha": "^5.2.0",
|
||||
"standard": "^11.0.1",
|
||||
"supertest": "^3.1.0"
|
||||
},
|
||||
"directories": {},
|
||||
"dist": {
|
||||
"integrity": "sha512-sCHwywg4daQ2mY0YYwXSZRsgcCeerUwxMwNixGA7aMLkVmPTYBl7gJoZDHOZyXkqPrtuDT3s2B1A+RLI7WxSdQ==",
|
||||
"shasum": "e8e0b5186ffd4db625cfcce523758adbfadb9dca",
|
||||
"tarball": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.7.1.tgz",
|
||||
"fileCount": 20,
|
||||
"unpackedSize": 24588,
|
||||
"npm-signature": "-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbUkcuCRA9TVsSAnZWagAAMpkP/jGWI9/c5mDrE40MQeO8\nCcnjhks0SsCCvnhDNggmFW8TNB/uHtNMklDx32M3lKV3fZKBeA8TAXwh0y1I\nLooWC1niKdvmLts6AG+8hk6uZk2HWwQz8wTHTaEZUCdP6hOfK7eMsJSD7SAS\nWhJ8qZnAjFvGyqr/Ni5SJ8OUWKF1je9K9WIozlej7kaFhGlLl0JCHRns/Kiq\n2u+2bMQdKRYHFnJbfztJrvQenoYNpiYP1bNdamXIJ7K6veCantLrYw/f33R4\n2TR3rQb29WGknKkfIH6+szoGz1ruaZjD311u8UXWVIQv/10aaP8sBEQQjj3Y\n2QHV5lB9JAfXXqG8ny5KKpcGAnstDnQUBPNuNFN4I4GLAe4oO4wrYfo1Gy5S\nYsTQwKf2EuMOhsgvAv6fkD+kRCneuXA248b5GS37y6miZxgxJjxgZKbS01CF\n/xfoC9ykto2pp30UVwwFvtbZHwlS5pTyZu/daFaQXj9NYevIEiHAliNJ9omX\nc+90SD2dsSMtTqZkzJI6fkunpgWWkv4r2JC2px0NknzVGAw2WdOe74HFRAok\nrTy2ozagrqef05k5RpjMLvEM3oRI13IX63f1R0Zb0enL3ua23lCqNg7xFmxX\nbBHQIrjQrr0esPZq6MJYI/x9GX9rKs+7UsOtU5DngnEUZnZdnu/mtwGIc9fx\n2tUx\r\n=UGEX\r\n-----END PGP SIGNATURE-----\r\n"
|
||||
},
|
||||
"gitHead": "4880b53cb58d46dbdf09d01b9293e5365694f8b7",
|
||||
"homepage": "https://github.com/helmetjs/csp#readme",
|
||||
"keywords": [
|
||||
"helmet",
|
||||
"security",
|
||||
"express",
|
||||
"connect",
|
||||
"content",
|
||||
"security",
|
||||
"policy",
|
||||
"csp",
|
||||
"xss"
|
||||
],
|
||||
"license": "MIT",
|
||||
"maintainers": [
|
||||
{
|
||||
"name": "evanhahn",
|
||||
"email": "me@evanhahn.com"
|
||||
}
|
||||
],
|
||||
"name": "helmet-csp",
|
||||
"optionalDependencies": {},
|
||||
"readme": "ERROR: No README data found!",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/helmetjs/csp.git"
|
||||
},
|
||||
"scripts": {
|
||||
"generate-supported-directives-docs": "./scripts/generate_supported_directives_docs",
|
||||
"pretest": "standard --fix",
|
||||
"test": "mocha"
|
||||
},
|
||||
"standard": {
|
||||
"globals": [
|
||||
"describe",
|
||||
"beforeEach",
|
||||
"it"
|
||||
]
|
||||
},
|
||||
"version": "2.7.1"
|
||||
}
|
||||
Reference in New Issue
Block a user