summaryrefslogtreecommitdiff
path: root/content/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'content/main.js')
-rw-r--r--content/main.js78
1 files changed, 65 insertions, 13 deletions
diff --git a/content/main.js b/content/main.js
index b2cc9ed..a183913 100644
--- a/content/main.js
+++ b/content/main.js
@@ -13,7 +13,6 @@
* IMPORT sign_data
* IMPORT gen_nonce
* IMPORT is_privileged_url
- * IMPORT mozilla_suppress_scripts
* IMPORT is_chrome
* IMPORT is_mozilla
* IMPORT start_activity_info_server
@@ -132,10 +131,18 @@ function finish_waiting(waiting)
function _wait_for_head(doc, detached_html, callback)
{
const waiting = {doc, detached_html, callback, observers: []};
- if (try_body_started(waiting))
- return;
- waiting.observers = [make_body_start_observer(detached_html, waiting)];
+ /*
+ * For XML and SVG documents, instead of waiting for `<head>', we wait
+ * for the entire document to finish loading.
+ */
+ if (doc instanceof HTMLDocument) {
+ if (try_body_started(waiting))
+ return;
+
+ waiting.observers = [make_body_start_observer(detached_html, waiting)];
+ }
+
waiting.loaded_cb = () => finish_waiting(waiting);
doc.addEventListener("DOMContentLoaded", waiting.loaded_cb);
}
@@ -200,32 +207,72 @@ function desanitize_script(script, policy)
delete script.hachette_blocked_type;
}
-function apply_hachette_csp_rules(doc, policy)
+function apply_hachette_csp_rules(doc, head, policy)
{
const meta = doc.createElement("meta");
meta.setAttribute("http-equiv", "Content-Security-Policy");
meta.setAttribute("content", csp_rule(policy.nonce));
- doc.head.append(meta);
+ head.append(meta);
/* CSP is already in effect, we can remove the <meta> now. */
meta.remove();
}
+function sanitize_urls(element)
+{
+ for (const attribute of [...element.attributes]) {
+ if (/^(href|src|data)$/i.test(attribute.localName) &&
+ /^data:([^,;]*ml|unknown-content-type)/i.test(attribute.value))
+ block_attribute(element, attribute.localName);
+ }
+}
+
+function start_data_urls_sanitizing(doc)
+{
+ doc.querySelectorAll("*[href], *[src], *[data]").forEach(sanitize_urls);
+ const mutation_handler = m => m.addedNodes.forEach(sanitize_urls);
+ const mo = new MutationObserver(ms => ms.forEach(mutation_handler));
+ mo.observe(doc, {childList: true, subtree: true});
+}
+
+function apply_intrinsics_sanitizing(root_element)
+{
+ for (const subelem of root_element.querySelectorAll("*")) {
+ [...subelem.attributes]
+ .filter(a => /^on/i.test(a.localName))
+ .filter(a => /^javascript:/i.test(a.value))
+ .forEach(a => block_attribute(subelem, a.localName));
+ }
+}
+
async function sanitize_document(doc, policy)
{
/*
+ * Blocking of scripts that are in the DOM from the beginning. Needed for
+ * Mozilla, harmless on Chromium.
+ * Note that at least in SVG documents the `src' attr on `<script>'s seems
+ * to be ignored by Firefox, so we don't need to sanitize it.
+ */
+ for (const script of document.getElementsByTagName("script")) {
+ const old_children = [...script.childNodes];
+ script.innerHTML = "";
+ setTimeout(() => old_children.forEach(c => script.append(c)), 0);
+ }
+
+ /*
* Ensure our CSP rules are employed from the beginning. This CSP injection
* method is, when possible, going to be applied together with CSP rules
* injected using webRequest.
+ * For non-HTML documents this is just a dummy operation of adding and
+ * removing `head'.
*/
- const has_own_head = doc.head;
- if (!has_own_head)
- doc.documentElement.prepend(doc.createElement("head"));
+ let added_head = doc.createElement("head");
+ if (!doc.head)
+ doc.documentElement.prepend(added_head);
- apply_hachette_csp_rules(doc, policy);
+ apply_hachette_csp_rules(doc, added_head, policy);
- /* Probably not needed, but...: proceed with DOM in its initial state. */
- if (!has_own_head)
- doc.head.remove();
+ /* Proceed with DOM in its initial state. */
+ added_head.remove();
/*
* <html> node gets hijacked now, to be re-attached after <head> is loaded
@@ -243,10 +290,15 @@ async function sanitize_document(doc, policy)
for (const script of old_html.querySelectorAll("script"))
sanitize_script(script, policy);
+ if (!(doc instanceof HTMLDocument))
+ apply_intrinsics_sanitizing(old_html);
+
new_html.replaceWith(old_html);
for (const script of old_html.querySelectorAll("script"))
desanitize_script(script, policy);
+
+ start_data_urls_sanitizing(doc);
}
if (!is_privileged_url(document.URL)) {