Script works in Firefox 19 but fails in Firefox 20 #1715

Open
mjh563 opened this Issue Feb 23, 2013 · 18 comments

7 participants

@mjh563

The following script, when run on http://www.ebay.co.uk , alerts "Success" in Firefox 19 but "Fail" in Firefox 20.

The Error Console shows "Greasemonkey access violation: unsafeWindow cannot call GM_getResourceText."

// ==UserScript==

// @name      Testcase script
// @version   1
// @grant     GM_getResourceText
// @include   http://www.ebay.co.uk*
// @resource  yahoo http://yui.yahooapis.com/2.9.0/build/yahoo/yahoo-min.js

// ==/UserScript==

var counter = 0;

var observer = new MutationObserver(function(mutations)
{
    mutations.forEach(function()
    {
        if (counter++ == 0) //only alert once
        {
            var res = GM_getResourceText("yahoo");
            alert(res ? "Success" : "Fail");
        }
    });
});

observer.observe(document.documentElement, {childList:true, subtree:true});

Doing a Firefox regression check gives:

Last good nightly: 2012-11-22
First bad nightly: 2012-11-23

Pushlog:
http://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=20ec9014f220&tochange=d8e4f06198dc

@arantius
Collaborator

Have you reported this to Mozilla?

@mjh563

No, because I figured that no-one at Mozilla would know what GM_getResourceText was, which is the bit that seems to be broken.

I'm happy to submit a bug report to Mozilla though, if you think that would be helpful.

@arantius
Collaborator

True, it's Greasemonkey specific. But if you can reduce to 1 day's nightly, it's probably worth tracking. Knowing why GM_apiLeakCheck is rejecting the call, and maybe factoring out YUI for standalone JS (it looks like it's just a DOMSubtreeModified listener on document.documentElement), would also help.

@mjh563

Okay, I filed https://bugzilla.mozilla.org/show_bug.cgi?id=844406

Note that the @resource could be anything, it's only there to enable the script to call GM_getResourceText. The contents of the resource are not actually used.

@arantius
Collaborator

Ah! So MutationObserver is a thing https://developer.mozilla.org/en-US/docs/DOM/MutationObserver .

Then this sounds a lot like #1001 ; I really need to check exactly why GM_apiLeakCheck is denying the call in this case.

@alice0775

in this case, the following modification solves the problem.

modules\util\apiLeakCheck.js

function apiLeakCheck(apiName) {
var stack = Components.stack;

do {
// Valid locations for GM API calls are:
// * Greasemonkey scripts.
// * Greasemonkey extension by path.
// * Greasemonkey modules.
// * All of chrome. (In the script update case, chrome will list values.)
// Anything else on the stack and we will reject the API, to make sure that
// the content window (whose path would be e.g. http://...) has no access.
if (2 == stack.language

  • && stack.filename !== "self-hosted" && stack.filename !== gComponentPath && stack.filename.substr(0, gScriptDirPath.length) !== gScriptDirPath && stack.filename.substr(0, 24) !== 'resource://greasemonkey/'
@arantius
Collaborator

https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/self-hosting

The question is: would it be possible for malicious content to construct a call such that code it defines would show up on the call stack as "self-hosted", and otherwise the call stack would appear to be safe? If so, we can't blindly allow it. I don't know the answer to that question.

@Ventero

Assuming GM_xmlhttpRequest leaked to the content scope somehow, something like

['[{method: "GET", url: "http://google.com/", onload: function(){alert("load");}}].forEach(GM_xmlhttpRequest);'].forEach(setTimeout);

run in the page scope generates the following call stack:

resource://greasemonkey/util/apiLeakCheck.js
resource://greasemonkey/xmlhttprequester.js
resource://greasemonkey/util/hitch.js
self-hosted
self-hosted

and thus would execute the XHR when applying @alice0775's patch.

@jerone

I'm using Firefox 20 atm and having problems with running some scripts. In the error log I got the following error messages:

Timestamp: 28-2-2013 21:19:45
Error: Greasemonkey access violation: unsafeWindow cannot call GM_getValue.
Timestamp: 28-2-2013 21:22:46
Error: Greasemonkey access violation: unsafeWindow cannot call GM_xmlhttpRequest.
@arantius
Collaborator

As I feared, but thanks for the confirmation Ventero. Of course, .filename is not the only property on the stack.

Unfortunately, the rest of the properties aren't helpful. Expanded logging gives:

language        2
languageName    JavaScript
filename        resource://greasemonkey/util/apiLeakCheck.js
name    apiLeakCheck
lineNumber      23
sourceLine      null

language        2
languageName    JavaScript
filename        chrome://greasemonkey/content/miscapis.js
name    GM_ScriptStorage.prototype.getValue
lineNumber      24
sourceLine      null

language        2
languageName    JavaScript
filename        resource://greasemonkey/util/hitch.js
name    null
lineNumber      27
sourceLine      null

language        2
languageName    JavaScript
filename        self-hosted
name    forEach
lineNumber      323
sourceLine      null

None of those properties help us identify that "self-hosted" in this case came from content.

@arantius
Collaborator

P.S. My case which also seems to work follows. Intentionally broken script (but lots of scripts do things like this unintentionally):

// ==UserScript==
// @name        API Leaker
// @namespace   http://github.com/arantius
// @description Leaks GM API to content scope
// @include     http*
// @version     1
// @grant       GM_getValue
// @run-at      document-start
// ==/UserScript==

dump('exporting GM_getValue\n');
unsafeWindow.GM_getValue = GM_getValue;

And content code:

["test"].forEach(GM_getValue);

Produces the above trace.

@Ventero

Note that without modifying GM_util.apiLeakCheck, you won't see the full stack trace, as it aborts once it reaches the first self-hosted stack entry.

With your test script, I get an additional entry:

language        2
languageName    JavaScript
filename        https://ventero.de/temp/gmleak.html
name            null
lineNumber      8
sourceLine      null

Though this doesn't make a difference, as an additional call to setTimeout from within a self-hosted function replaces this stack entry.

@arantius
Collaborator

Quite right! I actually see two more:

language        2
languageName    JavaScript
filename        http://localhost/infinite.php?x=22
name    null
lineNumber      25
sourceLine      null

language        0
languageName    C++
filename        null
name    null
lineNumber      0
sourceLine      null

But as you say, the extra forEach wrapper:

['["test"].forEach(GM_getValue);'].forEach(setTimeout);

Makes it another self-hosted frame, content nowhere to be seen.

@jerone

Any progress on this?

Most of my scripts don't work anymore when used with GM_[gs]etValue & GM_xmlhttpRequest.
Really considering 'downgrading' to the stable channel :)

@arantius
Collaborator

See the bugzilla thread; fix is happening upstream.

@jerone

Firefox 20.0b6 seems to have this fixed.

@revolter

Same error after upgrading to Firefox 30

@janekptacijarabaci

Not confirmed on:
Firefox 31.0, Greasemonkey 2.2beta1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment