Project

General

Profile

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

haketilo / background / policy_injector.js @ 8b823e1a

1
/**
2
 * Myext injecting policy to page using webRequest
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 TYPE_PREFIX
12
 * IMPORT get_storage
13
 * IMPORT browser
14
 * IMPORT is_chrome
15
 * IMPORT is_mozilla
16
 * IMPORT gen_unique
17
 * IMPORT gen_nonce
18
 * IMPORT url_item
19
 * IMPORT url_extract_policy
20
 * IMPORT get_query_best
21
 * IMPORT csp_rule
22
 * IMPORTS_END
23
 */
24

    
25
var storage;
26
var query_best;
27

    
28
const csp_header_names = {
29
    "content-security-policy" : true,
30
    "x-webkit-csp" : true,
31
    "x-content-security-policy" : true
32
};
33

    
34
const header_name = "content-security-policy";
35

    
36
function is_csp_header(header)
37
{
38
    return !!csp_header_names[header.name.toLowerCase()];
39
}
40

    
41
function is_our_header(header, rule)
42
{
43
    return header.value === rule
44
}
45

    
46
function url_inject(details)
47
{
48
    const targets = url_extract_policy(details.url);
49
    if (targets.valid_sig) {
50
	return;
51
    } else if (targets.policy) {
52
	/* Redirect; update policy */
53
	targets.target = targets.target2;
54
	delete targets.target2
55
    }
56

    
57
    let [pattern, settings] = query_best(targets.base_url);
58
    if (!pattern)
59
	/* Defaults */
60
	settings = {};
61
    
62
    const policy = {allow: settings.allow, nonce: gen_nonce()};
63
    const policy_string = encodeURIComponent(JSON.stringify(policy));
64
    const sig = gen_unique(policy_string + targets.base_url);
65
    
66
    let redirect_url = targets.base_url + '#' + sig + policy_string;
67
    if (targets.target)
68
	redirect_url += targets.target;
69
    if (targets.target2)
70
	redirect_url += targets.target2;
71

    
72
    return {redirectUrl: redirect_url};
73
}
74

    
75
function inject(details)
76
{
77
    const targets = url_extract_policy(details.url);
78
    if (!targets.valid_sig)
79
	/* Block unsigned requests */
80
	return {cancel: true};
81

    
82
    const rule = csp_rule(targets.policy.nonce);
83

    
84
    var headers = details.responseHeaders;
85

    
86
    if (!targets.policy.allow || is_mozilla)
87
	/*
88
	 * Chrome doesn't have the buggy behavior of caching headers
89
	 * we injected. Firefox does and we have to remove it there.
90
	 */
91
	headers = headers.filter(h => !is_csp_header(h));
92

    
93
    if (!targets.policy.allow)
94
	headers.push({
95
	    name : header_name,
96
	    value : rule
97
	});
98

    
99
    return {responseHeaders: headers};
100
}
101

    
102
async function start_policy_injector()
103
{
104
    storage = await get_storage();
105
    query_best = await get_query_best();
106

    
107
    let extra_opts = ["blocking", "responseHeaders"];
108
    if (is_chrome)
109
	extra_opts.push("extraHeaders");
110

    
111
    browser.webRequest.onBeforeRequest.addListener(
112
	url_inject,
113
	{
114
	    urls: ["<all_urls>"],
115
	    types: ["main_frame", "sub_frame"]
116
	},
117
	["blocking"]
118
    );
119

    
120
    browser.webRequest.onHeadersReceived.addListener(
121
	inject,
122
	{
123
	    urls: ["<all_urls>"],
124
	    types: ["main_frame", "sub_frame"]
125
	},
126
	extra_opts
127
    );
128
}
129

    
130
/*
131
 * EXPORTS_START
132
 * EXPORT start_policy_injector
133
 * EXPORTS_END
134
 */
(5-5/8)