Project

General

Profile

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

haketilo / background / policy_injector.js @ 6b53d6c8

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 sign_data
12
 * IMPORT extract_signed
13
 * IMPORT sanitize_csp_header
14
 * IMPORT csp_rule
15
 * IMPORT is_csp_header_name
16
 * IMPORTS_END
17
 */
18

    
19
function inject_csp_headers(details, headers, policy)
20
{
21
    const url = details.url;
22

    
23
    let orig_csp_headers;
24
    let old_signature;
25
    let hachette_header;
26

    
27
    for (const header of headers.filter(h => h.name === "x-hachette")) {
28
	const match = /^([^%])(%.*)$/.exec(header.value);
29
	if (!match)
30
	    continue;
31

    
32
	const old_data = extract_signed(...match.splice(1, 2), [[0]]);
33
	if (!old_data || old_data.url !== url)
34
	    continue;
35

    
36
	/* Confirmed- it's the originals, smuggled in! */
37
	orig_csp_headers = old_data.csp_headers;
38
	old_signature = old_data.policy_sig;
39

    
40
	hachette_header = header;
41
	break;
42
    }
43

    
44
    if (!hachette_header) {
45
	hachette_header = {name: "x-hachette"};
46
	headers.push(hachette_header);
47
    }
48

    
49
    orig_csp_headers = orig_csp_headers ||
50
	headers.filter(h => is_csp_header_name(h.name));
51

    
52
    /* When blocking remove report-only CSP headers that snitch on us. */
53
    headers = headers.filter(h => !is_csp_header_name(h.name, !policy.allow));
54

    
55
    if (old_signature)
56
	headers = headers.filter(h => h.name.search(old_signature) === -1);
57

    
58
    const sanitizer = h => sanitize_csp_header(h, policy);
59
    headers.push(...orig_csp_headers.map(sanitizer));
60

    
61
    const policy_str = encodeURIComponent(JSON.stringify(policy));
62
    const policy_sig = sign_data(policy_str, new Date());
63
    const later_30sec = new Date(new Date().getTime() + 30000).toGMTString();
64
    headers.push({
65
	name: "Set-Cookie",
66
	value: `hachette-${policy_sig}=${policy_str}; Expires=${later_30sec};`
67
    });
68

    
69
    /*
70
     * Smuggle in the signature and the original CSP headers for future use.
71
     * These are signed with a time of 0, as it's not clear there is a limit on
72
     * how long Firefox might retain headers in the cache.
73
     */
74
    let hachette_data = {csp_headers: orig_csp_headers, policy_sig, url};
75
    hachette_data = encodeURIComponent(JSON.stringify(hachette_data));
76
    hachette_header.value = sign_data(hachette_data, 0) + hachette_data;
77

    
78
    /* To ensure there is a CSP header if required */
79
    if (!policy.allow)
80
	headers.push({
81
	    name: "content-security-policy",
82
	    value: csp_rule(policy.nonce)
83
	});
84

    
85
    return headers;
86
}
87

    
88
/*
89
 * EXPORTS_START
90
 * EXPORT inject_csp_headers
91
 * EXPORTS_END
92
 */
(3-3/6)