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:
- Greasemonkey 4[2]
- FireMonkey[3]
Related issues
- https://github.com/violentmonkey/violentmonkey/issues/107
- https://github.com/violentmonkey/violentmonkey/issues/172
- https://github.com/violentmonkey/violentmonkey/issues/173
- https://github.com/violentmonkey/violentmonkey/issues/408
- https://github.com/violentmonkey/violentmonkey/issues/487
- https://github.com/violentmonkey/violentmonkey/issues/604
Environment
- Browser: Firefox v76.0.1
- Violentmonkey: v2.12.7
- OS: Linux (Arch)
Footnotes
- “Scripts requiring access to JavaScript objects in the web page will not work in [
@inject-into content
] mode.” - “GM4 does not yet support
@grant none
.” @grant none
isn’t supported.unsafeWindow
is but I couldn’t get theXHR#open
hook to work.
About this issue
- Original URL
- State: open
- Created 4 years ago
- Reactions: 6
- Comments: 17 (8 by maintainers)
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 incontent
for reliability on a hostile site. When MV3 implements userscripts API we’ll only allowpage
anduserscript
(or another name) worlds andcontent
will be equal touserscript
.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.
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.