UNPKG

@web/rollup-plugin-html

Version:
131 lines 4.48 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.hashInlineScripts = void 0; const parse5_utils_1 = require("@web/parse5-utils"); const crypto_1 = __importDefault(require("crypto")); function isMetaCSPTag(node) { if ((0, parse5_utils_1.getTagName)(node) === 'meta' && (0, parse5_utils_1.getAttribute)(node, 'http-equiv') === 'Content-Security-Policy') { return true; } return false; } function isInlineScript(node) { if ((0, parse5_utils_1.getTagName)(node) === 'script' && !(0, parse5_utils_1.hasAttribute)(node, 'src')) { return true; } return false; } /** * Parses Meta CSP Content string as an object so we can easily mutate it in JS * E.g.: * * "default-src 'self'; prefetch-src 'self'; upgrade-insecure-requests; style-src 'self' 'unsafe-inline';" * * becomes * * { * 'default-src': ["'self'"], * 'prefetch-src': ["'self'"], * 'upgrade-insecure-requests': [], * 'style-src': ["'self'", "'unsafe-inline'"] * } * */ function parseMetaCSPContent(content) { return content.split(';').reduce((acc, curr) => { const trimmed = curr.trim(); if (!trimmed) { return acc; } const splitItem = trimmed.split(' '); const [, ...values] = splitItem; return Object.assign(Object.assign({}, acc), { [splitItem[0]]: values }); }, {}); } /** * Serializes * * { * 'default-src': ["'self'"], * 'prefetch-src': ["'self'"], * 'upgrade-insecure-requests': [], * 'style-src': ["'self'", "'unsafe-inline'"] * } * * back to * * "default-src 'self'; prefetch-src 'self'; upgrade-insecure-requests; style-src 'self' 'unsafe-inline';" */ function serializeMetaCSPContent(data) { const dataEntries = Object.entries(data); return dataEntries.reduce((accOuter, currOuter, indexOuter) => { let suffixOuter = ' '; let sep = ' '; // If there are no items for this key if (currOuter[1].length === 0) { suffixOuter = '; '; sep = ''; } // Don't insert space suffix when it is the last item if (indexOuter === dataEntries.length - 1) { suffixOuter = ''; } return `${accOuter}${currOuter[0]}${sep}${currOuter[1].reduce((accInner, currInner, indexInner) => { let suffixInner = ' '; if (indexInner === currOuter[1].length - 1) { suffixInner = ';'; } return `${accInner}${currInner}${suffixInner}`; }, '')}${suffixOuter}`; }, ''); } function injectCSPScriptRules(metaCSPEl, hashes) { const content = (0, parse5_utils_1.getAttribute)(metaCSPEl, 'content'); if (content) { const data = parseMetaCSPContent(content); if (Array.isArray(data['script-src'])) { data['script-src'].push(...hashes); } else { data['script-src'] = ["'self'", ...hashes]; } const newContent = serializeMetaCSPContent(data); (0, parse5_utils_1.setAttribute)(metaCSPEl, 'content', newContent); } } function injectCSPMetaTag(document, hashes) { const metaTag = (0, parse5_utils_1.createElement)('meta', { 'http-equiv': 'Content-Security-Policy', content: `script-src 'self' ${hashes.join(' ')};`, }); const head = (0, parse5_utils_1.findNode)(document, node => node.nodeName === 'head'); if (head) { (0, parse5_utils_1.prepend)(head, metaTag); } } function hashInlineScripts(document) { const metaCSPEl = (0, parse5_utils_1.findElement)(document, isMetaCSPTag); const inlineScripts = (0, parse5_utils_1.findElements)(document, isInlineScript); const hashes = []; inlineScripts.forEach(node => { if (node.childNodes[0]) { const scriptContent = (0, parse5_utils_1.getTextContent)(node.childNodes[0]); const hash = crypto_1.default.createHash('sha256').update(scriptContent).digest('base64'); hashes.push(`'sha256-${hash}'`); } }); if (hashes.length > 0) { if (metaCSPEl) { injectCSPScriptRules(metaCSPEl, hashes); } else { injectCSPMetaTag(document, hashes); } } } exports.hashInlineScripts = hashInlineScripts; //# sourceMappingURL=hashInlineScripts.js.map