/** * Hachette injecting policy to page using webRequest * * Copyright (C) 2021 Wojtek Kosior * Copyright (C) 2021 jahoti * Redistribution terms are gathered in the `copyright' file. */ /* * IMPORTS_START * IMPORT TYPE_PREFIX * IMPORT get_storage * IMPORT browser * IMPORT is_chrome * IMPORT is_mozilla * IMPORT gen_unique * IMPORT gen_nonce * IMPORT url_item * IMPORT url_extract_policy * IMPORT sign_policy * IMPORT get_query_best * IMPORT csp_rule * IMPORTS_END */ var storage; var query_best; const csp_header_names = { "content-security-policy" : true, "x-webkit-csp" : true, "x-content-security-policy" : true }; const header_name = "content-security-policy"; function is_csp_header(header) { return !!csp_header_names[header.name.toLowerCase()]; } function is_our_header(header, rule) { return header.value === rule } function url_inject(details) { const targets = url_extract_policy(details.url); if (targets.current) { return; } else if (targets.policy) { /* Redirect; update policy */ targets.target = targets.target2; delete targets.target2 } let [pattern, settings] = query_best(targets.base_url); if (!pattern) /* Defaults */ settings = {}; const policy = encodeURIComponent( JSON.stringify({ allow: settings.allow, nonce: gen_nonce(), base_url: targets.base_url }) ); let redirect_url = targets.base_url; redirect_url += '#' + sign_policy(policy, new Date()) + policy; if (targets.target) redirect_url += targets.target; if (targets.target2) redirect_url += targets.target2; return {redirectUrl: redirect_url}; } function inject(details) { const targets = url_extract_policy(details.url); if (!targets.current) /* Block mis-/unsigned requests */ return {cancel: true}; const rule = csp_rule(targets.policy.nonce); var headers = details.responseHeaders; if (!targets.policy.allow || is_mozilla) /* * Chrome doesn't have the buggy behavior of caching headers * we injected. Firefox does and we have to remove it there. */ headers = headers.filter(h => !is_csp_header(h)); if (!targets.policy.allow) headers.push({ name : header_name, value : rule }); return {responseHeaders: headers}; } async function start_policy_injector() { storage = await get_storage(); query_best = await get_query_best(); let extra_opts = ["blocking", "responseHeaders"]; if (is_chrome) extra_opts.push("extraHeaders"); browser.webRequest.onBeforeRequest.addListener( url_inject, { urls: [""], types: ["main_frame", "sub_frame"] }, ["blocking"] ); browser.webRequest.onHeadersReceived.addListener( inject, { urls: [""], types: ["main_frame", "sub_frame"] }, extra_opts ); } /* * EXPORTS_START * EXPORT start_policy_injector * EXPORTS_END */