Project

General

Profile

« Previous | Next » 

Revision 261548ff

Added by koszko about 2 years ago

emply an sh-based build system; make some changes to blocking

View differences:

content/main.js
5 5
 * Redistribution terms are gathered in the `copyright' file.
6 6
 */
7 7

  
8
"use strict";
8
/*
9
 * IMPORTS_START
10
 * IMPORT handle_page_actions
11
 * IMPORT url_item
12
 * IMPORT gen_unique
13
 * IMPORT sanitize_attributes
14
 * IMPORT script_suppressor
15
 * IMPORT is_chrome
16
 * IMPORT is_mozilla
17
 * IMPORTS_END
18
 */
9 19

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

  
16
    /*
17
     * Due to some technical limitations the chosen method of whitelisting sites
18
     * is to smuggle whitelist indicator in page's url as a "magical" string
19
     * after '#'. Right now this is not needed in HTTP(s) pages where native
20
     * script blocking happens through CSP header injection but is needed for
21
     * protocols like ftp:// and file://.
22
     *
23
     * The code that actually injects the magical string into ftp:// and file://
24
     * urls has not yet been added to the extension.
25
     */
31
let url = url_item(document.URL);
32
let unique = gen_unique(url);
33
let nonce = unique.substring(1);
26 34

  
27
    let url = url_item(document.URL);
28
    let unique = gen_unique(url);
29
    let nonce = unique.substring(1);
30
    
31
    const scriptSuppressor = window.scriptSuppressor(nonce);
32

  
33
    function needs_blocking()
34
    {
35
	if (url.startsWith("https://") || url.startsWith("http://"))
36
	    return false;
37

  
38
	let url_re = /^([^#]*)((#[^#]*)(#.*)?)?$/;
39
	let match = url_re.exec(document.URL);
40
	let base_url = match[1];
41
	let first_target = match[3];
42
	let second_target = match[4];
43

  
44
	if (first_target !== undefined &&
45
	    first_target === unique) {
46
	    if (second_target !== undefined)
47
		window.location.href = base_url + second_target;
48
	    else
49
		history.replaceState(null, "", base_url);
50

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

  
55
	console.log(["disallowing", document.URL]);
56
	return true;
57
    }
35
const suppressor = script_suppressor(nonce);
36

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

  
59
    function handle_mutation(mutations, observer)
60
    {
61
	if (document.readyState === 'complete') {
62
	    console.log("complete");
63
	    observer.disconnect();
64
	    return;
65
	}
66
	for (let mutation of mutations) {
67
	    for (let node of mutation.addedNodes) {
68
		/*
69
		 * Modifying <script> element doesn't always prevent its
70
		 * execution in some Mozilla browsers. Additional blocking
71
		 * through CSP meta tag injection is required.
72
		 */
73
		if (node.tagName === "SCRIPT") {
74
		    block_script(node);
75
		    continue;
76
		}
77

  
78
		sanitize_attributes(node);
79

  
80
		if (node.tagName === "HEAD")
81
		    inject_csp(node);
82
	    }
83
	}
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;
84 57
    }
85 58

  
86
    function block_script(node)
87
    {
88
	console.log(node);
89

  
90
	/*
91
	 * Disabling scripts this way allows them to still be relatively
92
	 * easily accessed in case they contain some useful data.
93
	 */
94
	if (node.hasAttribute("type"))
95
	    node.setAttribute("blocked-type", node.getAttribute("type"));
96
	node.setAttribute("type", "application/json");
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;
97 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
}
98 82

  
99
    function inject_csp(node)
100
    {
101
	console.log('injecting CSP');
102
	let meta = document.createElement("meta");
103
	meta.setAttribute("http-equiv", "Content-Security-Policy");
104
	meta.setAttribute("content", `\
105
script-src 'nonce-${nonce}'; \
106
script-src-elem 'nonce-${nonce}';\
107
`);
108
	node.appendChild(meta);
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;
109 93
    }
110 94

  
111
    if (needs_blocking()) {
112
	// Script blocking for Gecko
113
	addEventListener('beforescriptexecute', scriptSuppressor, true);
114
	
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

  
119
    let rule = `script-src 'nonce-${nonce}'; `;
120
    if (is_chrome)
121
	rule += `script-src-elem 'nonce-${nonce}';`;
122

  
123
    meta.setAttribute("content", rule);
124

  
125
    if (head.firstElementChild === null)
126
	head.appendChild(meta);
127
    else
128
	head.insertBefore(meta, head.firstElementChild);
129
}
130

  
131
if (needs_blocking()) {
132
    block_nodes_recursively(document.documentElement);
133

  
134
    if (is_chrome) {
115 135
	var observer = new MutationObserver(handle_mutation);
116 136
	observer.observe(document.documentElement, {
117 137
	    attributes: true,
......
120 140
	});
121 141
    }
122 142

  
123
    handle_page_actions(nonce);
124
})();
143
    if (is_mozilla)
144
	addEventListener('beforescriptexecute', suppressor, true);
145
}
146

  
147
handle_page_actions(nonce);

Also available in: Unified diff