Search code examples
javascriptsecuritygreasemonkeycode-injection

Is a Greasemonkey script that uses unsafeWindow secure as long as the @grant type is 'none'?


I've written a small Greasemonkey script to add a couple of hotkeys to YouTube for controlling the volume when the video doesn't necessarily have focus:

// ==UserScript==
// @name     Custom Youtube Hotkeys
// @version  1
// @include  https://www.youtube.com/watch*
// @grant    none
// ==/UserScript==

var customScript_Player = unsafeWindow.document.getElementById("movie_player");

window.onkeypress = function(event) {
  //ignore keypress if focused on search bar
  if (unsafeWindow.document.activeElement.tagName === "INPUT") { return; }
  try {
    if (event.keyCode == 120) {
      customScript_Player.setVolume(customScript_Player.getVolume() + 5);
    } else if (event.keyCode == 122) {
      customScript_Player.setVolume(customScript_Player.getVolume() - 5);
    }
  } catch(err) { console.log(err.message); }
};

However, I'm concerned about the security concerns raised with the use of unsafeWindow. As far as I can tell, it's necessary to execute JavaScript on the page elements, but I'm seeing conflicting information about what security risks it poses.

I've seen a couple of discussions where it's claimed that the only security holes opened by its use come from the APIs Greasemonkey provides which are blocked using @grant none in the script header, and that as long as grant is set to none the page can't do anything it wouldn't be able to normally.

However, the wiki itself makes several warnings about avoiding unsafeWindow and doesn't seem to ever specify its only a problem when using its own API.

Is the above script secure? If not, is there a safer way to inject javascript that can control the page elements?

Thanks!


Solution

  • I'm concerned about the security concerns raised with the use of unsafeWindow.

    In GreaseMonkey, userscripts run in content script context while webpage JavaScript runs in page context.

    The Xray vision separation is made specially to prevent page JavaScript gaining access to privileged functions.

    unsafeWindow creates a bridge between the 2 contexts which if not careful, can expose some of the content context functions to the page JavaScript.

    Note: In GreaseMokey (& FireMonkey) unsafeWindow is an alias for window.wrappedJSObject. Therefore, there is no difference between using either. TamperMonkey & ViolnetMonkey implementation of unsafeWindow is different.

    In your example code, if you don't need to use any of the GM functions, you can simply inject the whole code into the page context without the need for unsafeWindow e.g.

    const script = document.createElement('script');
    script.textContent = '...code....';
    document.body.appendChild(script);
    script.remove(); // if you want, makes no difference