aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjahoti <jahoti@tilde.team>2021-07-21 00:00:00 +0000
committerWojtek Kosior <koszko@koszko.org>2021-07-26 11:09:57 +0200
commitfba4820bec6714115ef03bd4bdfd714ba485ac2c (patch)
tree6b3b113cabbdb99280b03b67b00aa88b44150e2d
parent57e4ed2b06d15747b20737bad14bcdd2d73fd8a6 (diff)
downloadbrowser-extension-fba4820bec6714115ef03bd4bdfd714ba485ac2c.tar.gz
browser-extension-fba4820bec6714115ef03bd4bdfd714ba485ac2c.zip
[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?).
-rw-r--r--background/policy_injector.js72
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};