UNPKG

express-csp-generator

Version:

Content-Security-Policy Generator, Running as an express middleware that integrates with RapidSec.

187 lines (186 loc) 8.64 kB
"use strict"; 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;