3

In short instead of alerting URLs and the response body I'd like to send it to my app. This code works but I can not use GM_xmlhttpRequest unless I grant it.

Changing nothing else the code magically breaks. I'm unsure what is changed and how to fix it. I thought I could use console.log and copy/paste the data into my app however Facebook disables console.log.

I thought about doing xmlhttpRequest but that too is somehow blocked. I tested by executing code in a console. The 3 lines seem to work everywhere except on a Facebook domain. I believe it has something to do with CORS.

// ==UserScript==
// @name        FBTest
// @namespace   test
// @include     https://*.facebook.com/*
// @version     1
// @grant       none
// ==/UserScript==
//change none to GM_xmlhttpRequest
(function() {
    var proxied = window.XMLHttpRequest.prototype.open;
    window.XMLHttpRequest.prototype.open = function(method, url) {
        alert(url);
        return proxied.apply(this, [].slice.call(arguments));
    }; 
})();
Brock Adams
  • 90,639
  • 22
  • 233
  • 295
  • Use [`GM_xmlhttpRequest`](https://wiki.greasespot.net/GM_xmlhttpRequest) instead of `window.XMLHttpRequest` – Mottie Nov 16 '16 at 03:36
  • 1
    @Mottie - the code is trying to "intercept" the open function of XMLHttpRequest, not actually use XMLHttpRequest – Jaromanda X Nov 16 '16 at 04:03
  • the issue stems from the fact that any script with @grant *anything other than none* runs in a more secure scope. I originally though that you could then use unsafeWindow instead of window - however, that doesn't work either, as `.open` is then rendered unusable from the page because of the privileged scope that your `open` code runs in. – Jaromanda X Nov 16 '16 at 04:34
  • You'll have more luck doing this using [Add-on SDK](https://developer.mozilla.org/en-US/Add-ons/SDK) or even [WebExtensions](https://developer.mozilla.org/en-US/Add-ons/WebExtensions) – Jaromanda X Nov 16 '16 at 04:51

1 Answers1

6

When you grant GM_xmlhttpRequest, it switches on the sandbox -- which means that you cannot access window.XMLHttpRequest like that as it is in a different context now.

To work around this, use script injection to intercept the AJAX. And, use either messaging or a custom event to access the data in your userscript's context.

Here's an example script using a custom event (less vulnerable to 3rd-party exploits):

// ==UserScript==
// @name        _Intercept AJAX with grant/sandbox on
// @match       https://*.facebook.com/*
// @grant       GM_xmlhttpRequest
// ==/UserScript==

function xmlOpenIntercept () {
    var proxied = window.XMLHttpRequest.prototype.open;
    window.XMLHttpRequest.prototype.open = function (method, newUrl) {
        var cEvnt = new CustomEvent ('newAjaxStart', {'detail': newUrl} );
        document.body.dispatchEvent (cEvnt);

        return proxied.apply (this, [].slice.call (arguments) );
    };
}
addJS_Node (null, null, xmlOpenIntercept);  //-- Injects code


//--- This code listens for the right kind of message.
document.body.addEventListener ("newAjaxStart", receiveAjaxMessage);

function receiveAjaxMessage (zEvent) {
    console.log ("Intercepted AJAX to: ", zEvent.detail);
}

function addJS_Node (text, s_URL, funcToRun, runOnLoad) {
    var D                                   = document;
    var scriptNode                          = D.createElement ('script');
    if (runOnLoad)  scriptNode.addEventListener ("load", runOnLoad);
    scriptNode.type                         = "text/javascript";
    if (text)       scriptNode.textContent  = text;
    if (s_URL)      scriptNode.src          = s_URL;
    if (funcToRun)  scriptNode.textContent  = '(' + funcToRun.toString() + ')()';

    var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
    targ.appendChild (scriptNode);
}
Community
  • 1
  • 1
Brock Adams
  • 90,639
  • 22
  • 233
  • 295