Project

General

Profile

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

haketilo / content / main.js @ efd6ae83

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

    
9
/*
10
 * IMPORTS_START
11
 * IMPORT handle_page_actions
12
 * IMPORT url_extract_target
13
 * IMPORT gen_unique
14
 * IMPORT gen_nonce
15
 * IMPORT csp_rule
16
 * IMPORT is_privileged_url
17
 * IMPORT sanitize_attributes
18
 * IMPORT mozilla_suppress_scripts
19
 * IMPORT is_chrome
20
 * IMPORT is_mozilla
21
 * IMPORT start_activity_info_server
22
 * IMPORTS_END
23
 */
24

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

    
36
var nonce = undefined;
37

    
38
function handle_mutation(mutations, observer)
39
{
40
    if (document.readyState === 'complete') {
41
	console.log("mutation handling complete");
42
	observer.disconnect();
43
	return;
44
    }
45
    for (const mutation of mutations) {
46
	for (const node of mutation.addedNodes)
47
	    block_node(node);
48
    }
49
}
50

    
51
function block_nodes_recursively(node)
52
{
53
    block_node(node);
54
    for (const child of node.children)
55
	block_nodes_recursively(child);
56
}
57

    
58
function block_node(node)
59
{
60
    /*
61
     * Modifying <script> element doesn't always prevent its execution in some
62
     * Mozilla browsers. This is Chromium-specific code.
63
     */
64
    if (node.tagName === "SCRIPT") {
65
	block_script(node);
66
	return;
67
    }
68

    
69
    sanitize_attributes(node);
70

    
71
    if (node.tagName === "HEAD")
72
	inject_csp(node);
73
}
74

    
75
function block_script(node)
76
{
77
    /*
78
     * Disabling scripts this way allows them to still be relatively
79
     * easily accessed in case they contain some useful data.
80
     */
81
    if (node.hasAttribute("type"))
82
	node.setAttribute("blocked-type", node.getAttribute("type"));
83
    node.setAttribute("type", "application/json");
84
}
85

    
86
function inject_csp(head)
87
{
88
    console.log('injecting CSP');
89

    
90
    let meta = document.createElement("meta");
91
    meta.setAttribute("http-equiv", "Content-Security-Policy");
92
    meta.setAttribute("content", csp_rule(nonce));
93

    
94
    if (head.firstElementChild === null)
95
	head.appendChild(meta);
96
    else
97
	head.insertBefore(meta, head.firstElementChild);
98
}
99

    
100
if (!is_privileged_url(document.URL)) {
101
    const targets = url_extract_target(document.URL);
102
    if (targets.policy) {
103
	if (targets.target2)
104
	    window.location.href = targets.base_url + targets.target2;
105
	else
106
	    history.replaceState(null, "", targets.base_url);
107
    }
108

    
109
    const policy = targets.current ? targets.policy : {};
110

    
111
    nonce = policy.nonce || gen_nonce();
112
    handle_page_actions(nonce);
113

    
114
    if (!policy.allow) {
115
	block_nodes_recursively(document.documentElement);
116

    
117
	if (is_chrome) {
118
	    var observer = new MutationObserver(handle_mutation);
119
	    observer.observe(document.documentElement, {
120
		attributes: true,
121
		childList: true,
122
		subtree: true
123
	    });
124
	}
125

    
126
	if (is_mozilla)
127
	    addEventListener('beforescriptexecute', mozilla_suppress_scripts, true);
128
    }
129

    
130
    start_activity_info_server();
131
}
(3-3/4)