Project

General

Profile

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

haketilo / content / main.js @ c483ae19

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_item
13
 * IMPORT url_extract_target
14
 * IMPORT gen_unique
15
 * IMPORT gen_nonce
16
 * IMPORT csp_rule
17
 * IMPORT is_privileged_url
18
 * IMPORT sanitize_attributes
19
 * IMPORT mozilla_suppress_scripts
20
 * IMPORT is_chrome
21
 * IMPORT is_mozilla
22
 * IMPORT start_activity_info_server
23
 * IMPORT set_repo_query_url
24
 * IMPORTS_END
25
 */
26

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

    
38
var nonce = undefined;
39

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

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

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

    
71
    sanitize_attributes(node);
72

    
73
    if (node.tagName === "HEAD")
74
	inject_csp(node);
75
}
76

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

    
88
function inject_csp(head)
89
{
90
    console.log('injecting CSP');
91

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

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

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

    
111
    const policy = targets.current ? targets.policy : {};
112

    
113
    nonce = policy.nonce || gen_nonce();
114
    handle_page_actions(nonce);
115

    
116
    if (!policy.allow) {
117
	block_nodes_recursively(document.documentElement);
118

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

    
128
	if (is_mozilla)
129
	    addEventListener('beforescriptexecute', mozilla_suppress_scripts, true);
130
    }
131

    
132
    start_activity_info_server();
133

    
134
    set_repo_query_url(document.URL);
135
}
(3-3/5)