Why isNative?
As I though and as Diego Perini confirmed, sometimes we would like to trust the environment itself before we act in a certain way.
I wrote about an XMLHttpRequest wrapper for Internet Explorer, but cases are numerous, like a "not compliant" implementation of the Array.prototype.forEach, or others proto, or some injection problems about window.Array, window.String, or who knows what else.
Accordingly, since the JavaScript nature is dynamic, widely dynamic, we would like to be sure that a method is as "specs says" rather than how this or that library implemented it.
Can we trust an unreliable environment?
The first error we all committed was to find the "best way ever" to understand if a function/method has been changed, without considering the fact that the function isNative itself could be a fake!
No matter how good is our way to discover the native code because isNative(this, "isNative"); will ironically return false every time.
A way to partially solve the problem is the usage of const but this keyword makes sense only for FireFox and Safari, so Opera and even worse case Internet Explorer will not be able to create an immutable function.
/*@cc_on var // @*/ const
isNative = function(){
// whatever You need
};
Above snippet, described in one of my old posts, will let us create an immutable function only for certain browsers, but the isNative problem, as I said, will persist for IE, Opera, and probably other browsers as well.
About proposed solutions, plus mine
Both kangax and Diego have written a couple of functions, the first one created a sandbox via iframe while the second one a string sniff.
Both solutions are not that perfect because of iframes or because a function toString method coul be changed arbitrary, since functions are objects as well.
My solution has probably some side effect as well, but unless smebody will be able to demonstrate that it could be hacked, which was for Diego case, I will consider my proposal the most reliable without iframes.
function isNative(object, method){
var r = !!object && !/undefined|string/.test(typeof object[method]);
if(r){
var toString = (method = object[method]).toString;
try{ // necessary for native functions (alert, as example)
delete method.toString;
r = method.toString !== Object.prototype.toString && /\[.+?\]/.test(method);
if(method.toString != toString)
method.toString = toString;
}catch(e){
r = true;
}
};
return r;
};
Come on, break it and demonstrate that as I said, JavaScript is 100% unreliable programming language ;-)
P.S. this discussion remind me those days me and kentaromiura were trying to find a way to solve eval injections, we gave up after a week of tries hacking our code by our own :D
Ok :-
ReplyDeleteisNative = function() { return true; }
isNative(isNative);
lol it's pointless.
or without cheating:-
RegExp.prototype.test= function() { return arguments[0]=='function'?false:dfsdfsdf; }
alert(isNative({x:function(){}}, 'x'));
Exactly wht I was trying to say in this post Garet, even using the trick
ReplyDelete/*@cc_on var // @*/ const
isNative = function(){
// my code
};
you will always be able to change the function in Internet Explorer and Opera ;-)
And if we have a closuer, we could redefine the closure, or the entire library or the method which rely on isNative function.
This is my point, we have partial constants and no way to define something readOnly in every browser.
Regards
I'm still sure that the solution rely on make eval call himself causing outofstack error.
ReplyDeleteAt that time I wrote a proof of concept using the Function technique we adopted to rewrite eval, so if anyone wants to rewrite eval they had to call eval (or Function that is eval behind the scenes) and this is not allowed since it goes in out-of-the-stack never-ever-ending recursive call... seems stupid and need some work thought since there are different techniques to eval javascript (setTimeout examples) but in my opinion is the only viable compromise (it work only if you are in your sandbox, else it fail badly)
Ps. As soon I have a bit of spare time I will rewrote that proof of concept just for the sake ;)
I will post here updates :p
Andrea,
ReplyDeletewe agree on the fact that any skilled enough "badguy" will be able to fold any javascript test we put in place.
The point was finding a method good enough to detect a built-in method was overwritten by another piece of code or another framework/library.
I use this to provide alternative ways of solving a problem, so even if the test fails the application will still be able to return valid results.
As you already said using IFRAMEs in an SSL environment is quite dangerous because dialog boxes will pop-up warning users the underlying protocol is going to change.
Another point against IFRAMEs used for feature testing, probably worst than the above, is that we assume that the functionality of methods in a chrome window is the same we get in an IFRAME which resumes a chromeless window. While working with the "doScroll()" trick I realized that this assumption is quite wrong.
About "delete"...in my FF3.06 deleting host objects have no effects, deleting native properties nullifies them (null), while deleting user created properties effectively deletes them ("undefined").
If you copied the snippet from Ajaxian be aware that some escaped characters were munged from the RegExp" during cut & paste.
I posted a corrected patch to cover the signalled extra edge cases.
Diego
Diego my snippet is from the scratch, but would be nice to know where you put your last version :D
ReplyDeleteAndrea,
ReplyDeleteyou are rigth, didn't tell were I posted...it is on the Ajaxian article.
The real application of this was in my selector engine project which is published here:
http://github.com/dperini/nwevents/
it is in the "src" directory, in the "nwmatcher.js" file. Have a look, comments about other FT in there are appreciated.
Have the best,
Diego
OK, I just rewrote the test suite long time ago we used to crack our code ;)
ReplyDelete...
I come to the conclusion that even if my method have his logic, the ability to do caching of the function in javascript made all my efford a "windmill war" XD.
just to remember our conclusion:
javascript is so powerful in the way it leave you modify its behaviour that is totally unreliable.
Andrea, what could you say about this implementation?
ReplyDeleteisNativeFunction = function(funktion) {
var originalDecompiler, result;
originalDecompiler = funktion.toString;
delete funktion.toString;
try {
new Function('var a = ' + funktion + ';');
result = false;
} catch(e) {
result = true;
}
funktion.toString = originalDecompiler;
return result;
};