From 7aab9ae4266d70344f7d1b2f185c760b19cb6908 Mon Sep 17 00:00:00 2001 From: hackademix Date: Wed, 24 Jul 2019 22:48:02 +0200 Subject: Fix paste sanitization bugs and make it work on drag and drop too. --- src/content/sanitizePaste.js | 71 ++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/src/content/sanitizePaste.js b/src/content/sanitizePaste.js index 703f5b3..a1bcf53 100644 --- a/src/content/sanitizePaste.js +++ b/src/content/sanitizePaste.js @@ -1,58 +1,63 @@ 'use strict'; - -window.addEventListener("paste", e => { - let data = e.clipboardData; - let html = data.getData("text/html"); - let t = e.target; - if (t.nodeType !== 1) t = t.parentElement; - - try { - let node = t.cloneNode(); - - node.innerHTML = html; - - if (sanitizeExtras(node)) { - let sanitized = node.innerHTML; - setTimeout(function() { try { - if (sanitizeExtras(t)) { - console.log(`[NoScript] Sanitized\n\n${html}\nto\n\n${t.innerHTML}\n`, t); +{ + let urlAttributes = ['href', 'to', 'from', 'by', 'values']; + let selector = urlAttributes.map(a => `[${a}]`).join(','); + + for (let evType of ["drop", "paste"]) window.addEventListener(evType, e => { + let container = e.target; + let editing = false; + for (let el = container; el; el = el.parentElement) { + if (el.setRangeText || el.contentEditable) { + editing = true; + break; + } + } + if (!editing) return; + + let html = container.innerHTML; + // we won't touch DOM elements which are already there + let oldNodes = new Set(container.querySelectorAll(selector + ",form")); + window.setTimeout(() => { + // we delay our custom sanitization after the browser performed the paste + // or drop job, rather than replacing it, in order to avoid interferences + // with built-in sanitization + try { + if (sanitizeExtras(container, oldNodes)) { + let t = e.type; + console.log(`[NoScript] Sanitized\n<${t}>\n${html}\nto\n<${t}>\n${container.innerHTML}\n`, container); } } catch(ex) { console.log(ex); - }}, 0); - } - } catch(ex) { - console.log(ex); - } + } + }, 0); + }, true); function removeAttribute(node, name, value = node.getAttribute(name)) { node.setAttribute(`data-noscript-removed-${name}`, value); node.removeAttribute(name); } - function sanitizeExtras(el) { + function sanitizeExtras(container, oldNodes = []) { let ret = false; // remove attributes from forms - for (let f of el.getElementsByTagName("form")) { - for (let a of f.attributes) { - f.removeAttribute(a.name); - ret = true; + for (let f of container.getElementsByTagName("form")) { + if (oldNodes.has(f)) continue; + for (let a of [...f.attributes]) { + removeAttribute(f, a.name); } } - let urlAttributes = ['href', 'to', 'from', 'by', 'values']; - let selector = urlAttributes.map(a => `[${a}]`).join(','); - for (let node of el.querySelectorAll(selector)) { + for (let node of container.querySelectorAll(selector)) { + if (oldNodes.has(node)) continue; for (let name of urlAttributes) { let value = node.getAttribute(name); if (/^\W*(?:(?:javascript|data):|https?:[\s\S]+[[(<])/i.test(unescape(value))) { - node.setAttribute(`data-noscript-removed-${name}`, value); - node.removeAttribute(name); + removeAttribute(node, name, value); ret = true; } } } return ret; } -}, true); +} -- cgit v1.2.3