Is there a way to check if a native Javascript function was monkey patched?
Asked Answered
K

5

6

For example I loaded a script on some website, and I would like to know if JSON.parse/stringify wasn't monkey patched.

I noticed that if I use toString on the function in Chrome/FF, JSON.stringify.toString, then I get back:

function stringify() {
    [native code]
}

My question is do you think this is a good way to verify if a function was monkey patched? Also would love to hear of any other approaches to this problem.

Khoury answered 25/7, 2016 at 8:15 Comment(2)
Maybe you can run your own script in a WebWorker and compare the function from within the worker to the function from the main page.Mccartan
@Mccartan how can you do this? If I try to do myWorker.postMessage(Function.prototype.toString); it throws an error.Damon
P
6

Yes, this is the only practical way to check whether or not a native function had been overridden or not.

const isNative = fn => !!fn.toString().match(/\[native code\]/)

console.log(isNative(JSON.stringify));

A more robust solution could use Function.prototype.toString() instead of direct call of fn.toString(), but both are monkeypatchable as well. The joys of JavaScript :)

Potation answered 25/7, 2016 at 8:17 Comment(0)
T
6

One could easily fake JSON.stringify.toString

JSON.stringify = function() {}
JSON.stringify.toString = function() {return 'ha-ha'}

console.log(JSON.stringify); //ha-ha

A little more robust way would be to use Function.prototype.toString

Function.prototype.toString.call(JSON.stringify)

But really bad monkeypatcher could patch Function.prototype.toString as well :)

Thirtyone answered 25/7, 2016 at 8:21 Comment(0)
M
5

The spec ( http://www.ecma-international.org/ecma-262/7.0/index.html#sec-function.prototype.tostring ) does not specify the exact string returned for a builtin function :

19.2.3.5 Function.prototype.toString

When the toString method is called on an object func, the following steps are taken:

If func is a Bound Function exotic object, then Return an implementation-dependent String source code representation of func. The representation must conform to the rules below. It is implementation dependent whether the representation includes bound function information or information about the target function. If Type(func) is Object and is either a built-in function object or has an [[ECMAScriptCode]] internal slot, then Return an implementation-dependent String source code representation of func. The representation must conform to the rules below. Throw a TypeError exception. toString Representation Requirements:

The string representation must have the syntax of a FunctionDeclaration, FunctionExpression, GeneratorDeclaration, GeneratorExpression, ClassDeclaration, ClassExpression, ArrowFunction, MethodDefinition, or GeneratorMethod depending upon the actual characteristics of the object. The use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent. If the object was defined using ECMAScript code and the returned string representation is not in the form of a MethodDefinition or GeneratorMethod then the representation must be such that if the string is evaluated, using eval in a lexical context that is equivalent to the lexical context used to create the original object, it will result in a new functionally equivalent object. In that case the returned source code must not mention freely any variables that were not mentioned freely by the original function's source code, even if these “extra” names were originally in scope. If the implementation cannot produce a source code string that meets these criteria then it must return a string for which eval will throw a SyntaxError exception.

So checking for [Native Code] may or may not work depending on the interpreter. Furthermore, an implementation could well implement builtin functions as normal javascript code.

So in answer to your question, you cannot determine, is a Javascript specified way whether a builtin function has been monkey-patched.

That said it appears that Chrome and Firefox both return the [Native Code] string subject to verification on other implementations that may be a pragmatic solution.

Mutilate answered 25/7, 2016 at 8:51 Comment(1)
"Furthermore, an implementation could well implement builtin functions as normal javascript code." This is probably not an issue. V8's Promise is implemented in JavaScript and Promise.toString() still has '[native code]' in it.Butterfingers
A
3

I just wanted to add that, after ES6, all solutions that involve checking "[native code]" are even less reliable because of ES6 proxy traps.

// Example monkey-patching the Fetch API using an ES6 proxy trap
window.fetch = new Proxy(window.fetch, {
    apply(fetch, that, args) {
        const result = fetch.apply(that, args);
        result.then((response) => {
            console.log("Intercepted!", args, response);
        });
        return result;
    }
});

// True
console.log(window.fetch.toString().includes("[native code]"));

// True 
console.log(Function.prototype.toString.call(window.fetch).includes("[native code]"));

For more info, check this answer.

Anchises answered 23/7, 2022 at 15:19 Comment(0)
M
0

I tried to develop some of the ideas from other replies into a working script - here it is:

https://gist.github.com/mindplay-dk/767a5313b0052d6daf2b135fdecd775f

Paste it into the Chrome (or Edge) console and press ENTER - it'll print out a list of any constructors and class-methods not matching their native counterparts. (It does this by comparing against the native APIs in an iframe - which it creates via document.createElement, so, technically, it's possible to fool it by overriding that method, if you were intending to do so deliberately; this isn't a security tool.)

Note that this currently gives false positives for window.location, window.fetch and window.length - this appears to be because these properties aren't correctly reflected by their native browser implementations? If you know how to fix it, please post a comment.

Here is example output from a site that was incorrectly loading some IE11 polyfills into Chrome:

enter image description here

Mannes answered 7/9, 2020 at 13:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.