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
125
126
127
|
/**
* Myext main content script run in all frames
*
* Copyright (C) 2021 Wojtek Kosior
*
* Dual-licensed under:
* - 0BSD license
* - GPLv3 or (at your option) any later version
*/
"use strict";
(() => {
const handle_page_actions = window.handle_page_actions;
const url_item = window.url_item;
const gen_unique = window.gen_unique;
var url_re = /^([^#]*)((#[^#]*)(#.*)?)?$/;
var match = url_re.exec(document.URL);
var base_url = match[1];
var first_target = match[3];
var second_target = match[4];
// TODO: can be refactored *a little bit* with policy_smuggler.js
let url = url_item(document.URL);
let unique = gen_unique(url);
let nonce = unique.substring(1);
var block = true;
if (first_target !== undefined &&
first_target === unique) {
block = false;
console.log(["allowing", document.URL]);
if (second_target !== undefined)
window.location.href = base_url + second_target;
else
history.replaceState(null, "", base_url);
} else {
console.log(["not allowing", document.URL]);
}
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);
}
function sanitize_attributes(node)
{
if (node.attributes === undefined)
return;
/*
* We have to do it in 2 loops, removing attribute modifies
* our iterator
*/
let attr_names = [];
for (let attr of node.attributes) {
let attr_name = attr.localName;
if (attr_name.startsWith("on"))
attr_names.push(attr_name);
}
for (let attr_name of attr_names) {
node.removeAttribute(attr_name);
console.log("sanitized", attr_name);
}
}
if (block) {
var observer = new MutationObserver(handle_mutation);
observer.observe(document.documentElement, {
attributes: true,
childList: true,
subtree: true
});
}
handle_page_actions(nonce);
})();
|