Project

General

Profile

« Previous | Next » 

Revision 704f2da0

Added by koszko almost 2 years ago

re-enable sanitizing of data: URLs and also sanitize intrinsics on non-HTML pages where CSP doesn't work

View differences:

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