1/** 2 * @file 3 * CSP Nonce Propagation. 4 * 5 * This script ensures that dynamically created elements (scripts, styles) 6 * and HTML injections (innerHTML, insertAdjacentHTML) inherit the CSP nonce. 7 */ 8 9(function () { 10 'use strict'; 11 // Recovery of the nonce from the script tag itself. 12 const scriptTag = document.currentScript; 13 const nonce = scriptTag ? scriptTag.nonce || scriptTag.getAttribute('nonce') : null; 14 15 if (!nonce) { 16 return; 17 } 18 19 const tags = ['script', 'style']; 20 21 /** 22 * Processes dynamic HTML strings to inject nonces and transform style attributes. 23 * 24 * @param {string} text 25 * The HTML string to process. 26 * @param {string} nonceValue 27 * The nonce to inject. 28 * 29 * @return {string} 30 * The processed HTML string. 31 */ 32 const processDynamicHtml = function (text, nonceValue) { 33 if (typeof text !== 'string') { 34 return text; 35 } 36 37 // 1. Nonce script/style tags. 38 let processedText = text.replace(/<(script|style)\b([^>]*)/gi, (m, tag, attrs) => { 39 if (attrs.indexOf('nonce=') === -1) { 40 return `<${tag}${attrs} nonce="${nonceValue}"`; 41 } 42 return m; 43 }); 44 45 // 2. Transform style attributes to <style> tags (for CSP strict-dynamic compatibility). 46 processedText = processedText.replace(/<([a-z0-9]+)\b([^>]*)\bstyle=["']([^"']+)["']([^>]*)>/gi, (m, tag, before, val, after) => { 47 if (before.includes('data-csp-id=') || after.includes('data-csp-id=')) { 48 return m; 49 } 50 const id = `style-${Math.random().toString(36).substr(2, 9)}`; 51 return `<${tag}${before} data-csp-id="${id}" style=""${after}><style nonce="${nonceValue}">[${tag}][data-csp-id="${id}"] { ${val} !important; }</style>`; 52 }); 53 54 return processedText; 55 }; 56 57 // 1. Override createElement to handle manual element creation. 58 const oldCE = document.createElement; 59 document.createElement = function (tagName) { 60 const el = oldCE.apply(this, arguments); 61 if (tags.includes(tagName.toLowerCase())) { 62 el.setAttribute('nonce', nonce); 63 } 64 return el; 65 }; 66 67 // 2. Override insertAdjacentHTML to handle string-based injections. 68 const oldIAH = Element.prototype.insertAdjacentHTML; 69 Element.prototype.insertAdjacentHTML = function (pos, text) { 70 const processed = processDynamicHtml(text, nonce); 71 return oldIAH.call(this, pos, processed); 72 }; 73 74 // 3. Override innerHTML setter for template-based injections (CKEditor, AJAX). 75 const desc = Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML'); 76 if (desc && desc.set) { 77 Object.defineProperty(Element.prototype, 'innerHTML', { 78 set: function (text) { 79 const processed = processDynamicHtml(text, nonce); 80 desc.set.call(this, processed); 81 }, 82 get: function () { 83 return desc.get.call(this); 84 }, 85 configurable: true, 86 }); 87 } 88 89})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.