aboutsummaryrefslogtreecommitdiff
path: root/common/misc.js
diff options
context:
space:
mode:
Diffstat (limited to 'common/misc.js')
-rw-r--r--common/misc.js159
1 files changed, 25 insertions, 134 deletions
diff --git a/common/misc.js b/common/misc.js
index 3c7dc46..5b0addb 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
@@ -8,9 +10,7 @@
/*
* IMPORTS_START
- * IMPORT sha256
* IMPORT browser
- * IMPORT is_chrome
* IMPORT TYPE_NAME
* IMPORT TYPE_PREFIX
* IMPORTS_END
@@ -38,94 +38,27 @@ function Uint8toHex(data)
return returnValue;
}
-function gen_nonce(length) // Default 16
+function gen_nonce(length=16)
{
- let randomData = new Uint8Array(length || 16);
+ let randomData = new Uint8Array(length);
crypto.getRandomValues(randomData);
return Uint8toHex(randomData);
}
-function gen_unique(url)
-{
- return sha256(get_secure_salt() + url);
-}
-
-function get_secure_salt()
-{
- if (is_chrome)
- return browser.runtime.getManifest().key.substring(0, 50);
- else
- return browser.runtime.getURL("dummy");
-}
-
-/*
- * stripping url from query and target (everything after `#' or `?'
- * gets removed)
- */
-function url_item(url)
-{
- let url_re = /^([^?#]*).*$/;
- let match = url_re.exec(url);
- return match[1];
-}
-
-/*
- * Assume a url like:
- * https://example.com/green?illuminati=confirmed#<injected-policy>#winky
- * This function will make it into an object like:
- * {
- * "base_url": "https://example.com/green?illuminati=confirmed",
- * "target": "#<injected-policy>",
- * "target2": "#winky",
- * "policy": <injected-policy-as-js-object>,
- * "current": <boolean-indicating-whether-policy-url-matches>
- * }
- * In case url doesn't have 2 #'s, target2 and target can be set to undefined.
- */
-function url_extract_target(url)
-{
- const url_re = /^([^#]*)((#[^#]*)(#.*)?)?$/;
- const match = url_re.exec(url);
- const targets = {
- base_url: match[1],
- target: match[3] || "",
- target2: match[4] || ""
- };
- if (!targets.target)
- return targets;
-
- /* %7B -> { */
- const index = targets.target.indexOf('%7B');
- if (index === -1)
- return targets;
-
- const now = new Date();
- const sig = targets.target.substring(1, index);
- const policy = targets.target.substring(index);
- if (sig !== sign_policy(policy, now) &&
- sig !== sign_policy(policy, now, -1))
- return targets;
-
- try {
- targets.policy = JSON.parse(decodeURIComponent(policy));
- targets.current = targets.policy.base_url === targets.base_url;
- } catch (e) {
- /* This should not be reached - it's our self-produced valid JSON. */
- console.log("Unexpected internal error - invalid JSON smuggled!", e);
- }
-
- return targets;
-}
-
-/* csp rule that blocks all scripts except for those injected by us */
-function csp_rule(nonce)
+/* CSP rule that blocks scripts according to policy's needs. */
+function make_csp_rule(policy)
{
- let rule = `script-src 'nonce-${nonce}';`;
- if (is_chrome)
- rule += `script-src-elem 'nonce-${nonce}';`;
+ let rule = "prefetch-src 'none'; script-src-attr 'none';";
+ const script_src = policy.nonce !== undefined ?
+ `'nonce-${policy.nonce}'` : "'none'";
+ rule += ` script-src ${script_src}; script-src-elem ${script_src};`;
return rule;
}
+/* Check if some HTTP header might define CSP rules. */
+const csp_header_regex =
+ /^\s*(content-security-policy|x-webkit-csp|x-content-security-policy)/i;
+
/*
* Print item together with type, e.g.
* nice_name("s", "hello") → "hello (script)"
@@ -143,17 +76,13 @@ function open_in_settings(prefix, name)
window.open(url, "_blank");
}
-/* Check if url corresponds to a browser's special page */
-function is_privileged_url(url)
-{
- return !!/^(chrome(-extension)?|moz-extension):\/\/|^about:/i.exec(url);
-}
-
-/* Sign a given policy for a given time */
-function sign_policy(policy, now, hours_offset) {
- let time = Math.floor(now / 3600000) + (hours_offset || 0);
- return gen_unique(time + policy);
-}
+/*
+ * Check if url corresponds to a browser's special page (or a directory index in
+ * case of `file://' protocol).
+ */
+const privileged_reg =
+ /^(chrome(-extension)?|moz-extension):\/\/|^about:|^file:\/\/.*\/$/;
+const is_privileged_url = url => privileged_reg.test(url);
/* Parse a CSP header */
function parse_csp(csp) {
@@ -174,41 +103,7 @@ function parse_csp(csp) {
return directives;
}
-/* Make CSP headers do our bidding, not interfere */
-function sanitize_csp_header(header, rule, block)
-{
- const csp = parse_csp(header.value);
-
- if (block) {
- /* No snitching */
- delete csp['report-to'];
- delete csp['report-uri'];
-
- delete csp['script-src'];
- delete csp['script-src-elem'];
-
- csp['script-src-attr'] = ["'none'"];
- csp['prefetch-src'] = ["'none'"];
- }
-
- if ('script-src' in csp)
- csp['script-src'].push(rule);
- else
- csp['script-src'] = [rule];
-
- if ('script-src-elem' in csp)
- csp['script-src-elem'].push(rule);
- else
- csp['script-src-elem'] = [rule];
-
- const new_policy = Object.entries(csp).map(
- i => `${i[0]} ${i[1].join(' ')};`
- );
-
- return {name: header.name, value: new_policy.join('')};
-}
-
-/* Regexes and objest to use as/in schemas for parse_json_with_schema(). */
+/* Regexes and objects to use as/in schemas for parse_json_with_schema(). */
const nonempty_string_matcher = /.+/;
const matchers = {
@@ -223,15 +118,11 @@ const matchers = {
/*
* EXPORTS_START
* EXPORT gen_nonce
- * EXPORT gen_unique
- * EXPORT url_item
- * EXPORT url_extract_target
- * EXPORT sign_policy
- * EXPORT csp_rule
+ * EXPORT make_csp_rule
+ * EXPORT csp_header_regex
* EXPORT nice_name
* EXPORT open_in_settings
* EXPORT is_privileged_url
- * EXPORT sanitize_csp_header
* EXPORT matchers
* EXPORTS_END
*/