javascriptgoogle-chromegoogle-chrome-extensionchrome-extension-manifest-v3

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


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:

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


Solution

  • 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.