Do page CSPs apply to Safari WebExtension content scripts?

Currently, if a page csp disallows inline scripts, but the content script attempts implant inline scripts (DOM injected scripts) - the code will fail to execute.

This is an issue with the current Safari extension model. The effect of this is that content scripts that inject dynamic (user-generated) content can not properly interact with the page context.

This is also an issue with other major browsers, however I do not believe Chrome has this restriction. The reason I say "do not believe" is because the documentation indicates that "page csp does not apply to content scripts", but I have not personally tested this.

I'd like to know is this restriction will be present with the new Safari WebExtension model, especially considering that webRequestBlocking is not supported (which would be the only workaround I am aware of).









  • (moved to an answer)

Add a Comment

Replies

The page CSP applies to scripts, styles, images, etc that you add to the DOM. The page CSP does not apply to content scripts, which run in their own world. This matches other browsers.
Thanks for the reply, Timothy.

The model you are using for WebExtensions sounds like it hasn't deviated from the model you are currently using with Safari App Extension, is that correct?

When you say that it matches other browsers are you referring to Firefox or Chrome? It seems like they handle Page CSPs a bit different. I can't link out here in the forum but I will include an excerpt from Chrome's documentation:

Additionally, the CSP of the page does not apply to content scripts. More complicated are <script> tags that content scripts create and put into the DOM of the page they are running on. We will refer to these as DOM injected scripts going forward.

DOM injected scripts that would be executed immediately upon injection into the page will execute as you might expect. Imagine a content script with the following code as a simple example: document.write("<script>alert(1);</script>");

This content script will cause an alert immediately upon the document.write(). Note that this will execute regardless of the policy a page may specify.

Currently in Safari, if a page csp exclude inline scripts, document.write("<script>alert(1);</script>"); will not execute. However with Chrome this is seemingly possible, regardless of the page csp.

Will the new WebExtension model be in line with this?
The documentation for Chrome does not seem to match what they have implemented. Maybe they made a change at some point?

I got an error trying to do the document.write example in Chrome, just like Safari.
Thanks for the reply, Timothy.

That's interesting. I did not try it myself (yet). I figured the documentation would valid, but perhaps it is not and they changed it along the way at some point. Even if it *does* work as they've suggested in their docs, I am assuming Safari will not mimic this behavior; is that fair to say?

The reason I am curious about all this is that I am trying to figure out solution to deal with strict CSPs when attempting to inject dynamic javascript creating by an extension. Currently I add a listener that detects security policy violations when attempting to inline code and if that happens, I fall back to "evaling" the code from the content script, but then that "evaled" code lives in the content script context and not the page. I should note that the extension is written in the current App Extension format, not WebExtensions, but I am considering a re-write if feature set is "better" with one format.

One method would be to rewrite the headers (if webRequestBlocking was supported) to add an exception for the script(s), but I am not sure if that is a great solution even if webRequestBlocking was supported.

There appears to be a difference with regards to injecting <script> elements.

In order to force Google docs to show its annotated canvas, Google recommends extensions use code such as the following in their content script:

const scriptElem = document.createElement('script');
scriptElem.textContent = "(function() { window['_docs_annotate_canvas_by_ext'] = '<extension id>'; })();";
(document.head || document.documentElement).appendChild(scriptElem);
scriptElem.remove();

This works as expected on Chrome and Firefox but on Safari it produces:

Refused to execute a script because its hash, its nonce, or 'unsafe-inline' does not appear in the script-src directive of the Content Security Policy.

For what it's worth, unsafe-inline does appear in the host page's CSP (i.e. that of docs.google.com) and adding the script's hash to a content_security_policy member of the extension's manifest does not appear to help either.