Project

General

Profile

Download (3.36 KB) Statistics
| Branch: | Tag: | Revision:

haketilo / content / main.js @ 8708ddd3

1
/**
2
 * Myext main content script run in all frames
3
 *
4
 * Copyright (C) 2021 Wojtek Kosior
5
 * Redistribution terms are gathered in the `copyright' file.
6
 */
7

    
8
/*
9
 * IMPORTS_START
10
 * IMPORT handle_page_actions
11
 * IMPORT url_item
12
 * IMPORT url_extract_target
13
 * IMPORT gen_unique
14
 * IMPORT csp_rule
15
 * IMPORT sanitize_attributes
16
 * IMPORT script_suppressor
17
 * IMPORT is_chrome
18
 * IMPORT is_mozilla
19
 * IMPORTS_END
20
 */
21

    
22
/*
23
 * Due to some technical limitations the chosen method of whitelisting sites
24
 * is to smuggle whitelist indicator in page's url as a "magical" string
25
 * after '#'. Right now this is not needed in HTTP(s) pages where native
26
 * script blocking happens through CSP header injection but is needed for
27
 * protocols like ftp:// and file://.
28
 *
29
 * The code that actually injects the magical string into ftp:// and file://
30
 * urls has not yet been added to the extension.
31
 */
32

    
33
let url = url_item(document.URL);
34
let unique = gen_unique(url);
35

    
36
const suppressor = script_suppressor(unique);
37

    
38
function needs_blocking()
39
{
40
    if (url.startsWith("https://") || url.startsWith("http://"))
41
	return false;
42

    
43
    const parsed_url = url_extract_target(document.URL);
44

    
45
    if (parsed_url.target !== undefined &&
46
	parsed_url.target === '#' + unique) {
47
	if (parsed_url.target2 !== undefined)
48
	    window.location.href = parsed_url.base_url + parsed_url.target2;
49
	else
50
	    history.replaceState(null, "", parsed_url.base_url);
51

    
52
	console.log(["allowing whitelisted", document.URL]);
53
	return false;
54
    }
55

    
56
    console.log(["disallowing", document.URL]);
57
    return true;
58
}
59

    
60
function handle_mutation(mutations, observer)
61
{
62
    if (document.readyState === 'complete') {
63
	console.log("mutation handling complete");
64
	observer.disconnect();
65
	return;
66
    }
67
    for (const mutation of mutations) {
68
	for (const node of mutation.addedNodes)
69
	    block_node(node);
70
    }
71
}
72

    
73
function block_nodes_recursively(node)
74
{
75
    block_node(node);
76
    for (const child of node.children)
77
	block_nodes_recursively(child);
78
}
79

    
80
function block_node(node)
81
{
82
    /*
83
     * Modifying <script> element doesn't always prevent its
84
     * execution in some Mozilla browsers. Additional blocking
85
     * through CSP meta tag injection is required.
86
     */
87
    if (node.tagName === "SCRIPT") {
88
	block_script(node);
89
	return;
90
    }
91

    
92
    sanitize_attributes(node);
93

    
94
    if (node.tagName === "HEAD")
95
	inject_csp(node);
96
}
97

    
98
function block_script(node)
99
{
100
    /*
101
     * Disabling scripts this way allows them to still be relatively
102
     * easily accessed in case they contain some useful data.
103
     */
104
    if (node.hasAttribute("type"))
105
	node.setAttribute("blocked-type", node.getAttribute("type"));
106
    node.setAttribute("type", "application/json");
107
}
108

    
109
function inject_csp(head)
110
{
111
    console.log('injecting CSP');
112

    
113
    let meta = document.createElement("meta");
114
    meta.setAttribute("http-equiv", "Content-Security-Policy");
115
    meta.setAttribute("content", csp_rule(unique));
116

    
117
    if (head.firstElementChild === null)
118
	head.appendChild(meta);
119
    else
120
	head.insertBefore(meta, head.firstElementChild);
121
}
122

    
123
if (needs_blocking()) {
124
    block_nodes_recursively(document.documentElement);
125

    
126
    if (is_chrome) {
127
	var observer = new MutationObserver(handle_mutation);
128
	observer.observe(document.documentElement, {
129
	    attributes: true,
130
	    childList: true,
131
	    subtree: true
132
	});
133
    }
134

    
135
    if (is_mozilla)
136
	addEventListener('beforescriptexecute', suppressor, true);
137
}
138

    
139
handle_page_actions(unique);
(2-2/3)