Revision 704f2da0
Added by koszko almost 2 years ago
content/main.js | ||
---|---|---|
13 | 13 |
* IMPORT sign_data |
14 | 14 |
* IMPORT gen_nonce |
15 | 15 |
* IMPORT is_privileged_url |
16 |
* IMPORT mozilla_suppress_scripts |
|
17 | 16 |
* IMPORT is_chrome |
18 | 17 |
* IMPORT is_mozilla |
19 | 18 |
* IMPORT start_activity_info_server |
... | ... | |
132 | 131 |
function _wait_for_head(doc, detached_html, callback) |
133 | 132 |
{ |
134 | 133 |
const waiting = {doc, detached_html, callback, observers: []}; |
135 |
if (try_body_started(waiting)) |
|
136 |
return; |
|
137 | 134 |
|
138 |
waiting.observers = [make_body_start_observer(detached_html, waiting)]; |
|
135 |
/* |
|
136 |
* For XML and SVG documents, instead of waiting for `<head>', we wait |
|
137 |
* for the entire document to finish loading. |
|
138 |
*/ |
|
139 |
if (doc instanceof HTMLDocument) { |
|
140 |
if (try_body_started(waiting)) |
|
141 |
return; |
|
142 |
|
|
143 |
waiting.observers = [make_body_start_observer(detached_html, waiting)]; |
|
144 |
} |
|
145 |
|
|
139 | 146 |
waiting.loaded_cb = () => finish_waiting(waiting); |
140 | 147 |
doc.addEventListener("DOMContentLoaded", waiting.loaded_cb); |
141 | 148 |
} |
... | ... | |
200 | 207 |
delete script.hachette_blocked_type; |
201 | 208 |
} |
202 | 209 |
|
203 |
function apply_hachette_csp_rules(doc, policy) |
|
210 |
function apply_hachette_csp_rules(doc, head, policy)
|
|
204 | 211 |
{ |
205 | 212 |
const meta = doc.createElement("meta"); |
206 | 213 |
meta.setAttribute("http-equiv", "Content-Security-Policy"); |
207 | 214 |
meta.setAttribute("content", csp_rule(policy.nonce)); |
208 |
doc.head.append(meta);
|
|
215 |
head.append(meta); |
|
209 | 216 |
/* CSP is already in effect, we can remove the <meta> now. */ |
210 | 217 |
meta.remove(); |
211 | 218 |
} |
212 | 219 |
|
220 |
function sanitize_urls(element) |
|
221 |
{ |
|
222 |
for (const attribute of [...element.attributes]) { |
|
223 |
if (/^(href|src|data)$/i.test(attribute.localName) && |
|
224 |
/^data:([^,;]*ml|unknown-content-type)/i.test(attribute.value)) |
|
225 |
block_attribute(element, attribute.localName); |
|
226 |
} |
|
227 |
} |
|
228 |
|
|
229 |
function start_data_urls_sanitizing(doc) |
|
230 |
{ |
|
231 |
doc.querySelectorAll("*[href], *[src], *[data]").forEach(sanitize_urls); |
|
232 |
const mutation_handler = m => m.addedNodes.forEach(sanitize_urls); |
|
233 |
const mo = new MutationObserver(ms => ms.forEach(mutation_handler)); |
|
234 |
mo.observe(doc, {childList: true, subtree: true}); |
|
235 |
} |
|
236 |
|
|
237 |
function apply_intrinsics_sanitizing(root_element) |
|
238 |
{ |
|
239 |
for (const subelem of root_element.querySelectorAll("*")) { |
|
240 |
[...subelem.attributes] |
|
241 |
.filter(a => /^on/i.test(a.localName)) |
|
242 |
.filter(a => /^javascript:/i.test(a.value)) |
|
243 |
.forEach(a => block_attribute(subelem, a.localName)); |
|
244 |
} |
|
245 |
} |
|
246 |
|
|
213 | 247 |
async function sanitize_document(doc, policy) |
214 | 248 |
{ |
249 |
/* |
|
250 |
* Blocking of scripts that are in the DOM from the beginning. Needed for |
|
251 |
* Mozilla, harmless on Chromium. |
|
252 |
* Note that at least in SVG documents the `src' attr on `<script>'s seems |
|
253 |
* to be ignored by Firefox, so we don't need to sanitize it. |
|
254 |
*/ |
|
255 |
for (const script of document.getElementsByTagName("script")) { |
|
256 |
const old_children = [...script.childNodes]; |
|
257 |
script.innerHTML = ""; |
|
258 |
setTimeout(() => old_children.forEach(c => script.append(c)), 0); |
|
259 |
} |
|
260 |
|
|
215 | 261 |
/* |
216 | 262 |
* Ensure our CSP rules are employed from the beginning. This CSP injection |
217 | 263 |
* method is, when possible, going to be applied together with CSP rules |
218 | 264 |
* injected using webRequest. |
265 |
* For non-HTML documents this is just a dummy operation of adding and |
|
266 |
* removing `head'. |
|
219 | 267 |
*/ |
220 |
const has_own_head = doc.head;
|
|
221 |
if (!has_own_head)
|
|
222 |
doc.documentElement.prepend(doc.createElement("head"));
|
|
268 |
let added_head = doc.createElement("head");
|
|
269 |
if (!doc.head)
|
|
270 |
doc.documentElement.prepend(added_head);
|
|
223 | 271 |
|
224 |
apply_hachette_csp_rules(doc, policy); |
|
272 |
apply_hachette_csp_rules(doc, added_head, policy);
|
|
225 | 273 |
|
226 |
/* Probably not needed, but...: proceed with DOM in its initial state. */ |
|
227 |
if (!has_own_head) |
|
228 |
doc.head.remove(); |
|
274 |
/* Proceed with DOM in its initial state. */ |
|
275 |
added_head.remove(); |
|
229 | 276 |
|
230 | 277 |
/* |
231 | 278 |
* <html> node gets hijacked now, to be re-attached after <head> is loaded |
... | ... | |
243 | 290 |
for (const script of old_html.querySelectorAll("script")) |
244 | 291 |
sanitize_script(script, policy); |
245 | 292 |
|
293 |
if (!(doc instanceof HTMLDocument)) |
|
294 |
apply_intrinsics_sanitizing(old_html); |
|
295 |
|
|
246 | 296 |
new_html.replaceWith(old_html); |
247 | 297 |
|
248 | 298 |
for (const script of old_html.querySelectorAll("script")) |
249 | 299 |
desanitize_script(script, policy); |
300 |
|
|
301 |
start_data_urls_sanitizing(doc); |
|
250 | 302 |
} |
251 | 303 |
|
252 | 304 |
if (!is_privileged_url(document.URL)) { |
Also available in: Unified diff
re-enable sanitizing of data: URLs and also sanitize intrinsics on non-HTML pages where CSP doesn't work