From 538376341e9a50ebd350897fe26f43c433f0ee06 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 27 Aug 2021 10:01:32 +0200 Subject: enable whitelisting of `file://' protocol\n\nThis commit additionally also changes the semantics of triple asterisk wildcard in URL path. --- common/patterns.js | 160 +++++++++++++++++++++++++---------------------------- 1 file changed, 76 insertions(+), 84 deletions(-) (limited to 'common/patterns.js') diff --git a/common/patterns.js b/common/patterns.js index be7c650..0a322b0 100644 --- a/common/patterns.js +++ b/common/patterns.js @@ -5,35 +5,41 @@ * Redistribution terms are gathered in the `copyright' file. */ -const proto_re = "[a-zA-Z]*:\/\/"; +const proto_regex = /^(\w+):\/\/(.*)$/; + const domain_re = "[^/?#]+"; -const segments_re = "/[^?#]*"; -const query_re = "\\?[^#]*"; - -const url_regex = new RegExp(`\ -^\ -(${proto_re})\ -(${domain_re})\ -(${segments_re})?\ -(${query_re})?\ -#?.*\$\ -`); +const path_re = "[^?#]*"; +const query_re = "\\??[^#]*"; + +const http_regex = new RegExp(`^(${domain_re})(${path_re})(${query_re}).*`); + +const file_regex = new RegExp(`^(${path_re}).*`); function deconstruct_url(url) { - const regex_match = url_regex.exec(url); - if (regex_match === null) + const proto_match = proto_regex.exec(url); + if (proto_match === null) return undefined; - let [_, proto, domain, path, query] = regex_match; + const deco = {proto: proto_match[1]}; - domain = domain.split("."); - let path_trailing_dash = - path && path[path.length - 1] === "/"; - path = (path || "").split("/").filter(s => s !== ""); - path.unshift(""); + if (deco.proto === "file") { + deco.path = file_regex.exec(proto_match[2])[1]; + } else { + const http_match = http_regex.exec(proto_match[2]); + if (!http_match) + return undefined; + [deco.domain, deco.path, deco.query] = http_match.slice(1, 4); + deco.domain = deco.domain.split("."); + } - return {proto, domain, path, query, path_trailing_dash}; + const leading_dash = deco.path[0] === "/"; + deco.trailing_dash = deco.path[deco.path.length - 1] === "/"; + deco.path = deco.path.split("/").filter(s => s !== ""); + if (leading_dash || deco.path.length === 0) + deco.path.unshift(""); + + return deco; } /* Be sane: both arguments should be arrays of length >= 2 */ @@ -104,84 +110,70 @@ function url_matches(url, pattern) return false } - if (pattern_deco.proto !== url_deco.proto) - return false; - - return domain_matches(url_deco.domain, pattern_deco.domain) && - path_matches(url_deco.path, url_deco.path_trailing_dash, - pattern_deco.path, pattern_deco.path_trailing_dash); + return pattern_deco.proto === url_deco.proto && + !(pattern_deco.proto === "file" && pattern_deco.trailing_dash) && + !!url_deco.domain === !!pattern_deco.domain && + (!url_deco.domain || + domain_matches(url_deco.domain, pattern_deco.domain)) && + path_matches(url_deco.path, url_deco.trailing_dash, + pattern_deco.path, pattern_deco.trailing_dash); } -/* - * Call callback for every possible pattern that matches url. Return when there - * are no more patterns or callback returns false. - */ -function for_each_possible_pattern(url, callback) +function* each_domain_pattern(domain_segments) { - const deco = deconstruct_url(url); - - if (deco === undefined) { - console.log("bad url format", url); - return; + for (let slice = 0; slice < domain_segments.length; slice++) { + const domain_part = domain_segments.slice(slice).join("."); + const domain_wildcards = []; + if (slice === 0) + yield domain_part; + if (slice === 1) + yield "*." + domain_part; + if (slice > 1) + yield "**." + domain_part; + yield "***." + domain_part; } +} - for (let d_slice = 0; d_slice < deco.domain.length; d_slice++) { - const domain_part = deco.domain.slice(d_slice).join("."); - const domain_wildcards = []; - if (d_slice === 0) - domain_wildcards.push(""); - if (d_slice === 1) - domain_wildcards.push("*."); - if (d_slice > 0) - domain_wildcards.push("**."); - domain_wildcards.push("***."); - - for (const domain_wildcard of domain_wildcards) { - const domain_pattern = domain_wildcard + domain_part; - - for (let s_slice = deco.path.length; s_slice > 0; s_slice--) { - const path_part = deco.path.slice(0, s_slice).join("/"); - const path_wildcards = []; - if (s_slice === deco.path.length) { - if (deco.path_trailing_dash) - path_wildcards.push("/"); - path_wildcards.push(""); - } - if (s_slice === deco.path.length - 1 && - deco.path[s_slice] !== "*") - path_wildcards.push("/*"); - if (s_slice < deco.path.length && - (deco.path[s_slice] !== "**" || - s_slice < deco.path.length - 1)) - path_wildcards.push("/**"); - if (deco.path[s_slice] !== "***" || s_slice < deco.path.length) - path_wildcards.push("/***"); - - for (const path_wildcard of path_wildcards) { - const path_pattern = path_part + path_wildcard; - - const pattern = deco.proto + domain_pattern + path_pattern; - - if (callback(pattern) === false) - return; - } - } +function* each_path_pattern(path_segments, trailing_dash) +{ + for (let slice = path_segments.length; slice > 0; slice--) { + const path_part = path_segments.slice(0, slice).join("/"); + const path_wildcards = []; + if (slice === path_segments.length) { + if (trailing_dash) + yield path_part + "/"; + yield path_part; } + if (slice === path_segments.length - 1 && path_segments[slice] !== "*") + yield path_part + "/*"; + if (slice < path_segments.length - 1) + yield path_part + "/**"; + if (slice < path_segments.length - 1 || + path_segments[path_segments.length - 1] !== "***") + yield path_part + "/***"; } } -function possible_patterns(url) +/* Generate every possible pattern that matches url. */ +function* each_url_pattern(url) { - const patterns = []; - for_each_possible_pattern(url, patterns.push); + const deco = deconstruct_url(url); - return patterns; + if (deco === undefined) { + console.log("bad url format", url); + return false; + } + + const all_domains = deco.domain ? each_domain_pattern(deco.domain) : [""]; + for (const domain of all_domains) { + for (const path of each_path_pattern(deco.path, deco.trailing_dash)) + yield `${deco.proto}://${domain}${path}`; + } } /* * EXPORTS_START * EXPORT url_matches - * EXPORT for_each_possible_pattern - * EXPORT possible_patterns + * EXPORT each_url_pattern * EXPORTS_END */ -- cgit v1.2.3 From 48f76d7004da4bd4998d0c79266c62f893cfa7d3 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 27 Aug 2021 10:52:52 +0200 Subject: add support for `ftp://' protocol --- common/patterns.js | 9 ++++++++- content/main.js | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'common/patterns.js') diff --git a/common/patterns.js b/common/patterns.js index 0a322b0..ebb55ab 100644 --- a/common/patterns.js +++ b/common/patterns.js @@ -7,6 +7,7 @@ const proto_regex = /^(\w+):\/\/(.*)$/; +const user_re = "[^/?#@]+@" const domain_re = "[^/?#]+"; const path_re = "[^?#]*"; const query_re = "\\??[^#]*"; @@ -15,6 +16,8 @@ const http_regex = new RegExp(`^(${domain_re})(${path_re})(${query_re}).*`); const file_regex = new RegExp(`^(${path_re}).*`); +const ftp_regex = new RegExp(`^(${user_re})?(${domain_re})(${path_re}).*`); + function deconstruct_url(url) { const proto_match = proto_regex.exec(url); @@ -25,14 +28,18 @@ function deconstruct_url(url) if (deco.proto === "file") { deco.path = file_regex.exec(proto_match[2])[1]; + } else if (deco.proto === "ftp") { + [deco.domain, deco.path] = ftp_regex.exec(proto_match[2]).slice(2, 4); } else { const http_match = http_regex.exec(proto_match[2]); if (!http_match) return undefined; [deco.domain, deco.path, deco.query] = http_match.slice(1, 4); - deco.domain = deco.domain.split("."); } + if (deco.domain) + deco.domain = deco.domain.split("."); + const leading_dash = deco.path[0] === "/"; deco.trailing_dash = deco.path[deco.path.length - 1] === "/"; deco.path = deco.path.split("/").filter(s => s !== ""); diff --git a/content/main.js b/content/main.js index 06d3bf1..6c97350 100644 --- a/content/main.js +++ b/content/main.js @@ -109,7 +109,7 @@ if (!is_privileged_url(document.URL)) { document.cookie = `hachette-${signature}=; Max-Age=-1;`; } else { const scheme = /^([^:]*)/.exec(document.URL)[1]; - const known_scheme = ["file"].includes(scheme); + const known_scheme = ["file", "ftp"].includes(scheme); if (!known_scheme) console.warn(`Unknown url scheme: \`${scheme}'!`); -- cgit v1.2.3 From 72cbfa74f7f30fdf60fc6ad73182ed1cca3d3712 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Thu, 9 Sep 2021 20:18:01 +0200 Subject: limit allowed pattern lengths --- common/patterns.js | 141 +++++++++++++++++--------------------------------- html/display-panel.js | 28 +++------- 2 files changed, 53 insertions(+), 116 deletions(-) (limited to 'common/patterns.js') diff --git a/common/patterns.js b/common/patterns.js index ebb55ab..ae29fcd 100644 --- a/common/patterns.js +++ b/common/patterns.js @@ -5,6 +5,11 @@ * Redistribution terms are gathered in the `copyright' file. */ +const MAX_URL_PATH_LEN = 12; +const MAX_URL_PATH_CHARS = 255; +const MAX_DOMAIN_LEN = 7; +const MAX_DOMAIN_CHARS = 100; + const proto_regex = /^(\w+):\/\/(.*)$/; const user_re = "[^/?#@]+@" @@ -37,103 +42,51 @@ function deconstruct_url(url) [deco.domain, deco.path, deco.query] = http_match.slice(1, 4); } - if (deco.domain) - deco.domain = deco.domain.split("."); - const leading_dash = deco.path[0] === "/"; deco.trailing_dash = deco.path[deco.path.length - 1] === "/"; - deco.path = deco.path.split("/").filter(s => s !== ""); - if (leading_dash || deco.path.length === 0) - deco.path.unshift(""); - return deco; -} + if (deco.domain) { + if (deco.domain.length > MAX_DOMAIN_CHARS) { + const idx = deco.domain.indexOf(".", deco.domain.length - + MAX_DOMAIN_CHARS); + if (idx === -1) + deco.domain = []; + else + deco.domain = deco.domain.substring(idx + 1); -/* Be sane: both arguments should be arrays of length >= 2 */ -function domain_matches(url_domain, pattern_domain) -{ - const length_difference = url_domain.length - pattern_domain.length; - - for (let i = 1; i <= url_domain.length; i++) { - const url_part = url_domain[url_domain.length - i]; - const pattern_part = pattern_domain[pattern_domain.length - i]; - - if (pattern_domain.length === i) { - if (pattern_part === "*") - return length_difference === 0; - if (pattern_part === "**") - return length_difference > 0; - if (pattern_part === "***") - return true; - return length_difference === 0 && pattern_part === url_part; + deco.domain_truncated = true; } - if (pattern_part !== url_part) - return false; - } - - return pattern_domain.length === url_domain.length + 1 && - pattern_domain[0] === "***"; -} - -function path_matches(url_path, url_trailing_dash, - pattern_path, pattern_trailing_dash) -{ - const dashes_ok = !(pattern_trailing_dash && !url_trailing_dash); - - if (pattern_path.length === 0) - return url_path.length === 0 && dashes_ok; - - const length_difference = url_path.length - pattern_path.length; - - for (let i = 0; i < url_path.length; i++) { - if (pattern_path.length === i + 1) { - if (pattern_path[i] === "*") - return length_difference === 0; - if (pattern_path[i] === "**") { - return length_difference > 0 || - (url_path[i] === "**" && dashes_ok); - } - if (pattern_path[i] === "***") - return length_difference >= 0; - return length_difference === 0 && - pattern_path[i] === url_path[i] && dashes_ok; + if (deco.path.length > MAX_URL_PATH_CHARS) { + deco.path = deco.path.substring(0, deco.path.lastIndexOf("/")); + deco.path_truncated = true; } - - if (pattern_path[i] !== url_path[i]) - return false; } - return false; -} - -function url_matches(url, pattern) -{ - const url_deco = deconstruct_url(url); - const pattern_deco = deconstruct_url(pattern); - - if (url_deco === undefined || pattern_deco === undefined) { - console.log(`bad comparison: ${url} and ${pattern}`); - return false + if (typeof deco.domain === "string") { + deco.domain = deco.domain.split("."); + if (deco.domain.splice(0, deco.domain.length - MAX_DOMAIN_LEN).length + > 0) + deco.domain_truncated = true; } - return pattern_deco.proto === url_deco.proto && - !(pattern_deco.proto === "file" && pattern_deco.trailing_dash) && - !!url_deco.domain === !!pattern_deco.domain && - (!url_deco.domain || - domain_matches(url_deco.domain, pattern_deco.domain)) && - path_matches(url_deco.path, url_deco.trailing_dash, - pattern_deco.path, pattern_deco.trailing_dash); + deco.path = deco.path.split("/").filter(s => s !== ""); + if (deco.domain && deco.path.splice(MAX_URL_PATH_LEN).length > 0) + deco.path_truncated = true; + if (leading_dash || deco.path.length === 0) + deco.path.unshift(""); + + return deco; } -function* each_domain_pattern(domain_segments) +function* each_domain_pattern(deco) { - for (let slice = 0; slice < domain_segments.length; slice++) { - const domain_part = domain_segments.slice(slice).join("."); + for (let slice = 0; slice < deco.domain.length - 1; slice++) { + const domain_part = deco.domain.slice(slice).join("."); const domain_wildcards = []; - if (slice === 0) + if (slice === 0 && !deco.domain_truncated) yield domain_part; - if (slice === 1) + if (slice === 1 && !deco.domain_truncated) yield "*." + domain_part; if (slice > 1) yield "**." + domain_part; @@ -141,22 +94,23 @@ function* each_domain_pattern(domain_segments) } } -function* each_path_pattern(path_segments, trailing_dash) +function* each_path_pattern(deco) { - for (let slice = path_segments.length; slice > 0; slice--) { - const path_part = path_segments.slice(0, slice).join("/"); + for (let slice = deco.path.length; slice > 0; slice--) { + const path_part = deco.path.slice(0, slice).join("/"); const path_wildcards = []; - if (slice === path_segments.length) { - if (trailing_dash) + if (slice === deco.path.length && !deco.path_truncated) { + if (deco.trailing_dash) yield path_part + "/"; yield path_part; } - if (slice === path_segments.length - 1 && path_segments[slice] !== "*") + if (slice === deco.path.length - 1 && !deco.path_truncated && + deco.path[slice] !== "*") yield path_part + "/*"; - if (slice < path_segments.length - 1) + if (slice < deco.path.length - 1) yield path_part + "/**"; - if (slice < path_segments.length - 1 || - path_segments[path_segments.length - 1] !== "***") + if (slice !== deco.path.length - 1 || deco.path_truncated || + deco.path[slice] !== "***") yield path_part + "/***"; } } @@ -167,20 +121,19 @@ function* each_url_pattern(url) const deco = deconstruct_url(url); if (deco === undefined) { - console.log("bad url format", url); + console.error("bad url format", url); return false; } - const all_domains = deco.domain ? each_domain_pattern(deco.domain) : [""]; + const all_domains = deco.domain ? each_domain_pattern(deco) : [""]; for (const domain of all_domains) { - for (const path of each_path_pattern(deco.path, deco.trailing_dash)) + for (const path of each_path_pattern(deco)) yield `${deco.proto}://${domain}${path}`; } } /* * EXPORTS_START - * EXPORT url_matches * EXPORT each_url_pattern * EXPORTS_END */ diff --git a/html/display-panel.js b/html/display-panel.js index 623ff36..84c922f 100644 --- a/html/display-panel.js +++ b/html/display-panel.js @@ -21,7 +21,6 @@ * IMPORT TYPE_PREFIX * IMPORT nice_name * IMPORT open_in_settings - * IMPORT url_matches * IMPORT each_url_pattern * IMPORT by_id * IMPORT clone_template @@ -114,36 +113,21 @@ function add_pattern_to_list(pattern) return template; } -function ensure_pattern_exists(pattern) -{ - let entry_object = known_patterns.get(pattern); - /* - * As long as pattern computation works well, we should never get into this - * conditional block. This is just a safety measure. To be removed as part - * of a bigger rework when we start taking iframes into account. - */ - if (entry_object === undefined) { - console.log(`unknown pattern: ${pattern}`); - entry_object = add_pattern_to_list(pattern); - } - - return entry_object; -} - function style_possible_pattern_entry(pattern, exists_in_settings) { const [text, class_action] = exists_in_settings ? ["Edit", "add"] : ["Add", "remove"]; - const entry_object = ensure_pattern_exists(pattern); + const entry_object = known_patterns.get(pattern); - entry_object.button.textContent = `${text} setting`; - entry_object.entry.classList[class_action]("matched_pattern"); + if (entry_object) { + entry_object.button.textContent = `${text} setting`; + entry_object.entry.classList[class_action]("matched_pattern"); + } } function handle_page_change(change) { - if (url_matches(tab_url, change.item)) - style_possible_pattern_entry(change.item, change.new_val !== undefined); + style_possible_pattern_entry(change.item, change.new_val !== undefined); } function populate_possible_patterns_list(url) -- cgit v1.2.3 From 2bd35bc4b0d32b70320b06d932db90e75e89373e Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Mon, 13 Sep 2021 16:56:44 +0200 Subject: rename the extension to "Haketilo" --- README.txt | 6 +- background/cookie_filter.js | 17 ++--- background/main.js | 4 +- background/page_actions_server.js | 4 +- background/policy_injector.js | 26 ++++---- background/storage.js | 4 +- background/storage_server.js | 4 +- background/stream_filter.js | 6 +- common/ajax.js | 5 +- common/connection_types.js | 4 +- common/lock.js | 4 +- common/message_server.js | 4 +- common/misc.js | 4 +- common/observable.js | 5 +- common/once.js | 5 +- common/patterns.js | 4 +- common/sanitize_JSON.js | 5 +- common/settings_query.js | 4 +- common/signing.js | 7 ++- common/storage_client.js | 4 +- common/storage_light.js | 5 +- common/storage_raw.js | 5 +- common/stored_types.js | 4 +- content/activity_info_server.js | 7 ++- content/main.js | 22 ++++--- content/page_actions.js | 6 +- content/repo_query.js | 5 +- copyright | 2 +- html/DOM_helpers.js | 4 +- html/MOZILLA_scrollbar_fix.css | 6 +- html/back_button.css | 5 +- html/base.css | 4 +- html/default_blocking_policy.js | 5 +- html/display-panel.html | 8 ++- html/display-panel.js | 4 +- html/import_frame.js | 4 +- html/options.html | 6 +- html/options_main.js | 4 +- icons/hachette.svg | 127 -------------------------------------- icons/hachette128.png | Bin 6031 -> 0 bytes icons/hachette16.png | Bin 752 -> 0 bytes icons/hachette32.png | Bin 1358 -> 0 bytes icons/hachette48.png | Bin 2154 -> 0 bytes icons/hachette64.png | Bin 2908 -> 0 bytes icons/haketilo.svg | 127 ++++++++++++++++++++++++++++++++++++++ icons/haketilo128.png | Bin 0 -> 6031 bytes icons/haketilo16.png | Bin 0 -> 752 bytes icons/haketilo32.png | Bin 0 -> 1358 bytes icons/haketilo48.png | Bin 0 -> 2154 bytes icons/haketilo64.png | Bin 0 -> 2908 bytes manifest.json | 28 +++++---- re-generate_icons.sh | 2 +- 52 files changed, 292 insertions(+), 224 deletions(-) delete mode 100644 icons/hachette.svg delete mode 100644 icons/hachette128.png delete mode 100644 icons/hachette16.png delete mode 100644 icons/hachette32.png delete mode 100644 icons/hachette48.png delete mode 100644 icons/hachette64.png create mode 100644 icons/haketilo.svg create mode 100644 icons/haketilo128.png create mode 100644 icons/haketilo16.png create mode 100644 icons/haketilo32.png create mode 100644 icons/haketilo48.png create mode 100644 icons/haketilo64.png (limited to 'common/patterns.js') diff --git a/README.txt b/README.txt index ad640b0..1aec0ba 100644 --- a/README.txt +++ b/README.txt @@ -1,4 +1,4 @@ -# Hachette - Make The Web Great Again! # +# Haketilo - Make The Web Great Again! # This extension's goal is to allow replacing javascript served by websites with scripts specified by user. Something like NoScript and Greasemonkey @@ -9,7 +9,7 @@ Currently, the target browsers for this extension are Ungoogled Chromium and various forks of Firefox (version 60+). This extension is still in an early stage. Also see -`https://hachettebugs.koszko.org/projects/hachette/wiki/' for documentation in +`https://hydrillabugs.koszko.org/projects/haketilo/wiki/' for documentation in development. ## Installation ## @@ -28,6 +28,6 @@ various additional licenses and permissions for particular files. ## Contributing ## Get the code from: https://git.koszko.org/browser-extension/ -Come to: https://hachettebugs.koszko.org/projects/hachette +Come to: https://hydrillabugs.koszko.org/projects/haketilo Optionally, write to $(echo a29zemtvQGtvc3prby5vcmcK | base64 -d) diff --git a/background/cookie_filter.js b/background/cookie_filter.js index fea2d23..64d18b2 100644 --- a/background/cookie_filter.js +++ b/background/cookie_filter.js @@ -1,7 +1,8 @@ /** - * part of Hachette - * Filtering request headers to remove hachette cookies that might have slipped - * through. + * This file is part of Haketilo. + * + * Function: Filtering request headers to remove haketilo cookies that might + * have slipped through. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. @@ -13,29 +14,29 @@ * IMPORTS_END */ -function is_valid_hachette_cookie(cookie) +function is_valid_haketilo_cookie(cookie) { - const match = /^hachette-(\w*)=(.*)$/.exec(cookie); + const match = /^haketilo-(\w*)=(.*)$/.exec(cookie); if (!match) return false; return !extract_signed(match.slice(1, 3)).fail; } -function remove_hachette_cookies(header) +function remove_haketilo_cookies(header) { if (header.name !== "Cookie") return header; const cookies = header.value.split("; "); - const value = cookies.filter(c => !is_valid_hachette_cookie(c)).join("; "); + const value = cookies.filter(c => !is_valid_haketilo_cookie(c)).join("; "); return value ? {name: "Cookie", value} : null; } function filter_cookie_headers(headers) { - return headers.map(remove_hachette_cookies).filter(h => h); + return headers.map(remove_haketilo_cookies).filter(h => h); } /* diff --git a/background/main.js b/background/main.js index 03cd5d7..40b3a9e 100644 --- a/background/main.js +++ b/background/main.js @@ -1,5 +1,7 @@ /** - * Hachette main background script + * This file is part of Haketilo. + * + * Function: Main background script. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/background/page_actions_server.js b/background/page_actions_server.js index e21ca6e..156a79f 100644 --- a/background/page_actions_server.js +++ b/background/page_actions_server.js @@ -1,5 +1,7 @@ /** - * Hachette serving of page actions to content scripts + * This file is part of Haketilo. + * + * Function: Serving page actions to content scripts. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/background/policy_injector.js b/background/policy_injector.js index e5af055..881595b 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 @@ -19,10 +21,10 @@ function inject_csp_headers(headers, policy) { let csp_headers; let old_signature; - let hachette_header; + let haketilo_header; - for (const header of headers.filter(h => h.name === "x-hachette")) { - /* x-hachette header has format: _0_ */ + for (const header of headers.filter(h => h.name === "x-haketilo")) { + /* x-haketilo header has format: _0_ */ const match = /^([^_]+)_(0_.*)$/.exec(header.value); if (!match) continue; @@ -38,7 +40,7 @@ function inject_csp_headers(headers, policy) csp_headers = old_data.csp_headers; old_signature = old_data.policy_sig; - hachette_header = header; + haketilo_header = header; break; } @@ -53,9 +55,9 @@ function inject_csp_headers(headers, policy) headers.push(...csp_headers || []); } - if (!hachette_header) { - hachette_header = {name: "x-hachette"}; - headers.push(hachette_header); + if (!haketilo_header) { + haketilo_header = {name: "x-haketilo"}; + headers.push(haketilo_header); } if (old_signature) @@ -66,7 +68,7 @@ function inject_csp_headers(headers, policy) const later_30sec = new Date(new Date().getTime() + 30000).toGMTString(); headers.push({ name: "Set-Cookie", - value: `hachette-${signed_policy.join("=")}; Expires=${later_30sec};` + value: `haketilo-${signed_policy.join("=")}; Expires=${later_30sec};` }); /* @@ -74,9 +76,9 @@ function inject_csp_headers(headers, policy) * 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("_"); + let haketilo_data = {csp_headers, policy_sig: signed_policy[0]}; + haketilo_data = encodeURIComponent(JSON.stringify(haketilo_data)); + haketilo_header.value = sign_data(haketilo_data, 0).join("_"); if (!policy.allow) { headers.push({ diff --git a/background/storage.js b/background/storage.js index 12c0c61..a4e626a 100644 --- a/background/storage.js +++ b/background/storage.js @@ -1,5 +1,7 @@ /** - * Hachette storage manager + * This file is part of Haketilo. + * + * Function: Storage manager. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/background/storage_server.js b/background/storage_server.js index 2252eb5..73126d4 100644 --- a/background/storage_server.js +++ b/background/storage_server.js @@ -1,5 +1,7 @@ /** - * Hachette storage through connection (server side) + * This file is part of Haketilo. + * + * Function: Storage through messages (server side). * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/background/stream_filter.js b/background/stream_filter.js index 3e30a4b..e5e0827 100644 --- a/background/stream_filter.js +++ b/background/stream_filter.js @@ -1,5 +1,7 @@ /** - * Hachette modifying a web page using the StreamFilter API + * This file is part of Haketilo. + * + * Function: Modifying a web page using the StreamFilter API. * * Copyright (C) 2018 Giorgio Maone * Copyright (C) 2021 Wojtek Kosior @@ -173,7 +175,7 @@ function filter_data(properties, event) */ const dummy_script = - ``; + ``; const doctype_decl = /^(\s*"']*>)?/i.exec(decoded)[0]; decoded = doctype_decl + dummy_script + decoded.substring(doctype_decl.length); diff --git a/common/ajax.js b/common/ajax.js index 8082bbe..7269a8a 100644 --- a/common/ajax.js +++ b/common/ajax.js @@ -1,6 +1,7 @@ /** - * part of Hachette - * Wrapping XMLHttpRequest into a Promise. + * This file is part of Haketilo. + * + * Function: Wrapping XMLHttpRequest into a Promise. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/connection_types.js b/common/connection_types.js index 88c6964..3e9df56 100644 --- a/common/connection_types.js +++ b/common/connection_types.js @@ -1,5 +1,7 @@ /** - * Hachette background scripts message connection types "enum" + * This file is part of Haketilo. + * + * Function: Define an "enum" of message connection types. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/lock.js b/common/lock.js index 822ad1b..6cf0835 100644 --- a/common/lock.js +++ b/common/lock.js @@ -1,5 +1,7 @@ /** - * Hachette lock (aka binary semaphore aka mutex) + * This file is part of Haketilo. + * + * Function: Implement a lock (aka binary semaphore aka mutex). * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/message_server.js b/common/message_server.js index ea40487..c8c6696 100644 --- a/common/message_server.js +++ b/common/message_server.js @@ -1,5 +1,7 @@ /** - * Hachette message server + * This file is part of Haketilo. + * + * Function: Message server. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/misc.js b/common/misc.js index 6cded84..9ffb7ff 100644 --- a/common/misc.js +++ b/common/misc.js @@ -1,5 +1,7 @@ /** - * Hachette miscellaneous operations refactored to a separate file + * This file is part of Haketilo. + * + * Function: Miscellaneous operations refactored to a separate file. * * Copyright (C) 2021 Wojtek Kosior * Copyright (C) 2021 jahoti diff --git a/common/observable.js b/common/observable.js index 02f1c1b..ab3b444 100644 --- a/common/observable.js +++ b/common/observable.js @@ -1,6 +1,7 @@ /** - * part of Hachette - * Facilitate listening to events + * This file is part of Haketilo. + * + * Function: Facilitate listening to (internal, self-generated) events. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/once.js b/common/once.js index 098b43f..93e842f 100644 --- a/common/once.js +++ b/common/once.js @@ -1,5 +1,8 @@ /** - * Hachette feature initialization promise + * This file is part of Haketilo. + * + * Function: Wrap APIs that depend on some asynchronous initialization into + * promises. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/patterns.js b/common/patterns.js index ae29fcd..625be05 100644 --- a/common/patterns.js +++ b/common/patterns.js @@ -1,5 +1,7 @@ /** - * Hachette operations on page url patterns + * This file is part of Haketilo. + * + * Function: Operations on page URL patterns. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/sanitize_JSON.js b/common/sanitize_JSON.js index 8b86d2d..4cf1ef4 100644 --- a/common/sanitize_JSON.js +++ b/common/sanitize_JSON.js @@ -1,6 +1,7 @@ /** - * part of Hachette - * Powerful, full-blown format enforcer for externally-obtained JSON + * This file is part of Haketilo. + * + * Function: Powerful, full-blown format enforcer for externally-obtained JSON. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/settings_query.js b/common/settings_query.js index b54e580..7e1315e 100644 --- a/common/settings_query.js +++ b/common/settings_query.js @@ -1,5 +1,7 @@ /** - * Hachette querying page settings with regard to wildcard records + * This file is part of Haketilo. + * + * Function: Querying page settings. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/signing.js b/common/signing.js index 1904bcd..11cd442 100644 --- a/common/signing.js +++ b/common/signing.js @@ -1,6 +1,7 @@ /** - * part of Hachette - * Functions related to "signing" of data, refactored to a separate file. + * This file is part of Haketilo. + * + * Functions: Operations related to "signing" of data. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. @@ -16,7 +17,7 @@ /* * In order to make certain data synchronously accessible in certain contexts, - * hachette smuggles it in string form in places like cookies, URLs and headers. + * Haketilo smuggles it in string form in places like cookies, URLs and headers. * When using the smuggled data, we first need to make sure it isn't spoofed. * For that, we use this pseudo-signing mechanism. * diff --git a/common/storage_client.js b/common/storage_client.js index 2b2f495..ef4a0b8 100644 --- a/common/storage_client.js +++ b/common/storage_client.js @@ -1,5 +1,7 @@ /** - * Hachette storage through connection (client side) + * This file is part of Haketilo. + * + * Function: Storage through messages (client side). * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/storage_light.js b/common/storage_light.js index 067bf0c..32e3b1f 100644 --- a/common/storage_light.js +++ b/common/storage_light.js @@ -1,6 +1,7 @@ /** - * part of Hachette - * Storage manager, lighter than the previous one. + * This file is part of Haketilo. + * + * Function: Storage manager, lighter than the previous one. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/storage_raw.js b/common/storage_raw.js index 4c02ee4..e354b6b 100644 --- a/common/storage_raw.js +++ b/common/storage_raw.js @@ -1,6 +1,7 @@ /** - * part of Hachette - * Basic wrappers for storage API functions. + * This file is part of Haketilo. + * + * Function: Basic wrappers for storage API functions. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/common/stored_types.js b/common/stored_types.js index bfceba6..a693b1c 100644 --- a/common/stored_types.js +++ b/common/stored_types.js @@ -1,5 +1,7 @@ /** - * Hachette stored item types "enum" + * This file is part of Haketilo. + * + * Function: Define an "enum" of stored item types. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/content/activity_info_server.js b/content/activity_info_server.js index 1b69703..d1dfe36 100644 --- a/content/activity_info_server.js +++ b/content/activity_info_server.js @@ -1,7 +1,8 @@ /** - * part of Hachette - * Informing about activities performed by content script (script injection, - * script blocking). + * This file is part of Haketilo. + * + * Function: Informing the popup about what happens in the content script + * (script injection, script blocking, etc.). * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/content/main.js b/content/main.js index 6478ea0..cec9943 100644 --- a/content/main.js +++ b/content/main.js @@ -1,5 +1,7 @@ /** - * Hachette main content script run in all frames + * This file is part of Haketilo. + * + * Function: Main content script that runs in all frames. * * Copyright (C) 2021 Wojtek Kosior * Copyright (C) 2021 jahoti @@ -33,7 +35,7 @@ function extract_cookie_policy(cookie, min_time) let policy = null; const extracted_signatures = []; - for (const match of cookie.matchAll(/hachette-(\w*)=([^;]*)/g)) { + for (const match of cookie.matchAll(/haketilo-(\w*)=([^;]*)/g)) { const new_result = extract_signed(...match.slice(1, 3)); if (new_result.fail) continue; @@ -60,7 +62,7 @@ function extract_url_policy(url, min_time) const [base_url, payload, anchor] = /^([^#]*)#?([^#]*)(#?.*)$/.exec(url).splice(1, 4); - const match = /^hachette_([^_]+)_(.*)$/.exec(payload); + const match = /^haketilo_([^_]+)_(.*)$/.exec(payload); if (!match) return [null, url]; @@ -83,7 +85,7 @@ function employ_nonhttp_policy(policy) policy.nonce = gen_nonce(); const [base_url, target] = /^([^#]*)(#?.*)$/.exec(policy.url).slice(1, 3); const encoded_policy = encodeURIComponent(JSON.stringify(policy)); - const payload = "hachette_" + + const payload = "haketilo_" + sign_data(encoded_policy, new Date().getTime()).join("_"); const resulting_url = `${base_url}#${payload}${target}`; location.href = resulting_url; @@ -187,7 +189,7 @@ function sanitize_meta(meta) function sanitize_script(script) { - script.hachette_blocked_type = script.getAttribute("type"); + script.haketilo_blocked_type = script.getAttribute("type"); script.type = "text/plain"; } @@ -197,12 +199,12 @@ function sanitize_script(script) */ function desanitize_script(script) { - script.setAttribute("type", script.hachette_blocked_type); + script.setAttribute("type", script.haketilo_blocked_type); - if ([null, undefined].includes(script.hachette_blocked_type)) + if ([null, undefined].includes(script.haketilo_blocked_type)) script.removeAttribute("type"); - delete script.hachette_blocked_type; + delete script.haketilo_blocked_type; } const bad_url_reg = /^data:([^,;]*ml|unknown-content-type)/i; @@ -235,7 +237,7 @@ function start_data_urls_sanitizing(doc) */ function prevent_script_execution(event) { - if (!event.target._hachette_payload) + if (!event.target.haketilo_payload) event.preventDefault(); } @@ -336,7 +338,7 @@ if (!is_privileged_url(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;`; + document.cookie = `haketilo-${signature}=; Max-Age=-1;`; } else { const scheme = /^([^:]*)/.exec(document.URL)[1]; const known_scheme = ["file", "ftp"].includes(scheme); diff --git a/content/page_actions.js b/content/page_actions.js index 040b4ab..db7c352 100644 --- a/content/page_actions.js +++ b/content/page_actions.js @@ -1,5 +1,7 @@ /** - * Hachette handling of page actions in content scripts + * This file is part of Haketilo. + * + * Function: Handle page actions in a content script. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. @@ -60,7 +62,7 @@ function add_script(script_text) let script = document.createElement("script"); script.textContent = script_text; script.setAttribute("nonce", nonce); - script._hachette_payload = true; + script.haketilo_payload = true; document.body.appendChild(script); report_script(script_text); diff --git a/content/repo_query.js b/content/repo_query.js index 3708108..637282c 100644 --- a/content/repo_query.js +++ b/content/repo_query.js @@ -1,6 +1,7 @@ /** - * part of Hachette - * Getting available content for site from remote repositories. + * This file is part of Haketilo. + * + * Function: Getting available content for site from remote repositories. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/copyright b/copyright index 4c37eb3..fe2aed7 100644 --- a/copyright +++ b/copyright @@ -1,5 +1,5 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: Hachette +Upstream-Name: Haketilo Source: https://git.koszko.org/browser-extension/ Files: * diff --git a/html/DOM_helpers.js b/html/DOM_helpers.js index 01e2be9..4fe118d 100644 --- a/html/DOM_helpers.js +++ b/html/DOM_helpers.js @@ -1,5 +1,7 @@ /** - * Hachette operations on DOM elements + * This file is part of Haketilo. + * + * Function: Operations on DOM elements. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/html/MOZILLA_scrollbar_fix.css b/html/MOZILLA_scrollbar_fix.css index 5feb7c3..cdbd5c6 100644 --- a/html/MOZILLA_scrollbar_fix.css +++ b/html/MOZILLA_scrollbar_fix.css @@ -1,6 +1,8 @@ /** - * Hachette - * Hacky fix for vertical scrollbar width being included in child's width. + * This file is part of Haketilo. + * + * Function: Hacky fix for vertical scrollbar width being included in child's + * width. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/html/back_button.css b/html/back_button.css index 1ddc5da..b83e834 100644 --- a/html/back_button.css +++ b/html/back_button.css @@ -1,6 +1,7 @@ /** - * part of Hachette - * Style for a "back" button with a CSS arrow image. + * This file is part of Haketilo. + * + * Function: Style for a "back" button with a CSS arrow image. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/html/base.css b/html/base.css index df52f7c..517a5c1 100644 --- a/html/base.css +++ b/html/base.css @@ -1,5 +1,7 @@ /** - * Hachette base styles + * This file is part of Haketilo. + * + * Function: Base styles. * * Copyright (C) 2021 Wojtek Kosior * Copyright (C) 2021 Nicholas Johnson diff --git a/html/default_blocking_policy.js b/html/default_blocking_policy.js index 2f49bac..b6458f3 100644 --- a/html/default_blocking_policy.js +++ b/html/default_blocking_policy.js @@ -1,6 +1,7 @@ /** - * part of Hachette - * Default policy dialog logic. + * This file is part of Haketilo. + * + * Function: Logic for the dialog of default policy selection. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/html/display-panel.html b/html/display-panel.html index 3ed1b7a..ee9e767 100644 --- a/html/display-panel.html +++ b/html/display-panel.html @@ -1,12 +1,16 @@ - Hachette - page settings + Haketilo - page settings @@ -331,7 +335,7 @@ diff --git a/html/display-panel.js b/html/display-panel.js index 84c922f..c078850 100644 --- a/html/display-panel.js +++ b/html/display-panel.js @@ -1,5 +1,7 @@ /** - * Hachette display panel logic + * This file is part of Haketilo. + * + * Function: Popup logic. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/html/import_frame.js b/html/import_frame.js index c0eb2f0..ae6fab4 100644 --- a/html/import_frame.js +++ b/html/import_frame.js @@ -1,5 +1,7 @@ /** - * Hachette HTML import frame script + * This file is part of Haketilo. + * + * Function: Logic for the settings import frame. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/html/options.html b/html/options.html index 54ab9e8..2e8317c 100644 --- a/html/options.html +++ b/html/options.html @@ -1,12 +1,16 @@ - Hachette options + Haketilo options diff --git a/html/options_main.js b/html/options_main.js index 27ab0ec..f8faf9b 100644 --- a/html/options_main.js +++ b/html/options_main.js @@ -1,5 +1,7 @@ /** - * Hachette HTML options page main script + * This file is part of Haketilo. + * + * Function: Settings page logic. * * Copyright (C) 2021 Wojtek Kosior * Redistribution terms are gathered in the `copyright' file. diff --git a/icons/hachette.svg b/icons/hachette.svg deleted file mode 100644 index 6e8948d..0000000 --- a/icons/hachette.svg +++ /dev/null @@ -1,127 +0,0 @@ - - - Hatchet - - - - - - - - - - - - image/svg+xml - - Hatchet - - - David Lyons - - - - - dlyons - - - - 2017-05 - - - hatchet - ax - wood - - - Hatchet - - - - - - - - - - - - - - - - - - - - - - diff --git a/icons/hachette128.png b/icons/hachette128.png deleted file mode 100644 index 18816e9..0000000 Binary files a/icons/hachette128.png and /dev/null differ diff --git a/icons/hachette16.png b/icons/hachette16.png deleted file mode 100644 index 182ede5..0000000 Binary files a/icons/hachette16.png and /dev/null differ diff --git a/icons/hachette32.png b/icons/hachette32.png deleted file mode 100644 index ffaa84b..0000000 Binary files a/icons/hachette32.png and /dev/null differ diff --git a/icons/hachette48.png b/icons/hachette48.png deleted file mode 100644 index 1ffcd38..0000000 Binary files a/icons/hachette48.png and /dev/null differ diff --git a/icons/hachette64.png b/icons/hachette64.png deleted file mode 100644 index a02abb0..0000000 Binary files a/icons/hachette64.png and /dev/null differ diff --git a/icons/haketilo.svg b/icons/haketilo.svg new file mode 100644 index 0000000..6e8948d --- /dev/null +++ b/icons/haketilo.svg @@ -0,0 +1,127 @@ + + + Hatchet + + + + + + + + + + + + image/svg+xml + + Hatchet + + + David Lyons + + + + + dlyons + + + + 2017-05 + + + hatchet + ax + wood + + + Hatchet + + + + + + + + + + + + + + + + + + + + + + diff --git a/icons/haketilo128.png b/icons/haketilo128.png new file mode 100644 index 0000000..18816e9 Binary files /dev/null and b/icons/haketilo128.png differ diff --git a/icons/haketilo16.png b/icons/haketilo16.png new file mode 100644 index 0000000..182ede5 Binary files /dev/null and b/icons/haketilo16.png differ diff --git a/icons/haketilo32.png b/icons/haketilo32.png new file mode 100644 index 0000000..ffaa84b Binary files /dev/null and b/icons/haketilo32.png differ diff --git a/icons/haketilo48.png b/icons/haketilo48.png new file mode 100644 index 0000000..1ffcd38 Binary files /dev/null and b/icons/haketilo48.png differ diff --git a/icons/haketilo64.png b/icons/haketilo64.png new file mode 100644 index 0000000..a02abb0 Binary files /dev/null and b/icons/haketilo64.png differ diff --git a/manifest.json b/manifest.json index ce2577e..9d34732 100644 --- a/manifest.json +++ b/manifest.json @@ -1,18 +1,20 @@ +// This is the manifest file of Haketilo. +// // Copyright (C) 2021 Wojtek Kosior // Redistribution terms are gathered in the `copyright' file. { "manifest_version": 2, - "name": "Hachette", - "short_name": "Hachette", + "name": "Haketilo", + "short_name": "Haketilo", "version": "0.0.1", "author": "various", "description": "Control your \"Web\" browsing.",_GECKO_APPLICATIONS_ "icons":{ - "128": "icons/hachette128.png", - "64": "icons/hachette64.png", - "48": "icons/hachette48.png", - "32": "icons/hachette32.png", - "16": "icons/hachette16.png" + "128": "icons/haketilo128.png", + "64": "icons/haketilo64.png", + "48": "icons/haketilo48.png", + "32": "icons/haketilo32.png", + "16": "icons/haketilo16.png" }, "permissions": [ "contextMenus", @@ -29,13 +31,13 @@ "browser_action": { "browser_style": true, "default_icon": { - "128": "icons/hachette128.png", - "64": "icons/hachette64.png", - "48": "icons/hachette48.png", - "32": "icons/hachette32.png", - "16": "icons/hachette16.png" + "128": "icons/haketilo128.png", + "64": "icons/haketilo64.png", + "48": "icons/haketilo48.png", + "32": "icons/haketilo32.png", + "16": "icons/haketilo16.png" }, - "default_title": "Hachette", + "default_title": "Haketilo", "default_popup": "html/display-panel.html" }, "options_ui": { diff --git a/re-generate_icons.sh b/re-generate_icons.sh index ba0c28a..e557ad0 100755 --- a/re-generate_icons.sh +++ b/re-generate_icons.sh @@ -4,5 +4,5 @@ # Redistribution terms are gathered in the `copyright' file. for SIZE in 128 64 48 32 16; do - inkscape -z -e icons/hachette$SIZE.png -w $SIZE -h $SIZE icons/hachette.svg + inkscape -z -e icons/haketilo$SIZE.png -w $SIZE -h $SIZE icons/haketilo.svg done -- cgit v1.2.3 From 93dd73600e91eb19e11f5ca57f9429a85cf0150f Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Wed, 24 Nov 2021 15:53:00 +0100 Subject: improve unit testing approach Unit tests were moved to their own subdirectory. Fixtures common to many unit tests were moved to test/unit/conftest.py. A facility to execute scripts in page's global scope was added. A workaround was employed to present information about errors in injected scripts. Sample unit tests for regexes in common/patterns.js were added. --- common/patterns.js | 48 ++++++++++++-------- compute_scripts.awk | 14 ++++-- copyright | 5 ++- test/__init__.py | 1 + test/profiles.py | 5 +++ test/script_loader.py | 21 +++++---- test/test_unit.py | 57 ------------------------ test/unit/__init__.py | 2 + test/unit/conftest.py | 109 +++++++++++++++++++++++++++++++++++++++++++++ test/unit/test_basic.py | 41 +++++++++++++++++ test/unit/test_patterns.py | 91 +++++++++++++++++++++++++++++++++++++ 11 files changed, 305 insertions(+), 89 deletions(-) delete mode 100644 test/test_unit.py create mode 100644 test/unit/__init__.py create mode 100644 test/unit/conftest.py create mode 100644 test/unit/test_basic.py create mode 100644 test/unit/test_patterns.py (limited to 'common/patterns.js') diff --git a/common/patterns.js b/common/patterns.js index 625be05..635b128 100644 --- a/common/patterns.js +++ b/common/patterns.js @@ -7,15 +7,17 @@ * Redistribution terms are gathered in the `copyright' file. */ -const MAX_URL_PATH_LEN = 12; -const MAX_URL_PATH_CHARS = 255; -const MAX_DOMAIN_LEN = 7; -const MAX_DOMAIN_CHARS = 100; +const MAX = { + URL_PATH_LEN: 12, + URL_PATH_CHARS: 255, + DOMAIN_LEN: 7, + DOMAIN_CHARS: 100 +}; const proto_regex = /^(\w+):\/\/(.*)$/; const user_re = "[^/?#@]+@" -const domain_re = "[^/?#]+"; +const domain_re = "[.a-zA-Z0-9-]+"; const path_re = "[^?#]*"; const query_re = "\\??[^#]*"; @@ -25,11 +27,17 @@ const file_regex = new RegExp(`^(${path_re}).*`); const ftp_regex = new RegExp(`^(${user_re})?(${domain_re})(${path_re}).*`); -function deconstruct_url(url) +function deconstruct_url(url, use_limits=true) { + const max = MAX; + if (!use_limits) { + for (key in MAX) + max[key] = Infinity; + } + const proto_match = proto_regex.exec(url); if (proto_match === null) - return undefined; + throw `bad url '${url}'`; const deco = {proto: proto_match[1]}; @@ -37,20 +45,22 @@ function deconstruct_url(url) deco.path = file_regex.exec(proto_match[2])[1]; } else if (deco.proto === "ftp") { [deco.domain, deco.path] = ftp_regex.exec(proto_match[2]).slice(2, 4); - } else { + } else if (deco.proto === "http" || deco.proto === "https") { const http_match = http_regex.exec(proto_match[2]); if (!http_match) return undefined; [deco.domain, deco.path, deco.query] = http_match.slice(1, 4); + deco.domain = deco.domain.toLowerCase(); + } else { + throw `unsupported protocol in url '${url}'`; } - const leading_dash = deco.path[0] === "/"; deco.trailing_dash = deco.path[deco.path.length - 1] === "/"; if (deco.domain) { - if (deco.domain.length > MAX_DOMAIN_CHARS) { + if (deco.domain.length > max.DOMAIN_CHARS) { const idx = deco.domain.indexOf(".", deco.domain.length - - MAX_DOMAIN_CHARS); + max.DOMAIN_CHARS); if (idx === -1) deco.domain = []; else @@ -59,7 +69,7 @@ function deconstruct_url(url) deco.domain_truncated = true; } - if (deco.path.length > MAX_URL_PATH_CHARS) { + if (deco.path.length > max.URL_PATH_CHARS) { deco.path = deco.path.substring(0, deco.path.lastIndexOf("/")); deco.path_truncated = true; } @@ -67,16 +77,14 @@ function deconstruct_url(url) if (typeof deco.domain === "string") { deco.domain = deco.domain.split("."); - if (deco.domain.splice(0, deco.domain.length - MAX_DOMAIN_LEN).length + if (deco.domain.splice(0, deco.domain.length - max.DOMAIN_LEN).length > 0) deco.domain_truncated = true; } deco.path = deco.path.split("/").filter(s => s !== ""); - if (deco.domain && deco.path.splice(MAX_URL_PATH_LEN).length > 0) + if (deco.domain && deco.path.splice(max.URL_PATH_LEN).length > 0) deco.path_truncated = true; - if (leading_dash || deco.path.length === 0) - deco.path.unshift(""); return deco; } @@ -98,13 +106,14 @@ function* each_domain_pattern(deco) function* each_path_pattern(deco) { - for (let slice = deco.path.length; slice > 0; slice--) { - const path_part = deco.path.slice(0, slice).join("/"); + for (let slice = deco.path.length; slice >= 0; slice--) { + const path_part = ["", ...deco.path.slice(0, slice)].join("/"); const path_wildcards = []; if (slice === deco.path.length && !deco.path_truncated) { if (deco.trailing_dash) yield path_part + "/"; - yield path_part; + if (slice > 0 || deco.proto !== "file") + yield path_part; } if (slice === deco.path.length - 1 && !deco.path_truncated && deco.path[slice] !== "*") @@ -137,5 +146,6 @@ function* each_url_pattern(url) /* * EXPORTS_START * EXPORT each_url_pattern + * EXPORT deconstruct_url * EXPORTS_END */ diff --git a/compute_scripts.awk b/compute_scripts.awk index 2bad3c5..1f3b11e 100644 --- a/compute_scripts.awk +++ b/compute_scripts.awk @@ -105,8 +105,7 @@ function print_exports_code(filename, i, count, export_name) { } } -function wrap_file(filename) { - print "\"use strict\";\n\n({fun: (function() {\n" +function partially_wrap_file(filename) { print_imports_code(filename) printf "\n\n" @@ -114,6 +113,13 @@ function wrap_file(filename) { printf "\n\n" print_exports_code(filename) +} + +function wrap_file(filename) { + print "\"use strict\";\n\n({fun: (function() {\n" + + partially_wrap_file(filename) + print "\n})}).fun();" } @@ -151,7 +157,7 @@ function compute_dependencies(filename, i, count, import_name, next_file) { } function print_usage() { - printf "usage: %2 compute_scripts.awk script_dependencies|wrapped_code FILENAME[...]\n", + printf "usage: %2 compute_scripts.awk script_dependencies|wrapped_code|partially_wrapped_code FILENAME[...]\n", ARGV[0] > "/dev/stderr" exit 1 } @@ -189,6 +195,8 @@ BEGIN { print("exports_init.js") if (compute_dependencies(root_filename) > 0) exit 1 + } else if (operation == "partially_wrapped_code") { + partially_wrap_file(root_filename) } else if (operation == "wrapped_code") { wrap_file(root_filename) } else { diff --git a/copyright b/copyright index c7934b7..243b4a1 100644 --- a/copyright +++ b/copyright @@ -75,11 +75,12 @@ License: AGPL-3+ Comment: Wojtek Kosior promises not to sue even in case of violations of the license. -Files: test/__init__.py test/test_unit.py test/default_profiles/icecat_empty/extensions.json +Files: test/__init__.py test/unit/* + test/default_profiles/icecat_empty/extensions.json Copyright: 2021 Wojtek Kosior License: CC0 -Files: test/profiles.py test/script_loader.py +Files: test/profiles.py test/script_loader.py test/unit/conftest.py Copyright: 2021 Wojtek Kosior License: GPL-3+ Comment: Wojtek Kosior promises not to sue even in case of violations diff --git a/test/__init__.py b/test/__init__.py index 19b869e..2b351bb 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1 +1,2 @@ # SPDX-License-Identifier: CC0-1.0 +# Copyright (C) 2021 Wojtek Kosior diff --git a/test/profiles.py b/test/profiles.py index a833097..d6a4efc 100755 --- a/test/profiles.py +++ b/test/profiles.py @@ -43,11 +43,15 @@ def set_profile_proxy(profile, proxy_host, proxy_port): profile.set_preference(f'network.proxy.backup.{proto}', '') profile.set_preference(f'network.proxy.backup.{proto}_port', 0) +def set_profile_console_logging(profile): + profile.set_preference('devtools.console.stdout.content', True) + def firefox_safe_mode(firefox_binary=default_firefox_binary, proxy_host=default_proxy_host, proxy_port=default_proxy_port): profile = webdriver.FirefoxProfile() set_profile_proxy(profile, proxy_host, proxy_port) + set_profile_console_logging(profile) options = Options() options.add_argument('--safe-mode') @@ -61,6 +65,7 @@ def firefox_with_profile(firefox_binary=default_firefox_binary, proxy_port=default_proxy_port): profile = webdriver.FirefoxProfile(profile_dir) set_profile_proxy(profile, proxy_host, proxy_port) + set_profile_console_logging(profile) return webdriver.Firefox(firefox_profile=profile, firefox_binary=firefox_binary) diff --git a/test/script_loader.py b/test/script_loader.py index 22196c3..15269c7 100644 --- a/test/script_loader.py +++ b/test/script_loader.py @@ -49,14 +49,15 @@ def available_scripts(directory): if script_name_regex.match(script.name): yield script -def get_wrapped_script(script_path): +def wrapped_script(script_path, wrap_partially=True): if script_path == 'exports_init.js': with open(script_root / 'MOZILLA_exports_init.js') as script: return script.read() - awk = subprocess.run(['awk', '-f', str(awk_script), 'wrapped_code', - str(script_path)], - stdout=subprocess.PIPE, cwd=script_root, check=True) + command = 'partially_wrapped_code' if wrap_partially else 'wrapped_code' + awk_command = ['awk', '-f', str(awk_script), command, str(script_path)] + awk = subprocess.run(awk_command, stdout=subprocess.PIPE, cwd=script_root, + check=True) return awk.stdout.decode() @@ -67,8 +68,10 @@ def load_script(path, import_dirs): project directory. Return a string containing script from `path` together with all other - scripts it depends on, wrapped in the same way Haketilo's build system wraps - them, with imports properly satisfied. + scripts it depends. Dependencies are wrapped in the same way Haketilo's + build system wraps them, with imports properly satisfied. The main script + being loaded is wrapped partially - it also has its imports satisfied, but + its code is not placed inside an anonymous function, so the """ path = make_relative_path(path) @@ -79,6 +82,8 @@ def load_script(path, import_dirs): str(path), *[str(s) for s in available]], stdout=subprocess.PIPE, cwd=script_root, check=True) - output = awk.stdout.decode() + to_load = awk.stdout.decode().split() + texts = [wrapped_script(path, wrap_partially=(i == len(to_load) - 1)) + for i, path in enumerate(to_load)] - return '\n'.join([get_wrapped_script(path) for path in output.split()]) + return '\n'.join(texts) diff --git a/test/test_unit.py b/test/test_unit.py deleted file mode 100644 index ce46f88..0000000 --- a/test/test_unit.py +++ /dev/null @@ -1,57 +0,0 @@ -# SPDX-License-Identifier: CC0-1.0 - -""" -Haketilo unit tests -""" - -# This file is part of Haketilo -# -# Copyright (C) 2021, jahoti -# Copyright (C) 2021, Wojtek Kosior -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the CC0 1.0 Universal License as published by -# the Creative Commons Corporation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# CC0 1.0 Universal License for more details. - -import pytest -from .profiles import firefox_safe_mode -from .server import do_an_internet -from .script_loader import load_script - -@pytest.fixture(scope="module") -def proxy(): - httpd = do_an_internet() - yield httpd - httpd.shutdown() - -@pytest.fixture(scope="module") -def driver(proxy): - with firefox_safe_mode() as driver: - yield driver - driver.quit() - -def test_proxy(driver): - """ - A trivial test case that verifies mocked web pages served by proxy can be - accessed by the browser driven. - """ - for proto in ['http://', 'https://']: - driver.get(proto + 'gotmyowndoma.in') - element = driver.find_element_by_tag_name('title') - title = driver.execute_script('return arguments[0].innerText;', element) - assert "Schrodinger's Document" in title - -def test_script_loader(driver): - """ - A trivial test case that verifies Haketilo's .js files can be properly - loaded into a test page together with their dependencies. - """ - driver.get('http://gotmyowndoma.in') - driver.execute_script(load_script('common/stored_types.js', ['common'])) - get_var_prefix = 'return window.haketilo_exports.TYPE_PREFIX.VAR;' - assert driver.execute_script(get_var_prefix) == '_' diff --git a/test/unit/__init__.py b/test/unit/__init__.py new file mode 100644 index 0000000..2b351bb --- /dev/null +++ b/test/unit/__init__.py @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: CC0-1.0 +# Copyright (C) 2021 Wojtek Kosior diff --git a/test/unit/conftest.py b/test/unit/conftest.py new file mode 100644 index 0000000..6877b7a --- /dev/null +++ b/test/unit/conftest.py @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: GPL-3.0-or-later + +""" +Common fixtures for Haketilo unit tests +""" + +# This file is part of Haketilo. +# +# Copyright (C) 2021 Wojtek Kosior +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# I, Wojtek Kosior, thereby promise not to sue for violation of this file's +# license. Although I request that you do not make use this code in a +# proprietary program, I am not going to enforce this in court. + +import pytest + +from ..profiles import firefox_safe_mode +from ..server import do_an_internet +from ..script_loader import load_script + +@pytest.fixture(scope="package") +def proxy(): + httpd = do_an_internet() + yield httpd + httpd.shutdown() + +@pytest.fixture(scope="package") +def driver(proxy): + with firefox_safe_mode() as driver: + yield driver + driver.quit() + +script_injecting_script = '''\ +/* + * Selenium by default executes scripts in some weird one-time context. We want + * separately-loaded scripts to be able to access global variables defined + * before, including those declared with `const` or `let`. To achieve that, we + * run our scripts by injecting them into the page inside a