express-csp-generator
Version:
Content-Security-Policy Generator, Running as an express middleware that integrates with RapidSec.
187 lines (186 loc) • 8.64 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __values = (this && this.__values) || function(o) {
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
if (m) return m.call(o);
if (o && typeof o.length === "number") return {
next: function () {
if (o && i >= o.length) o = void 0;
return { value: o && o[i++], done: !o };
}
};
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.dangerouslyDisableDefaultSrc = exports.getDefaultDirectives = void 0;
var dangerouslyDisableDefaultSrc = Symbol("dangerouslyDisableDefaultSrc");
exports.dangerouslyDisableDefaultSrc = dangerouslyDisableDefaultSrc;
var DEFAULT_DIRECTIVES = {
"default-src": ["'self'"],
"base-uri": ["'self'"],
"block-all-mixed-content": [],
"font-src": ["'self'", "https:", "data:"],
"frame-ancestors": ["'self'"],
"img-src": ["'self'", "data:"],
"object-src": ["'none'"],
"script-src": ["'self'"],
"script-src-attr": ["'none'"],
"style-src": ["'self'", "https:", "'unsafe-inline'"],
"upgrade-insecure-requests": [],
};
var getDefaultDirectives = function () { return (__assign({}, DEFAULT_DIRECTIVES)); };
exports.getDefaultDirectives = getDefaultDirectives;
var dashify = function (str) {
return str.replace(/[A-Z]/g, function (capitalLetter) { return "-" + capitalLetter.toLowerCase(); });
};
var isDirectiveValueInvalid = function (directiveValue) {
return /;|,/.test(directiveValue);
};
var has = function (obj, key) {
return Object.prototype.hasOwnProperty.call(obj, key);
};
function normalizeDirectives(options) {
var e_1, _a;
var _b = options.directives, rawDirectives = _b === void 0 ? getDefaultDirectives() : _b;
var result = [];
var directiveNamesSeen = new Set();
for (var rawDirectiveName in rawDirectives) {
if (!has(rawDirectives, rawDirectiveName)) {
continue;
}
if (rawDirectiveName.length === 0 ||
/[^a-zA-Z0-9-]/.test(rawDirectiveName)) {
throw new Error("Content-Security-Policy received an invalid directive name " + JSON.stringify(rawDirectiveName));
}
var directiveName = dashify(rawDirectiveName);
if (directiveNamesSeen.has(directiveName)) {
throw new Error("Content-Security-Policy received a duplicate directive " + JSON.stringify(directiveName));
}
directiveNamesSeen.add(directiveName);
var rawDirectiveValue = rawDirectives[rawDirectiveName];
var directiveValue = void 0;
if (typeof rawDirectiveValue === "string") {
directiveValue = [rawDirectiveValue];
}
else if (!rawDirectiveValue) {
throw new Error("Content-Security-Policy received an invalid directive value for " + JSON.stringify(directiveName));
}
else if (rawDirectiveValue === dangerouslyDisableDefaultSrc) {
if (directiveName === "default-src") {
continue;
}
else {
throw new Error("Content-Security-Policy: tried to disable " + JSON.stringify(directiveName) + " as if it were default-src; simply omit the key");
}
}
else {
directiveValue = rawDirectiveValue;
}
try {
for (var directiveValue_1 = (e_1 = void 0, __values(directiveValue)), directiveValue_1_1 = directiveValue_1.next(); !directiveValue_1_1.done; directiveValue_1_1 = directiveValue_1.next()) {
var element = directiveValue_1_1.value;
if (typeof element === "string" && isDirectiveValueInvalid(element)) {
throw new Error("Content-Security-Policy received an invalid directive value for " + JSON.stringify(directiveName));
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (directiveValue_1_1 && !directiveValue_1_1.done && (_a = directiveValue_1.return)) _a.call(directiveValue_1);
}
finally { if (e_1) throw e_1.error; }
}
result.push({ directiveName: directiveName, directiveValue: directiveValue });
}
if (!result.length) {
throw new Error("Content-Security-Policy has no directives. Either set some or disable the header");
}
if (!directiveNamesSeen.has("default-src")) {
throw new Error("Content-Security-Policy needs a default-src but none was provided");
}
return result;
}
function getHeaderValue(req, res, normalizedDirectives) {
var e_2, _a, e_3, _b;
var result = [];
try {
for (var normalizedDirectives_1 = __values(normalizedDirectives), normalizedDirectives_1_1 = normalizedDirectives_1.next(); !normalizedDirectives_1_1.done; normalizedDirectives_1_1 = normalizedDirectives_1.next()) {
var _c = normalizedDirectives_1_1.value, directiveName = _c.directiveName, rawDirectiveValue = _c.directiveValue;
var directiveValue = "";
try {
for (var rawDirectiveValue_1 = (e_3 = void 0, __values(rawDirectiveValue)), rawDirectiveValue_1_1 = rawDirectiveValue_1.next(); !rawDirectiveValue_1_1.done; rawDirectiveValue_1_1 = rawDirectiveValue_1.next()) {
var element = rawDirectiveValue_1_1.value;
directiveValue +=
" " + (element instanceof Function ? element(req, res) : element);
}
}
catch (e_3_1) { e_3 = { error: e_3_1 }; }
finally {
try {
if (rawDirectiveValue_1_1 && !rawDirectiveValue_1_1.done && (_b = rawDirectiveValue_1.return)) _b.call(rawDirectiveValue_1);
}
finally { if (e_3) throw e_3.error; }
}
if (!directiveValue) {
result.push(directiveName);
}
else if (isDirectiveValueInvalid(directiveValue)) {
return new Error("Content-Security-Policy received an invalid directive value for " + JSON.stringify(directiveName));
}
else {
result.push("" + directiveName + directiveValue);
}
}
}
catch (e_2_1) { e_2 = { error: e_2_1 }; }
finally {
try {
if (normalizedDirectives_1_1 && !normalizedDirectives_1_1.done && (_a = normalizedDirectives_1.return)) _a.call(normalizedDirectives_1);
}
finally { if (e_2) throw e_2.error; }
}
return result.join(";");
}
var contentSecurityPolicy = function contentSecurityPolicy(options) {
if (options === void 0) { options = {}; }
if ("loose" in options) {
console.warn("Content-Security-Policy middleware no longer needs the `loose` parameter. You should remove it.");
}
if ("setAllHeaders" in options) {
console.warn("Content-Security-Policy middleware no longer supports the `setAllHeaders` parameter. See <https://github.com/helmetjs/helmet/wiki/Setting-legacy-Content-Security-Policy-headers-in-Helmet-4>.");
}
["disableAndroid", "browserSniff"].forEach(function (deprecatedOption) {
if (deprecatedOption in options) {
console.warn("Content-Security-Policy middleware no longer does browser sniffing, so you can remove the `" + deprecatedOption + "` option. See <https://github.com/helmetjs/csp/issues/97> for discussion.");
}
});
var headerName = options.reportOnly
? "Content-Security-Policy-Report-Only"
: "Content-Security-Policy";
var normalizedDirectives = normalizeDirectives(options);
return function contentSecurityPolicyMiddleware(req, res, next) {
var result = getHeaderValue(req, res, normalizedDirectives);
if (result instanceof Error) {
next(result);
}
else {
res.setHeader(headerName, result);
next();
}
};
};
contentSecurityPolicy.getDefaultDirectives = getDefaultDirectives;
contentSecurityPolicy.dangerouslyDisableDefaultSrc = dangerouslyDisableDefaultSrc;
module.exports = contentSecurityPolicy;
exports.default = contentSecurityPolicy;