Project

General

Profile

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

haketilo / content / main.js @ cd5272ac

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 gen_unique
13
 * IMPORT csp_rule
14
 * IMPORT sanitize_attributes
15
 * IMPORT script_suppressor
16
 * IMPORT is_chrome
17
 * IMPORT is_mozilla
18
 * IMPORTS_END
19
 */
20

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

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

    
35
const suppressor = script_suppressor(unique);
36

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

    
42
    let url_re = /^([^#]*)((#[^#]*)(#.*)?)?$/;
43
    let match = url_re.exec(document.URL);
44
    let base_url = match[1];
45
    let first_target = match[3];
46
    let second_target = match[4];
47

    
48
    if (first_target !== undefined &&
49
	first_target === '#' + unique) {
50
	if (second_target !== undefined)
51
	    window.location.href = base_url + second_target;
52
	else
53
	    history.replaceState(null, "", base_url);
54

    
55
	console.log(["allowing whitelisted", document.URL]);
56
	return false;
57
    }
58

    
59
    console.log(["disallowing", document.URL]);
60
    return true;
61
}
62

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

    
76
function block_nodes_recursively(node)
77
{
78
    block_node(node);
79
    for (const child of node.children)
80
	block_nodes_recursively(child);
81
}
82

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

    
95
    sanitize_attributes(node);
96

    
97
    if (node.tagName === "HEAD")
98
	inject_csp(node);
99
}
100

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

    
112
function inject_csp(head)
113
{
114
    console.log('injecting CSP');
115

    
116
    let meta = document.createElement("meta");
117
    meta.setAttribute("http-equiv", "Content-Security-Policy");
118
    meta.setAttribute("content", csp_rule(unique));
119

    
120
    if (head.firstElementChild === null)
121
	head.appendChild(meta);
122
    else
123
	head.insertBefore(meta, head.firstElementChild);
124
}
125

    
126
if (needs_blocking()) {
127
    block_nodes_recursively(document.documentElement);
128

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

    
138
    if (is_mozilla)
139
	addEventListener('beforescriptexecute', suppressor, true);
140
}
141

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