How to inject a script from text string provided by the user or external API?
B

1

6

I'm making an extension for chrome where the user can input a script, then press "run" to inject it into the current tab. I am using MV3 (manifest v3). Are there any ways to do this?

My code:

HTML:

<div class="scriptrunner">
    <h1>Script Runner</h1>
    <textarea placeholder="Enter script here" id="script"></textarea>
    <button id="run">Run Script</button>
</div>

Javascript:

let button = document.getElementById("run");
button.addEventListener("click", async () => {
    let input = document.getElementById("script");
    let script = input.value;
    // this is where the script would be ran
});

I've tried the following:

  • Using chrome.scripting.executeScript()
  • Using eval()
  • Using chrome.scripting.executeScript() to insert a script tag with a function, then running the function

I just started working on chrome extensions, so maybe I missed something, or this is just impossible.

Backbone answered 2/2, 2022 at 1:5 Comment(0)
C
11

chrome.userScripts API in Chrome 120+

Requires each user of the extension to enable Developer mode in chrome://extensions, which may be problematic as it globally allows this dangerous permission for all installed extensions.

Examples: https://stackoverflow.com/a/77579988

Personal workaround via MAIN world

Create the code in the unsafe page context (MAIN world) and not in the default ISOLATED world of a content script.

async function execInPage(code) {
  const [tab] = await chrome.tabs.query({currentWindow: true, active: true});
  chrome.scripting.executeScript({
    target: {tabId: tab.id},
    func: code => {
      const el = document.createElement('script');
      el.textContent = code;
      document.documentElement.appendChild(el);
      el.remove();
    },
    args: [code],
    world: 'MAIN',
    //injectImmediately: true, // Chrome 102+
  });
}

execInPage('console.log(123)');

Warning! You can freely use it in an unpacked extension, but using it in a web store extension is a risk as it violates their policy. Also, this will be blocked by a site with a strict Content-Security-Policy, in which case you can remove this header via declarativeNetRequest API, but that reduces the security of the site and may be disallowed by the web store as well if you do it unconditionally, i.e. you should add an option (disabled by default) which the user would enable explicitly.

Cahan answered 2/2, 2022 at 2:37 Comment(8)
Thanks so much! However, this script only works on certain websites. On some, it throws the error Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self'. Is there any way to fix this?Backbone
It means these sites have a strict CSP which you'll have to disable or circumvent e.g. by removing Content-Security-Policy header using declarativeNetRequest.Cahan
I'm so sorry, but how would I do this with declarativeNetRequest? Would it involve making a rule set or something else I missed?Backbone
See the examples in the documentation and add a rule that removes this header.Cahan
I checked the documentation, and I added a rule using what it said. It's still not working.Backbone
It's a separate problem for which you can open a new question with MCVE.Cahan
It might not work if website adds strict CSP via HTML <meta> tag as extension cannot remove it to disable CSP.Assure
You can remove it via chrome.debugger API which allows patching the response body of any requests.Cahan

© 2022 - 2024 — McMap. All rights reserved.