aboutsummaryrefslogtreecommitdiff
path: root/background/policy_injector.js
diff options
context:
space:
mode:
Diffstat (limited to 'background/policy_injector.js')
-rw-r--r--background/policy_injector.js183
1 files changed, 19 insertions, 164 deletions
diff --git a/background/policy_injector.js b/background/policy_injector.js
index 9725e99..b49ec47 100644
--- a/background/policy_injector.js
+++ b/background/policy_injector.js
@@ -1,5 +1,7 @@
/**
- * Hachette injecting policy to page using webRequest
+ * This file is part of Haketilo.
+ *
+ * Function: Injecting policy to page by modifying HTTP headers.
*
* Copyright (C) 2021 Wojtek Kosior
* Copyright (C) 2021 jahoti
@@ -8,186 +10,39 @@
/*
* IMPORTS_START
- * IMPORT TYPE_PREFIX
- * IMPORT get_storage
- * IMPORT browser
- * IMPORT is_chrome
- * IMPORT is_mozilla
- * IMPORT gen_unique
- * IMPORT gen_nonce
- * IMPORT is_privileged_url
- * IMPORT url_item
- * IMPORT url_extract_target
- * IMPORT sign_policy
- * IMPORT query_best
- * IMPORT sanitize_csp_header
+ * IMPORT make_csp_rule
+ * IMPORT csp_header_regex
+ * Re-enable the import below once nonce stuff here is ready
+ * !mport gen_nonce
* IMPORTS_END
*/
-var storage;
-
-const csp_header_names = new Set([
- "content-security-policy",
- "x-webkit-csp",
- "x-content-security-policy"
-]);
-
-/* TODO: variable no longer in use; remove if not needed */
-const unwanted_csp_directives = new Set([
- "report-to",
- "report-uri",
- "script-src",
- "script-src-elem",
- "prefetch-src"
-]);
-
-const report_only = "content-security-policy-report-only";
-
-function url_inject(details)
-{
- if (is_privileged_url(details.url))
- return;
-
- const targets = url_extract_target(details.url);
- if (targets.current)
- return;
-
- /* Redirect; update policy */
- if (targets.policy)
- targets.target = "";
-
- let [pattern, settings] = query_best(storage, targets.base_url);
- /* Defaults */
- if (!pattern)
- settings = {};
-
- const policy = encodeURIComponent(
- JSON.stringify({
- allow: settings.allow,
- nonce: gen_nonce(),
- base_url: targets.base_url
- })
- );
-
- return {
- redirectUrl: [
- targets.base_url,
- '#', sign_policy(policy, new Date()), policy,
- targets.target,
- targets.target2
- ].join("")
- };
-}
-
-function headers_inject(details)
+function inject_csp_headers(headers, policy)
{
- const targets = url_extract_target(details.url);
- /* Block mis-/unsigned requests */
- if (!targets.current)
- return {cancel: true};
-
- let orig_csp_headers = is_chrome ? null : [];
- let headers = [];
- let csp_headers = is_chrome ? headers : [];
+ let csp_headers;
- const rule = `'nonce-${targets.policy.nonce}'`;
- const block = !targets.policy.allow;
+ if (policy.payload) {
+ headers = headers.filter(h => !csp_header_regex.test(h.name));
- for (const header of details.responseHeaders) {
- if (!csp_header_names.has(header)) {
- /* Remove headers that only snitch on us */
- if (header.name.toLowerCase() === report_only && block)
- continue;
- headers.push(header);
+ // TODO: make CSP rules with nonces and facilitate passing them to
+ // content scripts via dynamic content script registration or
+ // synchronous XHRs
- /* If these are the original CSP headers, use them instead */
- /* Test based on url_extract_target() in misc.js */
- if (is_mozilla && header.name === "x-orig-csp") {
- let index = header.value.indexOf('%5B');
- if (index === -1)
- continue;
-
- let sig = header.value.substring(0, index);
- let data = header.value.substring(index);
- if (sig !== sign_policy(data, 0))
- continue;
-
- /* Confirmed- it's the originals, smuggled in! */
- try {
- data = JSON.parse(decodeURIComponent(data));
- } catch (e) {
- /* This should not be reached -
- it's our self-produced valid JSON. */
- console.log("Unexpected internal error - invalid JSON smuggled!", e);
- }
-
- orig_csp_headers = csp_headers = null;
- for (const header of data)
- headers.push(sanitize_csp_header(header, rule, block));
- }
- } else if (is_chrome || !orig_csp_headers) {
- csp_headers.push(sanitize_csp_header(header, rule, block));
- if (is_mozilla)
- orig_csp_headers.push(header);
- }
- }
-
- if (orig_csp_headers) {
- /** Smuggle in the original CSP headers for future use.
- * These are signed with a time of 0, as it's not clear there
- * is a limit on how long Firefox might retain these headers in
- * the cache.
- */
- orig_csp_headers = encodeURIComponent(JSON.stringify(orig_csp_headers));
- headers.push({
- name: "x-orig-csp",
- value: sign_policy(orig_csp_headers, 0) + orig_csp_headers
- });
-
- headers = headers.concat(csp_headers);
+ // policy.nonce = gen_nonce();
}
- /* To ensure there is a CSP header if required */
- if (block) {
+ if (!policy.allow && (policy.nonce || !policy.payload)) {
headers.push({
name: "content-security-policy",
- value: `script-src ${rule}; script-src-elem ${rule}; ` +
- "script-src-attr 'none'; prefetch-src 'none';"
+ value: make_csp_rule(policy)
});
}
- return {responseHeaders: headers};
-}
-
-async function start_policy_injector()
-{
- storage = await get_storage();
-
- let extra_opts = ["blocking", "responseHeaders"];
- if (is_chrome)
- extra_opts.push("extraHeaders");
-
- browser.webRequest.onBeforeRequest.addListener(
- url_inject,
- {
- urls: ["<all_urls>"],
- types: ["main_frame", "sub_frame"]
- },
- ["blocking"]
- );
-
- browser.webRequest.onHeadersReceived.addListener(
- headers_inject,
- {
- urls: ["<all_urls>"],
- types: ["main_frame", "sub_frame"]
- },
- extra_opts
- );
+ return headers;
}
/*
* EXPORTS_START
- * EXPORT start_policy_injector
+ * EXPORT inject_csp_headers
* EXPORTS_END
*/