1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
'use strict';
{
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;
// 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 {
let html = container.innerHTML;
if (sanitizeExtras(container, oldNodes)) {
let t = e.type.toUpperCase();
console.log(`[NoScript] Sanitized\n<${t}>\n${html}\n</${t}>\nto\n<${t}>\n${container.innerHTML}\n</${t}>`, container);
}
} 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(container, oldNodes = []) {
let ret = false;
// remove attributes from forms
for (let f of container.getElementsByTagName("form")) {
if (oldNodes.has(f)) continue;
for (let a of [...f.attributes]) {
removeAttribute(f, a.name);
}
}
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))) {
removeAttribute(node, name, value);
ret = true;
}
}
}
return ret;
}
}
|