aboutsummaryrefslogtreecommitdiff
path: root/common/misc.js
diff options
context:
space:
mode:
Diffstat (limited to 'common/misc.js')
-rw-r--r--common/misc.js64
1 files changed, 53 insertions, 11 deletions
diff --git a/common/misc.js b/common/misc.js
index 7e48059..8cb26ab 100644
--- a/common/misc.js
+++ b/common/misc.js
@@ -15,6 +15,14 @@
* IMPORTS_END
*/
+/* Generate a random base64-encoded 128-bit sequence */
+function gen_nonce()
+{
+ let randomData = new Uint8Array(16);
+ crypto.getRandomValues(randomData);
+ return btoa(String.fromCharCode.apply(null, randomData));
+}
+
/*
* generating unique, per-site value that can be computed synchronously
* and is impossible to guess for a malicious website
@@ -61,24 +69,51 @@ function url_item(url)
}
/*
- * Assume a url like: https://example.com/green?illuminati=confirmed#tinky#winky
+ * 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" : "#tinky",
- * "target2" : "#winky"
+ * "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)
{
- let url_re = /^([^#]*)((#[^#]*)(#.*)?)?$/;
- let match = url_re.exec(url);
- return {
- base_url : match[1],
- target : match[3],
- target2 : match[4]
+ 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 */
@@ -113,12 +148,19 @@ 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);
+}
+
/*
* EXPORTS_START
- * EXPORT gen_unique
* EXPORT gen_nonce
+ * EXPORT gen_unique
* EXPORT url_item
* EXPORT url_extract_target
+ * EXPORT sign_policy
* EXPORT csp_rule
* EXPORT nice_name
* EXPORT open_in_settings