Project

General

Profile

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

haketilo / background / policy_injector.js @ 57e4ed2b

1
/**
2
 * Hachette 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 is_privileged_url
19
 * IMPORT url_extract_target
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 url_inject(details)
43
{
44
    if (is_privileged_url(details.url))
45
	return;
46

    
47
    const targets = url_extract_target(details.url);
48
    if (targets.current)
49
	return;
50

    
51
    /* Redirect; update policy */
52
    if (targets.policy)
53
	targets.target = "";
54

    
55
    let [pattern, settings] = query_best(targets.base_url);
56
    /* Defaults */
57
    if (!pattern)
58
	settings = {};
59

    
60
    const policy = encodeURIComponent(
61
	JSON.stringify({
62
	    allow: settings.allow,
63
	    nonce: gen_nonce(),
64
	    base_url: targets.base_url
65
	})
66
    );
67

    
68
    return {
69
	redirectUrl: [
70
	    targets.base_url,
71
	    '#', sign_policy(policy, new Date()), policy,
72
	    targets.target,
73
	    targets.target2
74
	].join("")
75
    };
76
}
77

    
78
function headers_inject(details)
79
{
80
    const targets = url_extract_target(details.url);
81
    /* Block mis-/unsigned requests */
82
    if (!targets.current)
83
	return {cancel: true};
84

    
85
    const rule = csp_rule(targets.policy.nonce);
86
    var headers = details.responseHeaders;
87

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

    
95
    if (!targets.policy.allow) {
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
	headers_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
 */
(4-4/7)