"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDefaultDirectives = void 0;
const 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": [],
};
const getDefaultDirectives = () => (Object.assign({}, DEFAULT_DIRECTIVES));
exports.getDefaultDirectives = getDefaultDirectives;
const isRawPolicyDirectiveNameInvalid = (rawDirectiveName) => rawDirectiveName.length === 0 || /[^a-zA-Z0-9-]/.test(rawDirectiveName);
const dashify = (str) => str.replace(/[A-Z]/g, (capitalLetter) => "-" + capitalLetter.toLowerCase());
const isDirectiveValueInvalid = (directiveValue) => /;|,/.test(directiveValue);
const has = (obj, key) => Object.prototype.hasOwnProperty.call(obj, key);
function getHeaderName({ reportOnly, }) {
    if (reportOnly) {
        return "Content-Security-Policy-Report-Only";
    }
    else {
        return "Content-Security-Policy";
    }
}
function normalizeDirectives(options) {
    const result = {};
    const { directives: rawDirectives = getDefaultDirectives() } = options;
    for (const rawDirectiveName in rawDirectives) {
        if (!has(rawDirectives, rawDirectiveName)) {
            continue;
        }
        if (isRawPolicyDirectiveNameInvalid(rawDirectiveName)) {
            throw new Error(`Content-Security-Policy received an invalid directive name ${JSON.stringify(rawDirectiveName)}`);
        }
        const directiveName = dashify(rawDirectiveName);
        if (has(result, directiveName)) {
            throw new Error(`Content-Security-Policy received a duplicate directive ${JSON.stringify(directiveName)}`);
        }
        const rawDirectiveValue = rawDirectives[rawDirectiveName];
        let directiveValue;
        if (typeof rawDirectiveValue === "string") {
            directiveValue = [rawDirectiveValue];
        }
        else {
            directiveValue = rawDirectiveValue;
        }
        for (const element of directiveValue) {
            if (typeof element === "string" && isDirectiveValueInvalid(element)) {
                throw new Error(`Content-Security-Policy received an invalid directive value for ${JSON.stringify(directiveName)}`);
            }
        }
        result[directiveName] = directiveValue;
    }
    if (!("default-src" in result)) {
        throw new Error("Content-Security-Policy needs a default-src but none was provided");
    }
    return result;
}
function getHeaderValue(req, res, directives) {
    const result = [];
    for (const directiveName in directives) {
        if (!has(directives, directiveName)) {
            continue;
        }
        const rawDirectiveValue = directives[directiveName];
        let directiveValue = "";
        for (const element of rawDirectiveValue) {
            if (element instanceof Function) {
                directiveValue += " " + element(req, res);
            }
            else {
                directiveValue += " " + element;
            }
        }
        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}`);
        }
    }
    return result.join(";");
}
function contentSecurityPolicy(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((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.`);
        }
    });
    const headerName = getHeaderName(options);
    const directives = normalizeDirectives(options);
    return function contentSecurityPolicyMiddleware(req, res, next) {
        const result = getHeaderValue(req, res, directives);
        if (result instanceof Error) {
            next(result);
        }
        else {
            res.setHeader(headerName, result);
            next();
        }
    };
}
contentSecurityPolicy.getDefaultDirectives = getDefaultDirectives;
module.exports = contentSecurityPolicy;
exports.default = contentSecurityPolicy;
