Working Skelleton
This commit is contained in:
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
|
||||
}
|
||||
+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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user