violentmonkey: [Firefox] Can't modify page properties on sites which use CSP

Re-opening this as a new issue as the last one got derailed…

This is my abiding issue with Violentmonkey (which I ❤️ - thank you!), but I can’t see an open issue for it. There are related closed issues, and this issue may point the way to a fix, but I thought it’d be better to track the bug explicitly rather than inferring it from the documentation[1] and scattered comments.

What is the problem?

It’s not possible to modify/mutate direct or nested properties of a page’s window object with the following combination:

  • Violentmonkey for Firefox
  • sites which use CSP (e.g. GitHub, Google, Twitter)

Sites which use CSP don’t run in Violentmonkey for Firefox unless @inject-into content is enabled, but @inject-into content is not compatible with unsafeWindow (or @grant none), which is needed to modify page objects.

There is a workaround for this on Firefox, but its use is not always obvious, and it requires browser/engine-specific code, which userscripts are meant to eliminate.

How to reproduce it?

// ==UserScript==
// @name          Hook XHR#open
// @version       0.0.1
// @include       https://twitter.com/*
// @include       https://github.com/*
// @include       https://*.google.tld/*
// @inject-into   content
// ==/UserScript==

const xhrProto = unsafeWindow.XMLHttpRequest.prototype

function hookXHROpen (oldOpen) {
    return function open () {
        console.warn('inside XHR#open')
        return oldOpen.apply(this, arguments)
    }
}

xhrProto.open = hookXHROpen(xhrProto.open)

What is the expected result?

XHR#open should be hooked and the message should be logged on those sites.

What is the actual result?

XHR#open isn’t hooked and the message isn’t logged.

Compatibility

Userscript engines this works in:

  • Tampermonkey (tested on Firefox)

Userscript engines this doesn’t work in:

Related issues

Environment

  • Browser: Firefox v76.0.1
  • Violentmonkey: v2.12.7
  • OS: Linux (Arch)

Footnotes

  1. “Scripts requiring access to JavaScript objects in the web page will not work in [@inject-into content] mode.”
  2. “GM4 does not yet support @grant none.”
  3. @grant none isn’t supported. unsafeWindow is but I couldn’t get the XHR#open hook to work.

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 6
  • Comments: 17 (8 by maintainers)

Most upvoted comments

It is also worth noting that the hack with removing the CSP can cause a conflict with other addons that use CSP to block content (like Ublock Origin).

CSP header is present only on the initial page response (the one that creates the main document or an iframe). It’s not scoped to a script. It’s global to the page/iframe environment. It defines what the environment can or cannot do. Removing the header on all URLs is the same as disabling CSP in about:config. The only advantage of doing it in Violentmonkey is that we can remove the header only if the site has active userscripts. Admittedly, this advantage is worth adding the workaround, but on the other hand this workaround is so lame that I’m reluctant to add it.

P.S. only Firefox devs can fix https://bugzil.la/1267027 so there is no solution to this problem that we can implement, only workarounds.

We should keep content mode because the option may be disabled or the script may want to run in content for reliability on a hostile site. When MV3 implements userscripts API we’ll only allow page and userscript (or another name) worlds and content will be equal to userscript.

Tampermonkey removes the CSP header and is allowed in AMO, so I guess it’s okay as long as it’s optional and user-controlled.

On the other hand, the same may be achieved by any user by setting security.csp.enable to false in about:config

Does the header solution disable CSP for all requests to an affected site? Or is it somehow scoped/sandboxed to a particular userscript?

If the former, it sounds like an insecure and dangerous workaround rather than a solution.

Ditto, of course, the user disabling it globally.