Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: userScripts.execute() method #477

Closed
newRoland opened this issue Oct 26, 2023 · 12 comments
Closed

Proposal: userScripts.execute() method #477

newRoland opened this issue Oct 26, 2023 · 12 comments
Labels
neutral: safari Not opposed or supportive from Safari proposal Proposal for a change or new feature topic: user scripts

Comments

@newRoland
Copy link

newRoland commented Oct 26, 2023

The user-scripts proposal mentions this as a potential future enhancement, nevertheless, I'm creating a formal proposal so that it can be discussed and tracked.

Context

The userScripts API addresses a significant gap that MV3 created for extensions that need to execute user-supplied JavaScript. Nonetheless, a gap persists for userscripts not driven by URLs. Many extensions activate userscripts through varied methods including gestures, keyboard shortcuts, context menus, voice, and more.

Issue was first mentioned by @Robbendebiene.

Proposal

A method to execute user-supplied javascript on the fly.

browser.userScripts.execute({code: "", world, allFrames, etc})

Security Considerations

The current User Scripts proposal already allows this capability. The extension can register the user script to run on all domains, and call a wrapped userscript on demand. For example, when the user triggers a gesture. This is inefficient and it would be harmful to users if developers had to resort to that.

A few extensions that would need this.

Cr×Mouse Chrome™ Gestures
Vimium [1]
AutoHotkey
modern scroll
Script Runner Pro
Foxy Gestures
Gesturefy
smartUp Gestures
Tampermonkey, Violentmonkey (edge cases like RegExp)
Surfingkeys [1]
Automa [1]

@erosman
Copy link

erosman commented Oct 26, 2023

A method to execute user scripts on the fly.
browser.userScripts.execute({code: ""})

Firefox only userScripts in MV2 is replaced by scripting API in MV3 which has scripting.executeScript() method.

It is my understanding that the ability to pass a string to the scripting API is already agreed upon. Therefore, all its applicable methods should have the same ability.

@newRoland
Copy link
Author

newRoland commented Oct 26, 2023

@erosman

Is this confirmed? I was under the impression that they deliberately stripped away that parameter to prevent the execution of remote code.

@erosman
Copy link

erosman commented Oct 27, 2023

Although, I don't see executeScript() mentioned in the proposal, I cant imagine that it would be allowed in register() method and not in executeScript().
Userscript managers also require executeScript() for their functions.

It would be good to mention it in the user scripts proposal.


Footnote

While the User Scripts API mentions userScripts namespace, I am under the impression that implementation would be under the original scripting namespace.

userScripts

Note: When using Manifest V3 or higher, use scripting.registerContentScripts() to register scripts.

There will be an additional 'USER_SCRIPT' option in the scripting.ExecutionWorld.

scripting.ExecutionWorld

Specifies the execution environment of a script injected with scripting.executeScript() or registered with scripting.registerContentScripts().

@newRoland
Copy link
Author

newRoland commented Oct 28, 2023

While the User Scripts API mentions userScripts namespace, I am under the impression that implementation would be under the original scripting namespace.

For Chromium, the getScripts, register, unregister, and update methods have been implemented under the userScripts namespace.

@Rob--W
Copy link
Member

Rob--W commented Nov 23, 2023

Preface: I am focusing the discussion to the userScripts API, not the scripting API, given their separate use cases.

What is the request here? The ability to run arbitrary code through the userScripts API? Or the ability for user script managers to run a new user script in already-loaded documents?

For the latter, rather than a userScripts.execute method that mimics tabs.executeScript, I think that it would make more sense to allow a user script to be executed again in a specific context. That ensures that a user script will only run in contexts where a registered user script would usually run. Otherwise issue #8 would be encountered.

@newRoland
Copy link
Author

newRoland commented Nov 23, 2023

Use case is for extensions where users can execute user-supplied code through gestures, shortcut keys, etc. You can never predict when, or where the user will trigger the gesture, or shortcut, so it's not possible to bind it to a specific URL. I think userScripts.execute() is the right approach here.

To avoid issue #8, the userScripts.execute() function could take a documentId, and that way the developer can execute on a specific document.

Since MessageSender includes documentId, the flow is straightforward.

// content script where gesture, shortcut, voice command, etc is triggered. 
browser.runtime.sendMessage({action: "ExecuteUserScript", id: "foo"})

// background 
function onMessage(msg, sender, sendResponse) {
    if (msg.action === "ExecuteUserScript") {
        const code = await getUserScriptFromStorage(msg.id)
        userScripts.execute({code, documentId: sender.documentId})
    }
}

@xeenon xeenon added proposal Proposal for a change or new feature and removed needs-triage labels Nov 24, 2023
@brookhong
Copy link

What is the request here? The ability to run arbitrary code through the userScripts API? Or the ability for user script managers to run a new user script in already-loaded documents?

What is the difference?

Both are almost same to me. An extension user wants to create his/her own script(arbitrary code, also as a new user script) to be executed in the context of the extension(could be a user script manager) on document loaded.

@xeenon xeenon added the neutral: safari Not opposed or supportive from Safari label Dec 7, 2023
@ricobl
Copy link

ricobl commented Dec 12, 2023

I'd like to highlight a use case where something like userScripts.execute() would be useful.

Powerlet is an interface to search and execute scripts from bookmarklets. The user will click the extension button or use a keyboard shortcut to activate the extension popup, search from there and execute a particular script. This is equivalent to clicking on a bookmark button on the browser's bookmarks toolbar.

The project has an issue mentioning migration to MV3.

I've been experimenting with MV3, it might be my lack of experience but I don't see a way to support similar extensions with the current userScripts API as execution is limited to page load. (Which is what I believe this issue refers to.)

I believe that the scripting API for MV3 doesn't work either as it requires either a script file to be packaged with the extension or a function to be provided which will then be serialized / deserialized. Any form of converting a bookmarklet URL to a function will be blocked (think eval(code) or new Function(code)).

It'd be nice to continue being able to extend the browser like that, I see this no less secure than running scripts on page load, even less intrusive IMO.

@newRoland
Copy link
Author

newRoland commented Jan 7, 2024

@oliverdunk Any chance we get this before Chrome's MV2 deadline in June? Many MV2 Chrome extensions have features that require this API.

Current options for extensions that require this API.

  1. Wait it out and hope this is implemented before June's deadline.
  2. Hope the deadline is extended again.
  3. If it's not a core feature, get rid of it.
  4. Update to MV3 and use the wrapped userScript approach.

@oliverdunk
Copy link
Member

@newRoland, as you mention there is a wrapped userScript approach which should be sufficient as a workaround here. With that in mind, we're not considering this a blocker, and would suggest using the workaround for migration in the short term. There's definitely still a chance a way of doing one-time injection lands in time, since we're planning to do further work on the userScripts API over the next few months, I just can't make any promises :)

@Rob--W
Copy link
Member

Rob--W commented Feb 15, 2024

FYI: @EmiliaPaz has filed a PR with a proposal at #540

@newRoland
Copy link
Author

PR #540 has been approved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
neutral: safari Not opposed or supportive from Safari proposal Proposal for a change or new feature topic: user scripts
Projects
None yet
Development

No branches or pull requests

7 participants