Chrome extension manifest v3 Content Security Policy
Asked Answered
C

2

27

I am trying to load (inject) in page a javascript code. The javascript file is local to the extension. the filepath is 'js/somefile.js'.

const basePath = chrome.runtime.getURL('');
    fetch(chrome.runtime.getURL(filePath), { mode: 'same-origin' }) // <-- important
      .then((_res) => _res.blob())
      .then((_blob) => {
        const reader = new FileReader();
        reader.addEventListener('loadend', (data) => {
          callback(data.currentTarget.result, basePath);
        });
        reader.readAsText(_blob);
      });

const scriptTag = document.createElement('script');
    scriptTag.innerHTML = scriptText;
    scriptTag.type = 'text/javascript';
    const scriptElement = document[injectLocation].appendChild(scriptTag);
    if (removeImmediately) document[injectLocation].removeChild(scriptElement);

My web accessible resources are:

"web_accessible_resources": [{
    "resources": [
    "js/*.js",
    ],
    "matches": ["<all_urls>"]
  }],

"content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'",
    "sandbox": "sandbox allow-scripts; script-src 'self' 'https://apis.google.com/' 'https://www.gstatic.com/' 'https://*.firebaseio.com' 'https://www.googleapis.com' 'https://ajax.googleapis.com'; object-src 'self'"
  },

The error that I get is:

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'". Either the 'unsafe-inline' keyword, a hash ('sha256-Wq/CW2mxkri68TjkuaA0+LnU0capVpyiEuSA5NOVNfU='), or a nonce ('nonce-...') is required to enable inline execution.
Callable answered 7/5, 2021 at 17:0 Comment(7)
The code you've shown is unrelated to the error which says you have an inline script which is a common problem in the popup or options pages: more info.Mackinaw
You are right, Indeed I am trying to inject said script in page. I saw that executeScript is a viable alternative. How can I aim to inject the code of a script in the current tab?Callable
Use a content script.Mackinaw
I updated the code with the offending line. It is the appendchild part that is blocked because V3 does not allow for injection of scripts. I am using a content script to do this but it still fails.Callable
Currently you use a content script to inject another script in page context, which is a very special thing needed to extract/access JS variables/functions from the page. To inject the code you don't need that. Simply inject the js file as a content script (declaratively or via executeScript).Mackinaw
I see makes a lot more sense now, thank youCallable
It's sucks, how to deal with third-party, in case they (third party) have some updates? End up compiling a new own script and include it into the extension bundle?Introgression
U
41

You can resolve the inline execution error by changing scriptTag.innerHTML = scriptText; to scriptTag.src = chrome.runtime.getURL(filePath);, no need to fetch the script. Manifest v3 seems to only allow injecting static scripts into the page context.

If you want to run dynamically sourced scripts I think this can be achieved by having the static (already trusted) script fetch a remote script then eval it.

UPDATE: example extension, with manifest v3, that injects a script that operates in the page context.

# myscript.js
window.variableInMainContext = "hi"
# manifest.json
{
  "name": "example",
  "version": "1.0",
  "description": "example extension",
  "manifest_version": 3,
  "content_scripts": [
    {
      "matches": ["https://*/*"],
      "run_at": "document_start",
      "js": ["inject.js"]
    }
  ],
  "web_accessible_resources": [
    {
      "resources": [ "myscript.js" ],
      "matches": [ "https://*/*" ]
    }
  ]
}

# inject.js

const nullthrows = (v) => {
    if (v == null) throw new Error("it's a null");
    return v;
}

function injectCode(src) {
    const script = document.createElement('script');
    // This is why it works!
    script.src = src;
    script.onload = function() {
        console.log("script injected");
        this.remove();
    };

    // This script runs before the <head> element is created,
    // so we add the script to <html> instead.
    nullthrows(document.head || document.documentElement).appendChild(script);
}


injectCode(chrome.runtime.getURL('/myscript.js'));
Ultramicroscope answered 28/9, 2021 at 11:38 Comment(12)
I've tried this when v3 was released and I could not access any of the javascript variables from the top window (website context). By access I mean. Say there is a window.text variable. Can I inject a script that does window.text='something';. So I cannot read the variable but I can still change it. From what I understand, this is not possible under v3.Callable
It's definitely possible to inject a script that does window.text='something'. I'm updating my answer with an example.Ultramicroscope
Yes it does work. I was able to verify it as well. What an elegant and simple solution! thank you so much!Callable
Can this work with an external script like paypal's smart button?Harvin
I'm getting this error on the remove call when I try to use it in typescipt: "TS2339: Property 'remove' does not exist on type 'GlobalEventHandlers'."Avertin
That saved me so much time, thanks <3Klockau
This works! However, it looks like it is bypassing the content security policy (CSP), so I'm wondering if at some point this could break.Wallen
"Manifest v3 seems to only allow injecting static scripts into the page context." I work on extensions at Google. This is definitely true.Carven
To get it to work for me in v3 I had to add the myscript.js to content_scripts as well.Merwin
I'm shocked this actually worked. Been fighting with this problem all week, since upgrading to V3. This method didn't work in V2, but I'm glad it does now!! Thank you!!!!Attempt
@JosephAstrahan Did you get an answer to your question? How did you make it work?We
@We unfortunately I didn't get it to work, I just forced the users to go to my website insteadHarvin
G
-4

Download the script files and put it on your project to make it local. It solved my content security policy problem.

Grip answered 28/5, 2022 at 3:57 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Avast

© 2022 - 2024 — McMap. All rights reserved.