Project

General

Profile

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

haketilo / content / main.js @ 5b419aed

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
 * IMPORT sanitize_csp_header
23
 * IMPORTS_END
24
 */
25

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

    
37
var nonce = undefined;
38

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

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

    
59
function block_node(node)
60
{
61
    /*
62
     * Modifying <script> element doesn't always prevent its execution in some
63
     * Mozilla browsers. This is Chromium-specific code.
64
     */
65
    if (node.tagName === "SCRIPT") {
66
	block_script(node);
67
	return;
68
    }
69
    
70
    else if (node.tagName === 'META' &&
71
	node.getAttribute('http-equiv') === 'content-security-policy') {
72
	
73
	node.content = sanitize_csp_header(
74
	    {value: node.content},
75
	    `'nonce-${nonce}'`,
76
	    !policy.allow
77
	).value;
78
	return;
79
    }
80

    
81
    sanitize_attributes(node);
82

    
83
    if (node.tagName === "HEAD")
84
	inject_csp(node);
85
}
86

    
87
function block_script(node)
88
{
89
    /*
90
     * Disabling scripts this way allows them to still be relatively
91
     * easily accessed in case they contain some useful data.
92
     */
93
    if (node.hasAttribute("type"))
94
	node.setAttribute("blocked-type", node.getAttribute("type"));
95
    node.setAttribute("type", "application/json");
96
}
97

    
98
function inject_csp(head)
99
{
100
    console.log('injecting CSP');
101

    
102
    let meta = document.createElement("meta");
103
    meta.setAttribute("http-equiv", "Content-Security-Policy");
104
    meta.setAttribute("content", csp_rule(nonce));
105

    
106
    if (head.firstElementChild === null)
107
	head.appendChild(meta);
108
    else
109
	head.insertBefore(meta, head.firstElementChild);
110
}
111

    
112
if (!is_privileged_url(document.URL)) {
113
    const targets = url_extract_target(document.URL);
114
    if (targets.policy) {
115
	if (targets.target2)
116
	    window.location.href = targets.base_url + targets.target2;
117
	else
118
	    history.replaceState(null, "", targets.base_url);
119
    }
120

    
121
    const policy = targets.current ? targets.policy : {};
122

    
123
    nonce = policy.nonce || gen_nonce();
124
    handle_page_actions(nonce);
125

    
126
    if (!policy.allow) {
127
	block_nodes_recursively(document.documentElement);
128

    
129
	/* Now needed on Mozilla as well to sanitize CSP header */
130
	var observer = new MutationObserver(handle_mutation);
131
	observer.observe(document.documentElement, {
132
	    attributes: true,
133
	    childList: true,
134
	    subtree: true
135
	});
136

    
137
	if (is_mozilla)
138
	    addEventListener('beforescriptexecute', mozilla_suppress_scripts, true);
139
    }
140

    
141
    start_activity_info_server();
142
}
(3-3/4)