@web/rollup-plugin-html
Version:
Rollup plugin for bundling HTML files
131 lines • 4.48 kB
JavaScript
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
;