diff options
-rw-r--r-- | background/policy_injector.js | 72 |
1 files changed, 53 insertions, 19 deletions
diff --git a/background/policy_injector.js b/background/policy_injector.js index 9a994f8..a67b4e3 100644 --- a/background/policy_injector.js +++ b/background/policy_injector.js @@ -12,14 +12,13 @@ * IMPORT get_storage * IMPORT browser * IMPORT is_chrome - * IMPORT is_mozilla * IMPORT gen_unique * IMPORT gen_nonce * IMPORT is_privileged_url * IMPORT url_extract_target * IMPORT sign_policy * IMPORT get_query_best - * IMPORT csp_rule + * IMPORT parse_csp * IMPORTS_END */ @@ -32,11 +31,19 @@ const csp_header_names = { "x-content-security-policy" : true }; +const unwanted_csp_directives = { + "report-to" : true, + "report-uri" : true, + "script-src" : true, + "script-src-elem" : true, + "prefetch-src": true +}; + const header_name = "content-security-policy"; -function is_csp_header(header) +function not_csp_header(header) { - return !!csp_header_names[header.name.toLowerCase()]; + return !csp_header_names[header.name.toLowerCase()]; } function url_inject(details) @@ -82,21 +89,48 @@ function headers_inject(details) if (!targets.current) return {cancel: true}; - const rule = csp_rule(targets.policy.nonce); - var headers = details.responseHeaders; - - /* - * Chrome doesn't have the buggy behavior of caching headers - * we injected. Firefox does and we have to remove it there. - */ - if (!targets.policy.allow || is_mozilla) - headers = headers.filter(h => !is_csp_header(h)); - - if (!targets.policy.allow) { - headers.push({ - name : header_name, - value : rule - }); + const headers = []; + for (let header of details.responseHeaders) { + if (not_csp_header(header)) { + /* Retain all non-snitching headers */ + if (header.name.toLowerCase() !== + 'content-security-policy-report-only') + headers.push(header); + + continue; + } + + const csp = parse_csp(header.value); + const rule = `'nonce-${targets.policy.nonce}'` + + /* TODO: confirm deleting non-existent things is OK everywhere */ + /* No snitching or prefetching/rendering */ + delete csp['report-to']; + delete csp['report-uri']; + + if (!target.policy.allow) { + delete csp['script-src']; + delete csp['script-src-elem']; + csp['script-src-attr'] = ["'none'"]; + csp['prefetch-src'] = ["'none'"]; + } + + if ('script-src' in csp) + csp['script-src'].push(rule); + else + csp['script-src'] = rule; + + if ('script-src-elem' in csp) + csp['script-src-elem'].push(rule); + else + csp['script-src-elem'] = rule; + + /* TODO: is this safe */ + let new_policy = Object.entries(csp).map( + i => i[0] + ' ' + i[1].join(' ') + ';' + ); + + headers.push({name: header.name, value: new_policy.join('')}); } return {responseHeaders: headers}; |