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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
/**
* Myext main content script run in all frames
*
* Copyright (C) 2021 Wojtek Kosior
* Redistribution terms are gathered in the `copyright' file.
*/
"use strict";
(() => {
const handle_page_actions = window.handle_page_actions;
const url_item = window.url_item;
const gen_unique = window.gen_unique;
const sanitize_attributes = window.sanitize_attributes;
/*
* Due to some technical limitations the chosen method of whitelisting sites
* is to smuggle whitelist indicator in page's url as a "magical" string
* after '#'. Right now this is not needed in HTTP(s) pages where native
* script blocking happens through CSP header injection but is needed for
* protocols like ftp:// and file://.
*
* The code that actually injects the magical string into ftp:// and file://
* urls has not yet been added to the extension.
*/
let url = url_item(document.URL);
let unique = gen_unique(url);
let nonce = unique.substring(1);
const scriptSuppressor = window.scriptSuppressor(nonce);
function needs_blocking()
{
if (url.startsWith("https://") || url.startsWith("http://"))
return false;
let url_re = /^([^#]*)((#[^#]*)(#.*)?)?$/;
let match = url_re.exec(document.URL);
let base_url = match[1];
let first_target = match[3];
let second_target = match[4];
if (first_target !== undefined &&
first_target === unique) {
if (second_target !== undefined)
window.location.href = base_url + second_target;
else
history.replaceState(null, "", base_url);
console.log(["allowing whitelisted", document.URL]);
return false;
}
console.log(["disallowing", document.URL]);
return true;
}
function handle_mutation(mutations, observer)
{
if (document.readyState === 'complete') {
console.log("complete");
observer.disconnect();
return;
}
for (let mutation of mutations) {
for (let node of mutation.addedNodes) {
/*
* Modifying <script> element doesn't always prevent its
* execution in some Mozilla browsers. Additional blocking
* through CSP meta tag injection is required.
*/
if (node.tagName === "SCRIPT") {
block_script(node);
continue;
}
sanitize_attributes(node);
if (node.tagName === "HEAD")
inject_csp(node);
}
}
}
function block_script(node)
{
console.log(node);
/*
* Disabling scripts this way allows them to still be relatively
* easily accessed in case they contain some useful data.
*/
if (node.hasAttribute("type"))
node.setAttribute("blocked-type", node.getAttribute("type"));
node.setAttribute("type", "application/json");
}
function inject_csp(node)
{
console.log('injecting CSP');
let meta = document.createElement("meta");
meta.setAttribute("http-equiv", "Content-Security-Policy");
meta.setAttribute("content", `\
script-src 'nonce-${nonce}'; \
script-src-elem 'nonce-${nonce}';\
`);
node.appendChild(meta);
}
if (needs_blocking()) {
// Script blocking for Gecko
addEventListener('beforescriptexecute', scriptSuppressor, true);
var observer = new MutationObserver(handle_mutation);
observer.observe(document.documentElement, {
attributes: true,
childList: true,
subtree: true
});
}
handle_page_actions(nonce);
})();
|