diff options
Diffstat (limited to 'content')
-rw-r--r-- | content/freezer.js | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/content/freezer.js b/content/freezer.js new file mode 100644 index 0000000..cdd0709 --- /dev/null +++ b/content/freezer.js @@ -0,0 +1,63 @@ +/** + * 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. + */ + +"use strict"; + +(() => { + const loaderAttributes = ["href", "src", "data"]; + const jsOrDataUrlRx = /^(?:data:(?:[^,;]*ml|unknown-content-type)|javascript:)/i; + + function sanitizeAttributes(element) { + if (element._frozen) + return; + let fa = []; + let loaders = []; + for (let a of element.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 scriptSuppressor(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; + }; + + window.scriptSuppressor = scriptSuppressor; + window.sanitize_attributes = sanitizeAttributes; +})(); |