Project

General

Profile

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

haketilo / background / policy_injector.js @ 692577bb

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 gen_unique
16
 * IMPORT gen_nonce
17
 * IMPORT url_item
18
 * IMPORT url_extract_policy
19
 * IMPORT get_query_best
20
 * IMPORT csp_rule
21
 * IMPORTS_END
22
 */
23

    
24
var storage;
25
var query_best;
26

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

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

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

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

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

    
56
    let redirect_url = targets.base_url + targets.sig;
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
    
64
    redirect_url += encodeURIComponent(JSON.stringify(policy));
65
    if (targets.target)
66
	redirect_url += targets.target;
67
    if (targets.target2)
68
	redirect_url += targets.target2;
69

    
70
    return {redirectUrl: redirect_url};
71
}
72

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

    
80
    const rule = csp_rule(targets.policy.nonce);
81

    
82
    var headers;
83

    
84
    if (targets.policy.allow) {
85
	/*
86
	 * Chrome doesn't have the buggy behavior of repeatedly injecting a
87
	 * header we injected once. Firefox does and we have to remove it there.
88
	 */
89
	if (is_chrome)
90
	    return {cancel: false};
91

    
92
	headers = details.responseHeaders.filter(h => !is_our_header(h, rule));
93
    } else {
94
	headers = details.responseHeaders.filter(h => !is_csp_header(h));
95

    
96
	headers.push({
97
	    name : header_name,
98
	    value : rule
99
	});
100
    }
101

    
102
    return {responseHeaders: headers};
103
}
104

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

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

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

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

    
133
/*
134
 * EXPORTS_START
135
 * EXPORT start_policy_injector
136
 * EXPORTS_END
137
 */
(5-5/8)