Project

General

Profile

« Previous | Next » 

Revision d09b7ee1

Added by koszko about 2 years ago

sanitize `' tags containing CSP rules under Chromium

This commit adds a mechanism of hijacking document when it loads and injecting sanitized nodes to the DOM from the level of content script.

View differences:

content/main.js
11 11
 * IMPORT handle_page_actions
12 12
 * IMPORT extract_signed
13 13
 * IMPORT gen_nonce
14
 * IMPORT csp_rule
15 14
 * IMPORT is_privileged_url
16
 * IMPORT sanitize_attributes
17 15
 * IMPORT mozilla_suppress_scripts
18 16
 * IMPORT is_chrome
19 17
 * IMPORT is_mozilla
20 18
 * IMPORT start_activity_info_server
19
 * IMPORT modify_on_the_fly
21 20
 * IMPORTS_END
22 21
 */
23 22

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

  
35
var nonce = undefined;
36

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

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

  
57
function block_node(node)
23
function accept_node(node, parent)
58 24
{
25
    const clone = document.importNode(node, false);
26
    node.hachette_corresponding = clone;
59 27
    /*
60
     * Modifying <script> element doesn't always prevent its execution in some
61
     * Mozilla browsers. This is Chromium-specific code.
28
     * TODO: Stop page's own issues like "Error parsing a meta element's
29
     * content:" from appearing as extension's errors.
62 30
     */
63
    if (node.tagName === "SCRIPT") {
64
	block_script(node);
65
	return;
66
    }
67

  
68
    sanitize_attributes(node);
69

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

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

  
85
function inject_csp(head)
86
{
87
    let meta = document.createElement("meta");
88
    meta.setAttribute("http-equiv", "Content-Security-Policy");
89
    meta.setAttribute("content", csp_rule(nonce));
90

  
91
    if (head.firstElementChild === null)
92
	head.appendChild(meta);
93
    else
94
	head.insertBefore(meta, head.firstElementChild);
31
    parent.hachette_corresponding.appendChild(clone);
95 32
}
96 33

  
97 34
if (!is_privileged_url(document.URL)) {
......
110 47

  
111 48
    handle_page_actions(policy.nonce);
112 49

  
113
    if (!policy.allow) {
114
	block_nodes_recursively(document.documentElement);
50
    if (!policy.allow && is_mozilla)
51
	addEventListener('beforescriptexecute', mozilla_suppress_scripts, true);
115 52

  
116
	if (is_chrome) {
117
	    var observer = new MutationObserver(handle_mutation);
118
	    observer.observe(document.documentElement, {
119
		attributes: true,
120
		childList: true,
121
		subtree: true
122
	    });
123
	}
53
    if (!policy.allow && is_chrome) {
54
	const old_html = document.documentElement;
55
	const new_html = document.createElement("html");
56
	old_html.replaceWith(new_html);
57
	old_html.hachette_corresponding = new_html;
124 58

  
125
	if (is_mozilla)
126
	    addEventListener('beforescriptexecute', mozilla_suppress_scripts, true);
59
	const modify_end =
60
	      modify_on_the_fly(old_html, policy, {node_eater: accept_node});
61
	document.addEventListener("DOMContentLoaded", modify_end);
127 62
    }
128 63

  
129 64
    start_activity_info_server();

Also available in: Unified diff