Project

General

Profile

« Previous | Next » 

Revision 03d041ce

Added by koszko about 2 years ago

only apply stream filter modifications when reasonably necessary

View differences:

background/main.js
57 57
	return;
58 58

  
59 59
    const [pattern, settings] = query_best(storage, details.url);
60
    const allow = !!(settings && settings.allow);
60
    const has_payload = !!(settings && settings.components);
61
    const allow = !has_payload && !!(settings && settings.allow);
61 62
    const nonce = gen_nonce();
62
    const policy = {allow, url, nonce};
63
    const policy = {allow, url, nonce, has_payload};
63 64

  
64 65
    let headers = details.responseHeaders;
65 66
    let skip = false;
background/stream_filter.js
12 12
/*
13 13
 * IMPORTS_START
14 14
 * IMPORT browser
15
 * IMPORT is_csp_header_name
15 16
 * IMPORTS_END
16 17
 */
17 18

  
......
110 111
    return new TextDecoder(charset || "latin1");
111 112
}
112 113

  
114
function may_define_csp_rules(html)
115
{
116
    const doc = new DOMParser().parseFromString(html, "text/html");
117

  
118
    for (const meta of doc.querySelectorAll("head>meta[http-equiv]")) {
119
	if (is_csp_header_name(meta.getAttribute("http-equiv"), true) &&
120
	    meta.content)
121
	    return true;
122
    }
123

  
124
    /*
125
     * Even if no naughty `<meta>' tags were found, subsequent chunk of HTML
126
     * data could add some. Before we return `false' we need to be sure we
127
     * reached the start of `<body>' where `<meta>' tags are no longer valid.
128
     */
129

  
130
    if (doc.documentElement.nextSibling || doc.body.nextSibling ||
131
	doc.body.childNodes.length > 1)
132
	return false;
133

  
134
    if (!doc.body.firstChild)
135
	return true;
136

  
137
    if (doc.body.firstChild.nodeName !== "#text")
138
	return false;
139

  
140
    return /^(<\/|&#|.)$/.test(doc.body.firstChild.wholeText);
141
}
142

  
113 143
function filter_data(properties, event)
114 144
{
115 145
    const data = new Uint8Array(event.data);
......
118 148
	first_chunk = true;
119 149
	properties.decoder = create_decoder(properties, data);
120 150
	properties.encoder = new TextEncoder();
121
	/* Force UTF-8, this is the only encoding we can produce. */
122
	properties.filter.write(new Uint8Array(UTF8_BOM));
123 151
    }
124 152

  
125 153
    let decoded = properties.decoder.decode(data);
126 154

  
127
    if (first_chunk) {
155
    /* Force UTF-8, this is the only encoding we can produce. */
156
    if (first_chunk)
157
	properties.filter.write(new Uint8Array(UTF8_BOM));
158

  
159
    if (first_chunk && may_define_csp_rules(decoded)) {
128 160
	/*
129 161
	 * HAX! Our content scripts that execute at `document_start' will always
130 162
	 * run before the first script in the document, but under Mozilla some
......
133 165
	 * will force `document_start' to happen earlier. This way our content
134 166
	 * scripts will be able to sanitize `http-equiv' tags with CSP rules
135 167
	 * that would otherwise stop our injected scripts from executing.
168
	 *
169
	 * As we want to only process HTML files that happen to have naughty
170
	 * `<meta>' tags in `<head>', we use a DOMParser-based heuristic in
171
	 * `may_define_rules()'. We don't do any additional MIME sniffing as it
172
	 * is too unreliable (and our heuristic will likely mark non-HTML files
173
	 * as harmless anyway).
136 174
	 */
175

  
137 176
	const dummy_script =
138 177
	      `<script data-hachette-deleteme="${properties.policy.nonce}" nonce="${properties.policy.nonce}">null</script>`;
139 178
	const doctype_decl = /^(\s*<!doctype[^<>"']*>)?/i.exec(decoded)[0];
......
149 188

  
150 189
function apply_stream_filter(details, headers, policy)
151 190
{
152
    if (policy.allow)
191
    if (!policy.has_payload)
153 192
	return headers;
154 193

  
155 194
    const properties = properties_from_headers(headers);

Also available in: Unified diff