diff options
Diffstat (limited to 'content')
-rw-r--r-- | content/activity_info_server.js | 6 | ||||
-rw-r--r-- | content/freezer.js | 1 | ||||
-rw-r--r-- | content/main.js | 114 | ||||
-rw-r--r-- | content/page_actions.js | 35 |
4 files changed, 127 insertions, 29 deletions
diff --git a/content/activity_info_server.js b/content/activity_info_server.js index 81a25fb..beecb1a 100644 --- a/content/activity_info_server.js +++ b/content/activity_info_server.js @@ -44,6 +44,11 @@ function report_settings(settings) report_activity("settings", settings); } +function report_content_type(content_type) +{ + report_activity("content_type", content_type); +} + function report_repo_query_action(update, port) { report_activity_oneshot("repo_query_action", update, port); @@ -91,5 +96,6 @@ function start_activity_info_server() * EXPORT start_activity_info_server * EXPORT report_script * EXPORT report_settings + * EXPORT report_content_type * EXPORTS_END */ diff --git a/content/freezer.js b/content/freezer.js index 9dbc95e..0ea362e 100644 --- a/content/freezer.js +++ b/content/freezer.js @@ -49,6 +49,7 @@ function mozilla_suppress_scripts(e) { console.log('Script suppressor has detached.'); return; } + console.log("script event", e); if (e.isTrusted && !e.target._hachette_payload) { e.preventDefault(); console.log('Suppressed script', e.target); 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(); } diff --git a/content/page_actions.js b/content/page_actions.js index aff56b8..8057541 100644 --- a/content/page_actions.js +++ b/content/page_actions.js @@ -11,13 +11,18 @@ * IMPORT browser * IMPORT report_script * IMPORT report_settings + * IMPORT report_content_type * IMPORTS_END */ -var port; -var loaded = false; -var scripts_awaiting = []; -var nonce; +let policy_received_callback; +/* Snapshot url and content type early; these can be changed by other code. */ +let url; +let is_html; +let port; +let loaded = false; +let scripts_awaiting = []; +let nonce; function handle_message(message) { @@ -31,11 +36,13 @@ function handle_message(message) scripts_awaiting.push(script_text); } } - if (action === "settings") + if (action === "settings") { report_settings(data); + policy_received_callback({url, allow: data[1].allow}); + } } -function document_loaded(event) +function document_ready(event) { loaded = true; @@ -47,6 +54,9 @@ function document_loaded(event) function add_script(script_text) { + if (!is_html) + return; + let script = document.createElement("script"); script.textContent = script_text; script.setAttribute("nonce", nonce); @@ -56,11 +66,18 @@ function add_script(script_text) report_script(script_text); } -function handle_page_actions(script_nonce) { - document.addEventListener("DOMContentLoaded", document_loaded); +function handle_page_actions(script_nonce, policy_received_cb, + doc_ready_promise) { + policy_received_callback = policy_received_cb; + url = document.URL; + is_html = /html/.test(document.contentType); + report_content_type(document.contentType); + + doc_ready_promise.then(document_ready); + port = browser.runtime.connect({name : CONNECTION_TYPE.PAGE_ACTIONS}); port.onMessage.addListener(handle_message); - port.postMessage({url: document.URL}); + port.postMessage({url}); nonce = script_nonce; } |