diff options
Diffstat (limited to 'content/main.js')
-rw-r--r-- | content/main.js | 114 |
1 files changed, 94 insertions, 20 deletions
diff --git a/content/main.js b/content/main.js index 8440eb5..4fe6d43 100644 --- a/content/main.js +++ b/content/main.js @@ -10,6 +10,7 @@ * IMPORTS_START * IMPORT handle_page_actions * IMPORT extract_signed + * IMPORT sign_data * IMPORT gen_nonce * IMPORT is_privileged_url * IMPORT mozilla_suppress_scripts @@ -22,15 +23,67 @@ * IMPORTS_END */ -function accept_node(node, parent) +function extract_cookie_policy(cookie, min_time) { - const clone = document.importNode(node, false); - node.hachette_corresponding = clone; - /* - * TODO: Stop page's own issues like "Error parsing a meta element's - * content:" from appearing as extension's errors. - */ - parent.hachette_corresponding.appendChild(clone); + let best_result = {time: -1}; + let policy = null; + const extracted_signatures = []; + + for (const match of cookie.matchAll(/hachette-(\w*)=([^;]*)/g)) { + const new_result = extract_signed(...match.slice(1, 3)); + if (new_result.fail) + continue; + + extracted_signatures.push(match[1]); + + if (new_result.time < Math.max(min_time, best_result.time)) + continue; + + /* This should succeed - it's our self-produced valid JSON. */ + const new_policy = JSON.parse(decodeURIComponent(new_result.data)); + if (new_policy.url !== document.URL) + continue; + + best_result = new_result; + policy = new_policy; + } + + return [policy, extracted_signatures]; +} + +function extract_url_policy(url, min_time) +{ + const [base_url, payload, anchor] = + /^([^#]*)#?([^#]*)(#?.*)$/.exec(url).splice(1, 4); + + const match = /^hachette_([^_]+)_(.*)$/.exec(payload); + if (!match) + return [null, url]; + + const result = extract_signed(...match.slice(1, 3)); + if (result.fail) + return [null, url]; + + const original_url = base_url + anchor; + const policy = result.time < min_time ? null : + JSON.parse(decodeURIComponent(result.data)); + + return [policy.url === original_url ? policy : null, original_url]; +} + +function employ_nonhttp_policy(policy) +{ + if (!policy.allow) + return; + + policy.nonce = gen_nonce(); + const [base_url, target] = /^([^#]*)(#?.*)$/.exec(policy.url).slice(1, 3); + const encoded_policy = encodeURIComponent(JSON.stringify(policy)); + const payload = "hachette_" + + sign_data(encoded_policy, new Date().getTime()).join("_"); + const resulting_url = `${base_url}#${payload}${target}`; + location.href = resulting_url; + location.reload(); } /* @@ -171,23 +224,44 @@ async function sanitize_document(doc, policy) } if (!is_privileged_url(document.URL)) { - const reductor = - (ac, [_, sig, pol]) => ac[0] && ac || [extract_signed(sig, pol), sig]; - const matches = [...document.cookie.matchAll(/hachette-(\w*)=([^;]*)/g)]; - let [policy, signature] = matches.reduce(reductor, []); + let policy_received_callback = () => undefined; + let policy; + + /* Signature valid for half an hour. */ + const min_time = new Date().getTime() - 1800 * 1000; + + if (/^https?:/.test(document.URL)) { + let signatures; + [policy, signatures] = extract_cookie_policy(document.cookie, min_time); + for (const signature of signatures) + document.cookie = `hachette-${signature}=; Max-Age=-1;`; + } else { + const scheme = /^([^:]*)/.exec(document.URL)[1]; + const known_scheme = ["file", "ftp"].includes(scheme); + + if (!known_scheme) + console.warn(`Unknown url scheme: \`${scheme}'!`); + + let original_url; + [policy, original_url] = extract_url_policy(document.URL, min_time); + history.replaceState(null, "", original_url); + + if (known_scheme && !policy) + policy_received_callback = employ_nonhttp_policy; + } - if (!policy || policy.url !== document.URL) { - console.log("WARNING! Using default policy!!!"); + if (!policy) { + console.warn("Using fallback policy!"); policy = {allow: false, nonce: gen_nonce()}; } - if (signature) - document.cookie = `hachette-${signature}=; Max-Age=-1;`; - - if (!policy.allow) - sanitize_document(document, policy); + const doc_ready = Promise.all([ + policy.allow ? Promise.resolve : sanitize_document(document, policy), + new Promise(cb => document.addEventListener("DOMContentLoaded", + cb, {once: true})) + ]); - handle_page_actions(policy.nonce); + handle_page_actions(policy.nonce, policy_received_callback, doc_ready); start_activity_info_server(); } |