/** * Helper functions for blocking scripts in pages, based off NoScript's lib/DocumentFreezer.js * * Copyright (C) 2005-2021 Giorgio Maone - https://maone.net * Copyright (C) 2021 jahoti * Redistribution terms are gathered in the `copyright' file. */ const loaderAttributes = ["href", "src", "data"]; const jsOrDataUrlRx = /^(?:data:(?:[^,;]*ml|unknown-content-type)|javascript:)/i; function sanitize_attributes(element) { if (element._frozen) return; let fa = []; let loaders = []; let attributes = element.attributes || []; for (let a of attributes) { let name = a.localName.toLowerCase(); if (loaderAttributes.includes(name)) if (jsOrDataUrlRx.test(a.value)) loaders.push(a); else if (name.startsWith("on")) { console.debug("Removing", a, element.outerHTML); fa.push(a.cloneNode()); a.value = ""; element[name] = null; } } if (loaders.length) { for (let a of loaders) { fa.push(a.cloneNode()); a.value = "javascript://frozen"; } if ("contentWindow" in element) element.replaceWith(element = element.cloneNode(true)); } if (fa.length) element._frozenAttributes = fa; element._frozen = true; } function script_suppressor(nonce) { const blockExecute = e => { if (document.readyState === 'complete') { removeEventListener('beforescriptexecute', blockExecute, true); return; } else if (e.isTrusted && e.target.getAttribute('nonce') !== nonce) { // Prevent blocking of injected scripts e.preventDefault(); console.log('Suppressed script', e.target); } }; return blockExecute; }; /* * EXPORTS_START * EXPORT script_suppressor * EXPORT sanitize_attributes * EXPORTS_END */