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

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 @grant none, which is needed to modify page objects.

Userscript engines this works in:

  • Tampermonkey (tested on Firefox)

Userscript engines this doesn’t work in:

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/*
// @grant         none
// @inject-into   content
// ==/UserScript==

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

// or unsafeWindow...
window.XMLHttpRequest.prototype.open = hookXHROpen(
    window.XMLHttpRequest.prototype.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.

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: closed
  • Created 4 years ago
  • Reactions: 2
  • Comments: 17 (5 by maintainers)

Commits related to this issue

Most upvoted comments

@chocolateboy How to determine that your script is working properly?

As for the example you’ve given, I don’t see any difference between that and assigning directly to console.log, e.g.:

unsafeWindow.wrappedJSObject.console.log = func

Try execute console.log("test") in the web console. Without exporting the function, an error will occur.

Don’t forget to use exportFunction or cloneInto with cloneFunctions:true option.

AFAIK Tampermonkey rewrites the CSP response header to allow its script elements. We could do it as well but it seems quite radical, I’m not even sure Mozilla will allow it in the future, they dislike addons rewriting CSP last time I heard.

unsafeWindow.wrappedJSObject works for me in Violentmonkey:

// ==UserScript==
// @name        test wrappedJSObject
// @match       https://www.google.com/*
// @grant       GM_foo
// @run-at      document-start
// @inject-into content
// ==/UserScript==

const pageWnd = unsafeWindow.wrappedJSObject;
const p = pageWnd.XMLHttpRequest.prototype;
const {open} = p;
const xhrOpen = function (method, url) {
  console.warn('intercepted', url);
  return open.apply(this, arguments);
}
p.open = exportFunction(xhrOpen, pageWnd);