skip to Main Content

I tried to proxy Function.prototype.

I created a new Proxy and rewrote the toString method, however console.log('cas') doesn’t work, why?

Function.prototype = new Proxy(Function.prototype, {
  get: function(target, property, receiver) {},
  set: function(target, property, value, receiver) {
    console.log('cas')
  }
})
Function.prototype._toString = Function.prototype.toString;
Function.prototype.toString = function() {
  console.log('bb')
  return this._toString()
}

2

Answers


  1. I created a new Proxy and rewrote the toString method, however console.log('cas') doesn’t work, why?

    The answer is simple: Function.prototype is not writable.

    console.config({ maximize: true });
    console.log(Object.getOwnPropertyDescriptor(Function, 'prototype'));
    <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>

    As for why JS didn’t throw an error… It’s JS. What do you expect?

    On the other hand, you can use 'use strict' to make these kinds of error explicit (thanks @Keith).

    To prevent Function#toString() from being overwritten, you can re-define it:

    Object.defineProperty(Function.prototype, 'toString', {
      value: Function.prototype.toString,
      writable: false,
      configurable: false
    });
    

    Try it:

    console.config({ maximize: true });
    
    function f() {}
    
    console.log(f.toString()); // function f() {}
    
    Object.defineProperty(Function.prototype, 'toString', {
      value: Function.prototype.toString,
      writable: false,
      configurable: false
    });
    
    Function.prototype._toString = Function.prototype.toString;
    Function.prototype.toString = function() {
      console.log('overwritten');
      return Function.prototype._toString.call(this);
    }
    
    console.log(Object.hasOwn(Function.prototype, '_toString')); // true
    console.log(Object.hasOwn(Function.prototype, 'toString')); // Also true
    console.log(f.toString()); // function f() {}, not tainted.
    <script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script>
    Login or Signup to reply.
  2. Ok, we cannot overwrite Function.prototype but we can set the prototype of Function.prototype (an evil professor’s laugh here):

    const props = Object.getOwnPropertyDescriptors(Function.prototype);
    
    const masked = {};
    
    for (const [name, value] of Object.entries(props)) {
        Object.defineProperty(masked, name, value);
    }
    
    const prototype = new Proxy({}, {
        get: function (target, property, receiver) {
            // avoid recursion and stack overflow
            if(!['call', 'apply'].includes(property)){
              console.log(`getting ${property}`);
            }
            return masked[property];
        },
        set: function (target, property, value, receiver) {
            console.log(`setting ${property}`);
            masked[property] = value;
        }
    });
    
    // clear Function.prototype and assign our proxy prototype to it
    
    for (const k in props) {
        delete Function.prototype[k];
    }
    
    Object.setPrototypeOf(Function.prototype, prototype);
    
    Function.prototype._toString = Function.prototype.toString;
    Function.prototype.toString = function () {
        console.log("I'm new toString()");
        return this._toString();
    };
    
    const test = () => false;
    
    console.log(test.toString());
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search