Revision 57b80d72
Added by jahoti about 2 years ago
background/policy_injector.js | ||
---|---|---|
12 | 12 |
* IMPORT get_storage |
13 | 13 |
* IMPORT browser |
14 | 14 |
* IMPORT is_chrome |
15 |
* IMPORT is_mozilla |
|
16 | 15 |
* IMPORT gen_unique |
17 | 16 |
* IMPORT gen_nonce |
18 | 17 |
* IMPORT is_privileged_url |
19 | 18 |
* IMPORT url_extract_target |
20 | 19 |
* IMPORT sign_policy |
21 | 20 |
* IMPORT get_query_best |
22 |
* IMPORT csp_rule
|
|
21 |
* IMPORT parse_csp
|
|
23 | 22 |
* IMPORTS_END |
24 | 23 |
*/ |
25 | 24 |
|
... | ... | |
32 | 31 |
"x-content-security-policy" : true |
33 | 32 |
}; |
34 | 33 |
|
34 |
const unwanted_csp_directives = { |
|
35 |
"report-to" : true, |
|
36 |
"report-uri" : true, |
|
37 |
"script-src" : true, |
|
38 |
"script-src-elem" : true, |
|
39 |
"prefetch-src": true |
|
40 |
}; |
|
41 |
|
|
35 | 42 |
const header_name = "content-security-policy"; |
36 | 43 |
|
37 |
function is_csp_header(header)
|
|
44 |
function not_csp_header(header)
|
|
38 | 45 |
{ |
39 |
return !!csp_header_names[header.name.toLowerCase()];
|
|
46 |
return !csp_header_names[header.name.toLowerCase()]; |
|
40 | 47 |
} |
41 | 48 |
|
42 | 49 |
function url_inject(details) |
... | ... | |
82 | 89 |
if (!targets.current) |
83 | 90 |
return {cancel: true}; |
84 | 91 |
|
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 |
}); |
|
92 |
const headers = []; |
|
93 |
for (let header of details.responseHeaders) { |
|
94 |
if (not_csp_header(header)) { |
|
95 |
/* Retain all non-snitching headers */ |
|
96 |
if (header.name.toLowerCase() !== |
|
97 |
'content-security-policy-report-only') |
|
98 |
headers.push(header); |
|
99 |
|
|
100 |
continue; |
|
101 |
} |
|
102 |
|
|
103 |
const csp = parse_csp(header.value); |
|
104 |
const rule = `'nonce-${targets.policy.nonce}'` |
|
105 |
|
|
106 |
/* TODO: confirm deleting non-existent things is OK everywhere */ |
|
107 |
/* No snitching or prefetching/rendering */ |
|
108 |
delete csp['report-to']; |
|
109 |
delete csp['report-uri']; |
|
110 |
|
|
111 |
if (!target.policy.allow) { |
|
112 |
delete csp['script-src']; |
|
113 |
delete csp['script-src-elem']; |
|
114 |
csp['script-src-attr'] = ["'none'"]; |
|
115 |
csp['prefetch-src'] = ["'none'"]; |
|
116 |
} |
|
117 |
|
|
118 |
if ('script-src' in csp) |
|
119 |
csp['script-src'].push(rule); |
|
120 |
else |
|
121 |
csp['script-src'] = rule; |
|
122 |
|
|
123 |
if ('script-src-elem' in csp) |
|
124 |
csp['script-src-elem'].push(rule); |
|
125 |
else |
|
126 |
csp['script-src-elem'] = rule; |
|
127 |
|
|
128 |
/* TODO: is this safe */ |
|
129 |
let new_policy = Object.entries(csp).map( |
|
130 |
i => i[0] + ' ' + i[1].join(' ') + ';' |
|
131 |
); |
|
132 |
|
|
133 |
headers.push({name: header.name, value: new_policy.join('')}); |
|
100 | 134 |
} |
101 | 135 |
|
102 | 136 |
return {responseHeaders: headers}; |
Also available in: Unified diff
[UNTESTED- will test] Use more nuanced CSP filtering
CSP headers are now parsed and processed, rather than treated as simple
units. This allows us to ensure policies delivered as HTTP headers do not
interfere with our script filtering, as well as to preserve useful protections
while removing the ones that could be problematic. Additionally, prefetching
should now be blocked on pages where native scripts aren't allowed, and
all reporting of CSP violations has been stripped (is this appropriate?).