/** * 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 sign_data * IMPORT extract_signed * IMPORT sanitize_csp_header * IMPORT csp_rule * IMPORT is_csp_header_name * IMPORTS_END */ function inject_csp_headers(headers, policy) { let csp_headers; let old_signature; let hachette_header; for (const header of headers.filter(h => h.name === "x-hachette")) { /* x-hachette header has format: _0_ */ const match = /^([^_]+)_(0_.*)$/.exec(header.value); if (!match) continue; const result = extract_signed(...match.slice(1, 3)); if (result.fail) continue; /* This should succeed - it's our self-produced valid JSON. */ const old_data = JSON.parse(decodeURIComponent(result.data)); /* Confirmed- it's the originals, smuggled in! */ csp_headers = old_data.csp_headers; old_signature = old_data.policy_sig; hachette_header = header; break; } if (!hachette_header) { hachette_header = {name: "x-hachette"}; headers.push(hachette_header); } csp_headers = csp_headers || headers.filter(h => is_csp_header_name(h.name)); /* When blocking remove report-only CSP headers that snitch on us. */ headers = headers.filter(h => !is_csp_header_name(h.name, !policy.allow)); if (old_signature) headers = headers.filter(h => h.value.search(old_signature) === -1); headers.push(...csp_headers.map(h => sanitize_csp_header(h, policy))); const policy_str = encodeURIComponent(JSON.stringify(policy)); const signed_policy = sign_data(policy_str, new Date().getTime()); const later_30sec = new Date(new Date().getTime() + 30000).toGMTString(); headers.push({ name: "Set-Cookie", value: `hachette-${signed_policy.join("=")}; Expires=${later_30sec};` }); /* * Smuggle in the signature and 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 headers in the cache. */ let hachette_data = {csp_headers, policy_sig: signed_policy[0]}; hachette_data = encodeURIComponent(JSON.stringify(hachette_data)); hachette_header.value = sign_data(hachette_data, 0).join("_"); /* To ensure there is a CSP header if required */ if (!policy.allow) headers.push({ name: "content-security-policy", value: csp_rule(policy.nonce) }); return headers; } /* * EXPORTS_START * EXPORT inject_csp_headers * EXPORTS_END */