Revision 44e89d8e
Added by koszko almost 2 years ago
content/main.js | ||
---|---|---|
16 | 16 |
* IMPORT is_chrome |
17 | 17 |
* IMPORT is_mozilla |
18 | 18 |
* IMPORT start_activity_info_server |
19 |
* IMPORT csp_rule |
|
20 |
* IMPORT is_csp_header_name |
|
21 |
* IMPORT sanitize_csp_header |
|
19 |
* IMPORT make_csp_rule |
|
20 |
* IMPORT csp_header_regex |
|
22 | 21 |
* IMPORTS_END |
23 | 22 |
*/ |
24 | 23 |
|
... | ... | |
172 | 171 |
const name = construct_name.join(""); |
173 | 172 |
seta(node, `${blocked_str}-${name}`, geta(node, name)); |
174 | 173 |
} |
175 |
} |
|
176 |
|
|
177 |
function sanitize_meta(meta, policy) |
|
178 |
{ |
|
179 |
const value = meta.content || ""; |
|
180 | 174 |
|
181 |
if (!value || !is_csp_header_name(meta.httpEquiv || "", true)) |
|
182 |
return; |
|
183 |
|
|
184 |
block_attribute(meta, "content"); |
|
175 |
rema(node, attr); |
|
185 | 176 |
} |
186 | 177 |
|
187 | 178 |
/* |
188 |
* Used to disable <script> that has not yet been added to live DOM (doesn't
|
|
189 |
* work for those already added). |
|
179 |
* Used to disable `<script>'s and `<meta>'s that have not yet been added to
|
|
180 |
* live DOM (doesn't work for those already added).
|
|
190 | 181 |
*/ |
182 |
function sanitize_meta(meta) |
|
183 |
{ |
|
184 |
if (csp_header_regex.test(meta.httpEquiv) && meta.content) |
|
185 |
block_attribute(meta, "content"); |
|
186 |
} |
|
187 |
|
|
191 | 188 |
function sanitize_script(script) |
192 | 189 |
{ |
193 | 190 |
script.hachette_blocked_type = script.getAttribute("type"); |
... | ... | |
195 | 192 |
} |
196 | 193 |
|
197 | 194 |
/* |
198 |
* Executed after script has been connected to the DOM, when it is no longer
|
|
199 |
* eligible for being executed by the browser |
|
195 |
* Executed after `<script>' has been connected to the DOM, when it is no longer
|
|
196 |
* eligible for being executed by the browser.
|
|
200 | 197 |
*/ |
201 |
function desanitize_script(script, policy)
|
|
198 |
function desanitize_script(script) |
|
202 | 199 |
{ |
203 | 200 |
script.setAttribute("type", script.hachette_blocked_type); |
204 | 201 |
|
205 |
if (script.hachette_blocked_type === null)
|
|
202 |
if ([null, undefined].includes(script.hachette_blocked_type))
|
|
206 | 203 |
script.removeAttribute("type"); |
207 | 204 |
|
208 | 205 |
delete script.hachette_blocked_type; |
... | ... | |
233 | 230 |
* cause part of the DOM to be loaded when our content scripts get to run. Thus, |
234 | 231 |
* before the CSP rules we inject (for non-HTTP pages) become effective, we need |
235 | 232 |
* to somehow block the execution of `<script>'s and intrinsics that were |
236 |
* already there. |
|
233 |
* already there. Additionally, some browsers (IceCat 60) seem to have problems |
|
234 |
* applying this CSP to non-inline `<scripts>' in certain scenarios. |
|
237 | 235 |
*/ |
236 |
function prevent_script_execution(event) |
|
237 |
{ |
|
238 |
if (!event.target._hachette_payload) |
|
239 |
event.preventDefault(); |
|
240 |
} |
|
241 |
|
|
238 | 242 |
function mozilla_initial_block(doc) |
239 | 243 |
{ |
240 |
const blocker = e => e.preventDefault(); |
|
241 |
doc.addEventListener("beforescriptexecute", blocker); |
|
242 |
setTimeout(() => doc.removeEventListener("beforescriptexecute", blocker)); |
|
244 |
doc.addEventListener("beforescriptexecute", prevent_script_execution); |
|
243 | 245 |
|
244 | 246 |
[...doc.all].flatMap(ele => [...ele.attributes].map(attr => [ele, attr])) |
245 | 247 |
.map(([ele, attr]) => [ele, attr.localName]) |
... | ... | |
273 | 275 |
* non-HTML documents. |
274 | 276 |
*/ |
275 | 277 |
const html = new DOMParser().parseFromString(`<html><head><meta \ |
276 |
http-equiv="Content-Security-Policy" content="${csp_rule(policy.nonce)}"\
|
|
278 |
http-equiv="Content-Security-Policy" content="${make_csp_rule(policy)}"\
|
|
277 | 279 |
/></head><body>Loading...</body></html>`, "text/html").documentElement; |
278 | 280 |
|
279 | 281 |
/* |
... | ... | |
284 | 286 |
root.replaceWith(html); |
285 | 287 |
|
286 | 288 |
/* |
287 |
* For XML documents, we don't intend to inject payload, so we neither block
|
|
288 |
* document's CSP `<meta>' tags nor wait for `<head>' to be parsed.
|
|
289 |
* When we don't inject payload, we neither block document's CSP `<meta>'
|
|
290 |
* tags nor wait for `<head>' to be parsed. |
|
289 | 291 |
*/ |
290 |
if (document instanceof HTMLDocument) {
|
|
292 |
if (policy.has_payload) {
|
|
291 | 293 |
await wait_for_head(doc, root); |
292 | 294 |
|
293 | 295 |
root.querySelectorAll("head meta") |
... | ... | |
333 | 335 |
policy = {allow: false, nonce: gen_nonce()}; |
334 | 336 |
} |
335 | 337 |
|
338 |
if (!(document instanceof HTMLDocument)) |
|
339 |
policy.has_payload = false; |
|
340 |
|
|
336 | 341 |
console.debug("current policy", policy); |
337 | 342 |
|
338 | 343 |
const doc_ready = Promise.all([ |
Also available in: Unified diff
simplify CSP handling
All page's CSP rules are now removed when a payload is to be injected. When there is no payload, CSP rules are not modified but only supplemented with Hachette's own.