Project

General

Profile

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

haketilo / background / policy_injector.js @ ecb78704

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 sign_policy
21
 * IMPORT get_query_best
22
 * IMPORT csp_rule
23
 * IMPORTS_END
24
 */
25

    
26
var storage;
27
var query_best;
28

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

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

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

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

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

    
58
    let [pattern, settings] = query_best(targets.base_url);
59
    if (!pattern)
60
	/* Defaults */
61
	settings = {};
62
    
63
    const policy = encodeURIComponent(
64
	JSON.stringify({
65
	    allow: settings.allow,
66
	    nonce: gen_nonce(),
67
	    base_url: targets.base_url
68
	})
69
    );
70
    
71
    let redirect_url = targets.base_url;
72
    redirect_url += '#' + sign_policy(policy, new Date()) + policy;
73
    if (targets.target)
74
	redirect_url += targets.target;
75
    if (targets.target2)
76
	redirect_url += targets.target2;
77

    
78
    return {redirectUrl: redirect_url};
79
}
80

    
81
function inject(details)
82
{
83
    const targets = url_extract_policy(details.url);
84
    if (!targets.current)
85
	/* Block mis-/unsigned requests */
86
	return {cancel: true};
87

    
88
    const rule = csp_rule(targets.policy.nonce);
89
    var headers = details.responseHeaders;
90

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

    
98
    if (!targets.policy.allow)
99
	headers.push({
100
	    name : header_name,
101
	    value : rule
102
	});
103

    
104
    return {responseHeaders: headers};
105
}
106

    
107
async function start_policy_injector()
108
{
109
    storage = await get_storage();
110
    query_best = await get_query_best();
111

    
112
    let extra_opts = ["blocking", "responseHeaders"];
113
    if (is_chrome)
114
	extra_opts.push("extraHeaders");
115

    
116
    browser.webRequest.onBeforeRequest.addListener(
117
	url_inject,
118
	{
119
	    urls: ["<all_urls>"],
120
	    types: ["main_frame", "sub_frame"]
121
	},
122
	["blocking"]
123
    );
124

    
125
    browser.webRequest.onHeadersReceived.addListener(
126
	inject,
127
	{
128
	    urls: ["<all_urls>"],
129
	    types: ["main_frame", "sub_frame"]
130
	},
131
	extra_opts
132
    );
133
}
134

    
135
/*
136
 * EXPORTS_START
137
 * EXPORT start_policy_injector
138
 * EXPORTS_END
139
 */
(5-5/8)