29

I am trying to iterate over all the globals defined in a website, but in doing so I am also getting the native browser functions.

var numf=0; var nump=0; var numo=0; 
for(var p in this) { 
    if(typeof(this[p]) === "function"){
        numf+=1;
        console.log(p+"()");
    } else if(typeof p != 'undefined'){
        nump+=1;
        console.log(p);
    } else { 
        numo+=1;
        console.log(p);
    }
}

Is there a way to determine if a function is native to the browser or created in a script?

asawilliams
  • 2,908
  • 2
  • 30
  • 54

7 Answers7

24

You can call the inherited .toString() function on the methods and check the outcome. Native methods will have a block like [native code].

if( this[p].toString().indexOf('[native code]') > -1 ) {
    // yep, native in the browser
}

Update because a lot of commentators want some clarification and people really have a requirement for such a detection. To make this check really save, we should probably use a line line this:

if( /\{\s+\[native code\]/.test( Function.prototype.toString.call( this[ p ] ) ) ) {
    // yep, native
}

Now we're using the .toString method from the prototype of Function which makes it very unlikely if not impossible some other script has overwritten the toString method. Secondly we're checking with a regular expression so we can't get fooled by comments within the function body.

jAndy
  • 231,737
  • 57
  • 305
  • 359
  • 1
    This would only work if toString had not been overridden, no? – joekarl Jul 06 '11 at 15:37
  • 3
    @jAndy Is this foolproof? I thought `toString` doesn't work in all modern browsers or something. – Lime Jul 21 '11 at 17:43
  • 5
    @joekit if `toString` is overridden you should be able to do `Function.prototype.toString.call(obj).indexOf('[native code]');` Also it would probably be a better idea to use RegExp. Try calling the function against itself, and it would come across as **native** because it appears in the string. – Lime Jul 21 '11 at 17:46
  • 1
    When `bind` is used to bind a method to certain context, the resulting method is though not native but your check would say its native. `window.alert = function () {}; window.alert = window.alert.bind()` – Hariom Balhara Jul 16 '14 at 07:57
  • 3
    Note this isn't the perfect solution. Checkout https://gist.github.com/jdalton/5e34d890105aca44399f – stevemao Sep 02 '14 at 02:34
  • So if I create a function `checkNative` using jAndy's code, it will return `true` on itself. – Tomáš Zato Oct 22 '15 at 23:13
  • 1
    What if `function () { /* [native code] */ console.log("I'm not native."); }`? – tsh Oct 06 '16 at 08:12
  • 1
    What if `Function.prototype.toString` is overridden? Your solution may not work. Also, your solution will determine the following function as native: `function () { return; /* { [native code] } */ }`. You should change the regex to `/\{ \[native code\] \}$/`. – Slavik Meltser Jul 20 '20 at 14:04
14
function isFuncNative(f) {
       return !!f && (typeof f).toLowerCase() == 'function' 
       && (f === Function.prototype 
       || /^\s*function\s*(\b[a-z$_][a-z0-9$_]*\b)*\s*\((|([a-z$_][a-z0-9$_]*)(\s*,[a-z$_][a-z0-9$_]*)*)\)\s*{\s*\[native code\]\s*}\s*$/i.test(String(f)));
}

this should be good enough. this function does the following tests:

  1. null or undefined;
  2. the param is actually a function;
  3. the param is Function.prototype itself (this is a special case, where Function.prototype.toString gives function Empty(){})
  4. the function body is exactly function <valid_function_name> (<valid_param_list>) { [native code] }

the regex is a little bit complicated, but it actually runs pretty decently fast in chrome on my 4GB lenovo laptop (duo core):

var n = (new Date).getTime(); 
for (var i = 0; i < 1000000; i++) {
    i%2 ? isFuncNative(isFuncNative) : 
          isFuncNative(document.getElementById);
}; 
(new Date).getTime() - n;

3023ms. so the function takes somewhere around 3 micro-sec to run once all is JIT'ed.

It works in all browsers. Previously, I used Function.prototype.toString.call, this crashes IE, since in IE, the DOM element methods and window methods are NOT functions, but objects, and they don't have toString method. String constructor solves the problem elegantly.

Yong
  • 141
  • 1
  • 4
3

Function.prototype.toString can be spoofed, something kinda like this:

Function.prototype.toString = (function(_toString){
  return function() {
    if (shouldSpoof) return 'function() { [native code] }'
    return _toString.apply(this, arguments)
  }
})(Function.prototype.toString)

You can detect if Function.prototype.toString is vandalized by trapping .apply(), .call(), .bind() (and others).

And if it was, you can grab a "clean" version of Function.prototype.toString from a newly injected IFRAME.

cvazac
  • 881
  • 6
  • 7
  • before initializing the script (if working with your own script): For method blocking `Object.defineDescriptor(Function.prototype,'toString',{configurable:false,writable:false});` or `let toString=Function.prototype.toString; .... toString.call(func);` or toString method checking `Function.prototype.toString.call(function empty(){})==='function empty(){}'` – AlexeyP0708 Dec 31 '20 at 16:07
2

2022 answer

Now that we have the Proxy API, there's no fail proof way to determine if a native function was overridden or not.

For example:

function isNativeFunction(f) {
  return f.toString().includes("[native code]");
}

window.fetch = new Proxy(window.fetch, {
  apply: function (target, thisArg, argumentsList) {
    console.log("Fetch call intercepted:", ...argumentsList);
    Reflect.apply(...arguments);
  },
});

window.fetch.toString(); // → "function fetch() { [native code] }"

isNativeFunction(window.fetch); // → true

Following the specs, a proxied object should be indistinguishable from its target. Some runtimes (e.g., Node.js) offers some utilities to go against the spec and check if an object is proxied, but in the browser the only way to do so is by monkey patching the Proxy API itself before any proxy is applied.

So, back to the original question — I think the only available option nowadays is to hold a reference of the “clean” native function and, later on, compare your potentially monkey patched function with it:

<html>
  <head>
    <script>
      // Store a reference of the original "clean" native function before any
      // other script has a chance to modify it.
      // In this case, we're just holding a reference of the original fetch API
      // and hide it behind a closure. If you don't know in advance what API
      // you'll want to check, you might need to store a reference to multiple
      // `window` objects.
      (function () {
        const { fetch: originalFetch } = window;
        window.__isFetchMonkeyPatched = function () {
          return window.fetch !== originalFetch;
        };
      })();
      // From now on, you can check if the fetch API has been monkey patched
      // by invoking window.__isFetchMonkeyPatched().
      //
      // Example:
      window.fetch = new Proxy(window.fetch, {
        apply: function (target, thisArg, argumentsList) {
          console.log("Fetch call intercepted:", ...argumentsList);
          Reflect.apply(...arguments);
        },
      });
      window.__isFetchMonkeyPatched(); // → true
    </script>
  </head>
</html>

By using a strict reference check, we avoid all toString() loopholes. And it even works on proxies because they can’t trap equality comparisons.

The main drawback of this approach is that it can be impractical. It requires storing the original function reference before running any other code in your app (to ensure it’s still untouched), which sometimes you won’t be able to do (e.g., if you’re building a library).

For more info, I recently wrote an article going in-depth into the available approaches for determining if a native function was monkey patched. You can find it here.

Matteo Mazzarolo
  • 4,420
  • 1
  • 16
  • 17
0

For developers who want to use these detection methods to block user behavior (such as userscript), there are actually ways to bypass these methods.

For Example:
(The detection method for the isNative function below comes from methods provided by others in the past.)

function isNative(f) {
    if (!!/bound/.test(f.name)) return 1;
    if (!!!/\{\s+\[native code\]/.test(Function.prototype.toString.call(f))) return 2;
    if (!!!/\{\s+\[native code\]/.test(eval(f) + "")) return 3;
    if ((typeof f).toLowerCase() !== 'function') return 4;
    if (!/^\s*function\s*(\b[a-z$_][a-z0-9$_]*\b)*\s*\((|([a-z$_][a-z0-9$_]*)(\s*,[a-z$_][a-z0-9$_]*)*)\)\s*{\s*\[native code\]\s*}\s*$/i.test(String(f))) return 5;

    return true;
}

function fakeStringify(value) {
    return `Fake Stringify ${value}`;
};

const s = new Proxy(fakeStringify, {
    get(target, prop, receiver) {
        if (prop === "name") {
            return "stringify";
        } else if (prop === Symbol.toPrimitive) {
            return function () {
                return "function () { [native code] }";
            };
        }
    }
});

const obj = {
    a: 1
};
console.log("========= [native] =========");
console.log(isNative(JSON.stringify));
console.log(JSON.stringify(obj));
console.log(JSON.stringify.name);

JSON.stringify = s;

console.log("======== [override] ========");
console.log(isNative(JSON.stringify));
console.log(JSON.stringify(obj));
console.log(JSON.stringify.name);

After execution, it can be found that the isNative detection can be successfully deceived after passing through the Proxy.

As far as I know, there is no way to detect Proxy fucntion in the browser environment. But if you know how to detect it, please provide it, thanks!

LianSheng
  • 448
  • 5
  • 17
-1

I tried a different approach. This is only tested for firefox, and chrome.

function isNative(obj){
    //Is there a function?
    //You may throw an exception instead if you want only functions to get in here.

    if(typeof obj === 'function'){
        //Check does this prototype appear as an object?
        //Most natives will not have a prototype of [object Object]
        //If not an [object Object] just skip to true.
        if(Object.prototype.toString.call(obj.prototype) === '[object Object]'){
            //Prototype was an object, but is the function Object?
            //If it's not Object it is not native.
            //This only fails if the Object function is assigned to prototype.constructor, or
            //Object function is assigned to the prototype, but
            //why you wanna do that?
            if(String(obj.prototype.constructor) !== String(Object.prototype.constructor)){
                return false;
            }
        }
    }
    return true;
}

function bla(){}

isNative(bla); //false
isNative(Number); //true
isNative(Object); //true
isNative(Function); //true
isNative(RegExp); //true
Quentin Engles
  • 2,744
  • 1
  • 20
  • 33
  • Returns `false` for `Promise` even though my browser supports promises natively. – mpen Nov 13 '15 at 00:36
  • Yes I don't know why that is. Promise.prototype.constructor does not equal Object.prototype.constructor. I usually don't test if it's native anyway, and look for the `then` method which is how native promises resolve library promises any way. If all else fails then I use `Promise.resolve(nonNative)` to normalize all promises. – Quentin Engles Nov 13 '15 at 23:00
  • @trusktr Interesting. In Firefox `Object.prototype.toString.call(RegExp.prototype)` returns [object RegExp]. I don't know about IE, or Opera. – Quentin Engles Aug 03 '16 at 04:58
  • I downvoted because I was able to easily make it return true on a non-native function: `let f = function() {}; f.prototype = Object.prototype; isNative(f) // true`. – trusktr Aug 09 '16 at 04:50
  • @trusktr Wouldn't you only do that if you were making a constructor? If you're the one making it then wouldn't you know what it is? There is really no perfect solutions to this particular problem. I've known about the '[native code]' check for a long time, but there are situations where that doesn't work as well. I didn't include that in my answer because someone else already posted it. – Quentin Engles Aug 09 '16 at 22:58
  • The constructor could be imported from somewhere and not made by me. I guess there may not really be a fool-proof answer. I wish there was! – trusktr Aug 10 '16 at 19:35
  • If it's a constructor it's not a function. Even though it is. ;) You know polymorphism destroys identifiability, or something like that. But still, are you really going to check if a function is native if you know it's a constructor? – Quentin Engles Aug 11 '16 at 06:37
-1

almost all of these will fail, because:

function notNative(){}
notNative.toString = String.bind(0, "function notNative() { [native code] }");
console.log( notNative.toString() );

instead:

Function.prototype.isNative = function(){
return Function.prototype.toString.call(this).slice(-14, -3) === "native code";
};

console.log(alert.isNative());
console.log(String.isNative());
function foo(){}
console.log(foo.isNative());
Jack G
  • 4,553
  • 2
  • 41
  • 50
adddff
  • 27
  • 1
  • 7