Project

General

Profile

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

haketilo / background / policy_injector.js @ 5957fbee

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_item
20
 * IMPORT url_extract_target
21
 * IMPORT sign_policy
22
 * IMPORT query_best
23
 * IMPORT csp_rule
24
 * IMPORTS_END
25
 */
26

    
27
var storage;
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(storage, 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

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

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

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

    
132
/*
133
 * EXPORTS_START
134
 * EXPORT start_policy_injector
135
 * EXPORTS_END
136
 */
(4-4/6)