erosman / support

Support Location for all my extensions
Mozilla Public License 2.0
175 stars 12 forks source link

[FireMonkey] No permission to access `unsafeWindow.__proto__` prop #664

Open Sv443 opened 2 days ago

Sv443 commented 2 days ago

For my library it is imperative that it is able to modify the __proto__ property of the window object, so that it can shim the addEventListener function to do selective event discarding: https://github.com/Sv443-Network/UserUtils/blob/main/lib/dom.ts#L128

However, when anything on the __proto__ is accessed, the page itself throws an Uncaught Error: Permission denied to access object, which causes the main page script to throw and halt execution, meaning the entire page doesn't load:

Click to show image ![image](https://github.com/user-attachments/assets/f85cb364-957d-4657-ad1e-0ce3a90e5579)

This doesn't occur on ViolentMonkey and TamperMonkey, but I did notice the same error on GreaseMonkey.
This could of course be intentional to prevent prototype pollution through userscripts, in which case I would love it if you could add some kind of way of enabling this functionality, like with a userscript metadata directive.
I am aware modifying prototypes is heavily discouraged, but there is literally no other way to achieve this, plus my library isn't the only one relying on this functionality, so I feel like there should be a way to allow it, while making it clear to the end user that that could be a potential attack vector.

Here is an example userscript to reproduce it:

// ==UserScript==
// @name             test
// @match            *://music.youtube.com/*
// @version          1.0
// @grant            unsafeWindow
// ==/UserScript==

interceptEvent(unsafeWindow, "onbeforeunload", () => true);

// better readable version here: https://github.com/Sv443-Network/UserUtils/blob/main/lib/dom.ts#L92
function interceptEvent(eventObject, eventName, predicate = () => true) {
  Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
  if (isNaN(Error.stackTraceLimit))
    Error.stackTraceLimit = 100;
  (function(original) {
    eventObject.__proto__.addEventListener = function(...args) {
      var _a, _b;
      const origListener = typeof args[1] === "function" ? args[1] : (_b = (_a = args[1]) == null ? void 0 : _a.handleEvent) != null ? _b : () => void 0;
      args[1] = function(...a) {
        if (args[0] === eventName && predicate(Array.isArray(a) ? a[0] : a))
          return;
        else
          return origListener.apply(this, a);
      };
      original.apply(this, args);
    };
  })(eventObject.__proto__.addEventListener);
}
erosman commented 2 days ago

This doesn't occur on ViolentMonkey and TamperMonkey, but I did notice the same error on GreaseMonkey.

GM|VM|TM inject into a content script context which shares the page context window. FM injects into an isolated userScript context (different window).

Try accessing the page context window with unsafeWindow and see. It should also work on GM|VM|TM.

Please note that there will be many changes to the userscript environment in MV3.

PS. I have created a dedicated FireMonkey repo.

Sv443 commented 2 days ago

Thanks for the quick response.

Try accessing the page context window with unsafeWindow and see.
It should also work on GM|VM|TM.

I don't understand what you mean by this.
Also I'm on Firefox so MV3 isn't a problem for now.

When I log the value of unsafeWindow.__proto__.addEventListener I can access it, it's just the assignment that fails.
Is this something you just can't do with the isolated context?
If so, do you think we could get an API to interact with the main page context?

erosman commented 2 days ago

I asked Mozilla engineers and this is what they said:

exportFunction must be used to make the function callable by the web page

Sv443 commented 2 days ago

Thanks, didn't know that was a thing. But I just tried to get that to work in many different ways but it either did nothing or threw the same error as before.
I guess it's just gonna have to be this way where calling that function in my library breaks the page on FM and GM.