Primary ConcernsAs long as the stash is not within scope, globals such aswindow/global would effectively be sanitized of any risky methods,such as evalThe API feels complete, am I missing anything?Are there any risky aspects to the sever method.Any thoughts with regard to performance or security.I don't think there is any performance implications from maintaining a stash of the extracted methods, as they would exist already, we are just moving them around.is the layout below easy to read and understand, should I document these things in the future or should I just stick to the code?Background InfoScalpel (s5l) is a small class to shuffle properties between objects. Initially developed for some work being done within the Electron framework, where window and globalneeded to be pruned of insecure methods.Scalpel will allow you to remove any methods fromthese global type objects, and then restore them, when/if you want.Here is the condensed version:class S5l /* S{calpe}(5)l*/ {    constructor (){ this.stash = {}; }    sever ( methods, contexts, message, stash ){        if(! Array.isArray(methods)) methods = [methods];        message = message ? message : ' has been disabled for security reasons.';        this.stash = stash ? stash : this.stash;        stash = this.stash;        methods.forEach(function(method){            contexts.forEach(function(tuple){                let label = Object.keys(tuple)[0];                if( Object.prototype.hasOwnProperty.call( tuple[label], method )){                    stash[label] = stash[label] || {};                    stash[label][method] = {                       _f: tuple[label][method], // the function/method itself.                      _r: tuple[label], // a parent reference, where the method was.                      _m: method // the method/property name                    };                    tuple[label][method] = function(){ throw new Error( method + message); };                }            });        });        return this;    }    purge(dotPath){        delete this.stash;        this.stash = {};        return this;    }    _dotToSteps(dotPath){        return dotPath.split('.');    }    _dotWalker(dotPath, context, _c){        var steps = this._dotToSteps(dotPath);        for( var i=0; i < steps.length; i++){            if( Object.prototype.hasOwnProperty.call( context, steps[i] )  ){                _c = context, context = context[steps[i]];                if(i === steps.length -1)                     return { at: steps[i], context: context, parent: _c };            } else {                return undefined;            }        }    }    _retrieve(dotPath, context){        context = context ? context : this.stash;        return dotPath ? this._dotWalker(dotPath, context ) : context;    }    use(dotPath, context){        return this._retrieve(dotPath, context).context._f || this._retrieve(dotPath, context).parent;    }    attach(dotPath, to, as){        var tmp = this._retrieve(dotPath, this.stash);            to[ as || tmp.at ] = tmp.context._f;            return to;    }}Here is the commented version:class S5l /* S{calpe}(5)l*/ {    constructor (){        this.stash = {};    }    /**        methods: a string or array of method names        contexts: an tuple or array of tuples.            tuple: { prop: object }        message (optional): a message used to make the stub.        stash (optional): the context used for the stash.    */    sever ( methods, contexts, message, stash ){        // Ensure we have an array, from which to pull methods from.        if(! Array.isArray(methods)) methods = [methods];        // Prepair or Incorperate a supplied message        message = message ? message : ' has been disabled for security reasons.';        // Swap out stashes, or continue using this.stash        this.stash = stash ? stash : this.stash;        // Make link for use below, ES6, would prevent us from needing this.        stash = this.stash;        // Process all the method.        methods.forEach(function(method){            /* TODO: we should responde to all types,                      but for now we expect all values                     to be objects.             */            /**                We loop through the contexts, the use case                here is that we have multple globals that                need to be stripped of a method(s).            */            contexts.forEach(function(tuple){                let label = Object.keys(tuple)[0];                // We only want to work with existing props/methods                if( Object.prototype.hasOwnProperty.call( tuple[label], method )){                    // Initialize a new sub object, or use existing.                    stash[label] = stash[label] || {};                    // Archive/Stash our source method/prop and info.                    stash[label][method] = {                       _f: tuple[label][method], // the function/method itself.                      _r: tuple[label], // a parent reference, where the method was.                      _m: method // the method/property name                    };                    // Assign stub to original context property/method.                    tuple[label][method] = function(){ throw new Error( method + message); };                }            });        });        return this;    }    /**        Will drop all link/references in the stash,         to allow garbage collection, if applicable.    */    purge(dotPath){        delete this.stash; // to ensure garbage collection        this.stash = {}; // reinitialize store        return this;    }    /**         Converts 'dot.path.string' into an array of successive props    */    _dotToSteps(dotPath){        return dotPath.split('.');    }    /**        Simple traverser, that uses the     */    _dotWalker(dotPath, context, _c){        var steps = this._dotToSteps(dotPath);        for( var i=0; i < steps.length; i++){            if( Object.prototype.hasOwnProperty.call( context, steps[i] )  ){                _c = context, context = context[steps[i]];                if(i === steps.length -1)                     /**                        To be able to restore things, we utilize a tuple literal                        to maintain reference and other vital information.                    */                    return { at: steps[i], context: context, parent: _c };            } else {                /**                    Will return on first occurence of an invalid path.                */                return undefined;            }        }        // Implicit undefined is returned.    }    /**        Get's the context as it was when stashed.        returns undefined if no such context exists within the stash, or it was falsy    */    _retrieve(dotPath, context){        context = context ? context : this.stash;        return dotPath ? this._dotWalker(dotPath, context ) : context;    }    /**        Returns a reference to the method in the Stash, you can't update the stash's        reference here, but you can use it.    */    use(dotPath, context){        return this._retrieve(dotPath, context).context._f || this._retrieve(dotPath, context).parent;    }    /**        Will assign a stashed value to an object/context supplied.    */    attach(dotPath, to, as){        var tmp = this._retrieve(dotPath, this.stash);            to[ as || tmp.at ] = tmp.context._f; //TODO: confirm everything works.            return to;    }}API ExamplesConstruct a new Scalpel Stash object.;    var s = new S5l();Remove methods from an object/context, can accept an array of methods to be removed. Expects an array of tuples { prop: object }, where prop will become the name used within the stash archive for the object/value.When replaced with a value, the initial context will become a stubfuntion which throws an error when called.s.sever(['eval','alert'], [{window: window}]);// window.alert && window.eval have been replaced with // stub functions.Access the original method/prop, uses dotPath notationwindow.alert('hi there');// Error is thrown alert has been disabled for security reasons.s.use('window.alert')('Hi there.');// calls the original alert function as it was initiallyAttach a severed property/method to context, accepts a dotPaths as the first argument.s.attach('window.alert', window);// window.alert, now equals the original alert method.s.attach('window.alert', window, 'scream');// window.scream, now equals the original alert method.Retrieve the stash, or parts of it.s._retrieve(window.alert)// stash.window.alertor get the whole stash s._retrieve()// stash {...}Release the stash, and reinitialize it.s.purge();// stash is now blank, {}There is also a _dotToSteps and _dotWalker methods, these are not intendedfor public use and so are not documented here.
modifying globals, window, global and other objects, in a restorable way
javascript;sandbox