VYPR
High severityNVD Advisory· Published Sep 17, 2021· Updated Aug 3, 2024

Prototype Pollution in mariocasciaro/object-path

CVE-2021-3805

Description

object-path is vulnerable to Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
object-pathnpm
< 0.11.80.11.8

Affected products

1

Patches

1
4f0903fd7c83

Fix prototype pollution vulnerability

https://github.com/mariocasciaro/object-pathMario CasciaroSep 16, 2021via ghsa
4 files changed · +441 149
  • index.js+148 134 modified
    @@ -1,87 +1,88 @@
    -(function (root, factory){
    -  'use strict';
    +(function (root, factory) {
    +  'use strict'
     
       /*istanbul ignore next:cant test*/
       if (typeof module === 'object' && typeof module.exports === 'object') {
    -    module.exports = factory();
    +    module.exports = factory()
       } else if (typeof define === 'function' && define.amd) {
         // AMD. Register as an anonymous module.
    -    define([], factory);
    +    define([], factory)
       } else {
         // Browser globals
    -    root.objectPath = factory();
    +    root.objectPath = factory()
       }
    -})(this, function(){
    -  'use strict';
    +})(this, function () {
    +  'use strict'
     
    -  var toStr = Object.prototype.toString;
    -  function hasOwnProperty(obj, prop) {
    -    if(obj == null) {
    +  var toStr = Object.prototype.toString
    +
    +  function hasOwnProperty (obj, prop) {
    +    if (obj == null) {
           return false
         }
         //to handle objects with null prototypes (too edge case?)
         return Object.prototype.hasOwnProperty.call(obj, prop)
       }
     
    -  function isEmpty(value){
    +  function isEmpty (value) {
         if (!value) {
    -      return true;
    +      return true
         }
         if (isArray(value) && value.length === 0) {
    -        return true;
    +      return true
         } else if (typeof value !== 'string') {
    -        for (var i in value) {
    -            if (hasOwnProperty(value, i)) {
    -                return false;
    -            }
    +      for (var i in value) {
    +        if (hasOwnProperty(value, i)) {
    +          return false
             }
    -        return true;
    +      }
    +      return true
         }
    -    return false;
    +    return false
       }
     
    -  function toString(type){
    -    return toStr.call(type);
    +  function toString (type) {
    +    return toStr.call(type)
       }
     
    -  function isObject(obj){
    -    return typeof obj === 'object' && toString(obj) === "[object Object]";
    +  function isObject (obj) {
    +    return typeof obj === 'object' && toString(obj) === '[object Object]'
       }
     
    -  var isArray = Array.isArray || function(obj){
    +  var isArray = Array.isArray || function (obj) {
         /*istanbul ignore next:cant test*/
    -    return toStr.call(obj) === '[object Array]';
    +    return toStr.call(obj) === '[object Array]'
       }
     
    -  function isBoolean(obj){
    -    return typeof obj === 'boolean' || toString(obj) === '[object Boolean]';
    +  function isBoolean (obj) {
    +    return typeof obj === 'boolean' || toString(obj) === '[object Boolean]'
       }
     
    -  function getKey(key){
    -    var intKey = parseInt(key);
    +  function getKey (key) {
    +    var intKey = parseInt(key)
         if (intKey.toString() === key) {
    -      return intKey;
    +      return intKey
         }
    -    return key;
    +    return key
       }
     
    -  function factory(options) {
    +  function factory (options) {
         options = options || {}
     
    -    var objectPath = function(obj) {
    -      return Object.keys(objectPath).reduce(function(proxy, prop) {
    -        if(prop === 'create') {
    -          return proxy;
    +    var objectPath = function (obj) {
    +      return Object.keys(objectPath).reduce(function (proxy, prop) {
    +        if (prop === 'create') {
    +          return proxy
             }
     
             /*istanbul ignore else*/
             if (typeof objectPath[prop] === 'function') {
    -          proxy[prop] = objectPath[prop].bind(objectPath, obj);
    +          proxy[prop] = objectPath[prop].bind(objectPath, obj)
             }
     
    -        return proxy;
    -      }, {});
    -    };
    +        return proxy
    +      }, {})
    +    }
     
         var hasShallowProperty
         if (options.includeInheritedProps) {
    @@ -94,213 +95,226 @@
           }
         }
     
    -    function getShallowProperty(obj, prop) {
    +    function getShallowProperty (obj, prop) {
           if (hasShallowProperty(obj, prop)) {
    -        return obj[prop];
    +        return obj[prop]
    +      }
    +    }
    +
    +    var getShallowPropertySafely
    +    if (options.includeInheritedProps) {
    +      getShallowPropertySafely = function (obj, currentPath) {
    +        if (typeof currentPath !== 'string' && typeof currentPath !== 'number') {
    +          currentPath = String(currentPath)
    +        }
    +        var currentValue = getShallowProperty(obj, currentPath)
    +        if (currentPath === '__proto__' || currentPath === 'prototype' ||
    +          (currentPath === 'constructor' && typeof currentValue === 'function')) {
    +          throw new Error('For security reasons, object\'s magic properties cannot be set')
    +        }
    +        return currentValue
    +      }
    +    } else {
    +      getShallowPropertySafely = function (obj, currentPath) {
    +        return getShallowProperty(obj, currentPath)
           }
         }
     
    -    function set(obj, path, value, doNotReplace){
    +    function set (obj, path, value, doNotReplace) {
           if (typeof path === 'number') {
    -        path = [path];
    +        path = [path]
           }
           if (!path || path.length === 0) {
    -        return obj;
    +        return obj
           }
           if (typeof path === 'string') {
    -        return set(obj, path.split('.').map(getKey), value, doNotReplace);
    -      }
    -      var currentPath = path[0];
    -      if (typeof currentPath !== 'string' && typeof currentPath !== 'number') {
    -        currentPath = String(currentPath)
    -      }
    -      var currentValue = getShallowProperty(obj, currentPath);
    -      if (options.includeInheritedProps && (currentPath === '__proto__' ||
    -        (currentPath === 'constructor' && typeof currentValue === 'function'))) {
    -        throw new Error('For security reasons, object\'s magic properties cannot be set')
    +        return set(obj, path.split('.').map(getKey), value, doNotReplace)
           }
    +      var currentPath = path[0]
    +      var currentValue = getShallowPropertySafely(obj, currentPath)
           if (path.length === 1) {
             if (currentValue === void 0 || !doNotReplace) {
    -          obj[currentPath] = value;
    +          obj[currentPath] = value
             }
    -        return currentValue;
    +        return currentValue
           }
     
           if (currentValue === void 0) {
             //check if we assume an array
    -        if(typeof path[1] === 'number') {
    -          obj[currentPath] = [];
    +        if (typeof path[1] === 'number') {
    +          obj[currentPath] = []
             } else {
    -          obj[currentPath] = {};
    +          obj[currentPath] = {}
             }
           }
     
    -      return set(obj[currentPath], path.slice(1), value, doNotReplace);
    +      return set(obj[currentPath], path.slice(1), value, doNotReplace)
         }
     
         objectPath.has = function (obj, path) {
           if (typeof path === 'number') {
    -        path = [path];
    +        path = [path]
           } else if (typeof path === 'string') {
    -        path = path.split('.');
    +        path = path.split('.')
           }
     
           if (!path || path.length === 0) {
    -        return !!obj;
    +        return !!obj
           }
     
           for (var i = 0; i < path.length; i++) {
    -        var j = getKey(path[i]);
    +        var j = getKey(path[i])
     
    -        if((typeof j === 'number' && isArray(obj) && j < obj.length) ||
    +        if ((typeof j === 'number' && isArray(obj) && j < obj.length) ||
               (options.includeInheritedProps ? (j in Object(obj)) : hasOwnProperty(obj, j))) {
    -          obj = obj[j];
    +          obj = obj[j]
             } else {
    -          return false;
    +          return false
             }
           }
     
    -      return true;
    -    };
    +      return true
    +    }
     
    -    objectPath.ensureExists = function (obj, path, value){
    -      return set(obj, path, value, true);
    -    };
    +    objectPath.ensureExists = function (obj, path, value) {
    +      return set(obj, path, value, true)
    +    }
     
    -    objectPath.set = function (obj, path, value, doNotReplace){
    -      return set(obj, path, value, doNotReplace);
    -    };
    +    objectPath.set = function (obj, path, value, doNotReplace) {
    +      return set(obj, path, value, doNotReplace)
    +    }
     
    -    objectPath.insert = function (obj, path, value, at){
    -      var arr = objectPath.get(obj, path);
    -      at = ~~at;
    +    objectPath.insert = function (obj, path, value, at) {
    +      var arr = objectPath.get(obj, path)
    +      at = ~~at
           if (!isArray(arr)) {
    -        arr = [];
    -        objectPath.set(obj, path, arr);
    +        arr = []
    +        objectPath.set(obj, path, arr)
           }
    -      arr.splice(at, 0, value);
    -    };
    +      arr.splice(at, 0, value)
    +    }
     
    -    objectPath.empty = function(obj, path) {
    +    objectPath.empty = function (obj, path) {
           if (isEmpty(path)) {
    -        return void 0;
    +        return void 0
           }
           if (obj == null) {
    -        return void 0;
    +        return void 0
           }
     
    -      var value, i;
    +      var value, i
           if (!(value = objectPath.get(obj, path))) {
    -        return void 0;
    +        return void 0
           }
     
           if (typeof value === 'string') {
    -        return objectPath.set(obj, path, '');
    +        return objectPath.set(obj, path, '')
           } else if (isBoolean(value)) {
    -        return objectPath.set(obj, path, false);
    +        return objectPath.set(obj, path, false)
           } else if (typeof value === 'number') {
    -        return objectPath.set(obj, path, 0);
    +        return objectPath.set(obj, path, 0)
           } else if (isArray(value)) {
    -        value.length = 0;
    +        value.length = 0
           } else if (isObject(value)) {
             for (i in value) {
               if (hasShallowProperty(value, i)) {
    -            delete value[i];
    +            delete value[i]
               }
             }
           } else {
    -        return objectPath.set(obj, path, null);
    +        return objectPath.set(obj, path, null)
           }
    -    };
    +    }
     
    -    objectPath.push = function (obj, path /*, values */){
    -      var arr = objectPath.get(obj, path);
    +    objectPath.push = function (obj, path /*, values */) {
    +      var arr = objectPath.get(obj, path)
           if (!isArray(arr)) {
    -        arr = [];
    -        objectPath.set(obj, path, arr);
    +        arr = []
    +        objectPath.set(obj, path, arr)
           }
     
    -      arr.push.apply(arr, Array.prototype.slice.call(arguments, 2));
    -    };
    +      arr.push.apply(arr, Array.prototype.slice.call(arguments, 2))
    +    }
     
         objectPath.coalesce = function (obj, paths, defaultValue) {
    -      var value;
    +      var value
     
           for (var i = 0, len = paths.length; i < len; i++) {
             if ((value = objectPath.get(obj, paths[i])) !== void 0) {
    -          return value;
    +          return value
             }
           }
     
    -      return defaultValue;
    -    };
    +      return defaultValue
    +    }
     
    -    objectPath.get = function (obj, path, defaultValue){
    +    objectPath.get = function (obj, path, defaultValue) {
           if (typeof path === 'number') {
    -        path = [path];
    +        path = [path]
           }
           if (!path || path.length === 0) {
    -        return obj;
    +        return obj
           }
           if (obj == null) {
    -        return defaultValue;
    +        return defaultValue
           }
           if (typeof path === 'string') {
    -        return objectPath.get(obj, path.split('.'), defaultValue);
    +        return objectPath.get(obj, path.split('.'), defaultValue)
           }
     
    -      var currentPath = getKey(path[0]);
    -      var nextObj = getShallowProperty(obj, currentPath)
    +      var currentPath = getKey(path[0])
    +      var nextObj = getShallowPropertySafely(obj, currentPath)
           if (nextObj === void 0) {
    -        return defaultValue;
    +        return defaultValue
           }
     
           if (path.length === 1) {
    -        return nextObj;
    +        return nextObj
           }
     
    -      return objectPath.get(obj[currentPath], path.slice(1), defaultValue);
    -    };
    +      return objectPath.get(obj[currentPath], path.slice(1), defaultValue)
    +    }
     
    -    objectPath.del = function del(obj, path) {
    +    objectPath.del = function del (obj, path) {
           if (typeof path === 'number') {
    -        path = [path];
    +        path = [path]
           }
     
           if (obj == null) {
    -        return obj;
    +        return obj
           }
     
           if (isEmpty(path)) {
    -        return obj;
    +        return obj
           }
    -      if(typeof path === 'string') {
    -        return objectPath.del(obj, path.split('.'));
    +      if (typeof path === 'string') {
    +        return objectPath.del(obj, path.split('.'))
           }
     
    -      var currentPath = getKey(path[0]);
    +      var currentPath = getKey(path[0])
    +      getShallowPropertySafely(obj, currentPath)
           if (!hasShallowProperty(obj, currentPath)) {
    -        return obj;
    +        return obj
           }
     
    -      if(path.length === 1) {
    +      if (path.length === 1) {
             if (isArray(obj)) {
    -          obj.splice(currentPath, 1);
    +          obj.splice(currentPath, 1)
             } else {
    -          delete obj[currentPath];
    +          delete obj[currentPath]
             }
           } else {
    -        return objectPath.del(obj[currentPath], path.slice(1));
    +        return objectPath.del(obj[currentPath], path.slice(1))
           }
     
    -      return obj;
    +      return obj
         }
     
    -    return objectPath;
    +    return objectPath
       }
     
    -  var mod = factory();
    -  mod.create = factory;
    +  var mod = factory()
    +  mod.create = factory
       mod.withInheritedProps = factory({includeInheritedProps: true})
    -  return mod;
    -});
    +  return mod
    +})
    
  • README.md+6 0 modified
    @@ -13,6 +13,10 @@ Access deep properties using a path
     
     ## Changelog
     
    +### 0.11.8
    +
    +* **SECURITY FIX**. Fix a prototype pollution vulnerability in the `del()`, `empty()`, `push()`, `insert()` functions when using the "inherited props" mode (e.g. when a new `object-path` instance is created with the `includeInheritedProps` option set to `true` or when using the `withInheritedProps` default instance. To help with preventing this type of vulnerability in the client code, also the `get()` function will now throw an exception if an object's magic properties are accessed. The vulnerability does not exist in the default instance exposed by object path (e.g `objectPath.del()`) if using version >= `0.11.0`.
    +
     ### 0.11.6
     
     * **SECURITY FIX**. Fix a circumvention of the security fix released in 0.11.5 when non-string/non-numeric values are used in the path (e.g. `op.withInheritedProps.set({}, [['__proto__'], 'polluted'], true)`)
    @@ -176,6 +180,8 @@ objectPath.withInheritedProps.get(obj, 'notOwn.prop');
     objectPath.set(obj, 'notOwn.prop', 'b');
     ```
     
    +**NOTE**: For security reasons `object-path` will throw an exception when trying to access an object's magic properties (e.g. `__proto__`, `constructor`) when in "inherited props" mode.
    +
     ### Immutability
     
     If you are looking for an *immutable* alternative of this library, you can take a look at: [object-path-immutable](https://github.com/mariocasciaro/object-path-immutable)
    
  • SECURITY.md+7 0 added
    @@ -0,0 +1,7 @@
    +
    +Reporting a security issue
    +===========
    +
    +Please report any suspected security vulnerabilities responsibly to protect the users of this package. Try not share them publicly before the issue is confirmed and a fix is produced. 
    +
    +Send us an email at report @ mario.fyi to privately report any security vulnerability to us.
    
  • test.js+280 15 modified
    @@ -133,6 +133,43 @@ describe('get', function () {
         expect(objectPath.get(extended, 'enabled')).to.be.equal(true)
         expect(objectPath.get(extended, 'one')).to.be.equal(undefined)
       })
    +
    +  it('[security] should not get magic properties in default mode', function () {
    +    expect(objectPath.get({}, '__proto__')).to.be.undefined
    +    expect(objectPath.get({}, [['__proto__']])).to.be.undefined
    +
    +    function Clazz() {}
    +    Clazz.prototype.test = []
    +
    +    expect(objectPath.get(new Clazz(), '__proto__')).to.be.undefined
    +    expect(objectPath.get(new Clazz(), [['__proto__']])).to.be.undefined
    +    expect(objectPath.get(new Clazz(), ['constructor', 'prototype'])).to.be.undefined
    +  })
    +
    +  it('[security] should not get magic properties in inheritedProps mode', function () {
    +    expect(function() {
    +      objectPath.withInheritedProps.get({}, '__proto__')
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.get({}, [['__proto__']])
    +    }).to.throw('For security reasons')
    +
    +    function Clazz() {}
    +    Clazz.prototype.test = 'original'
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.get(new Clazz(), '__proto__')
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.get(new Clazz(), [['__proto__']])
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.get(new Clazz(), ['constructor', 'prototype'])
    +    }).to.throw('For security reasons')
    +  })
     })
     
     
    @@ -244,9 +281,15 @@ describe('set', function () {
         objectPath.set({}, [['__proto__'], 'injected'], 'this is bad')
         expect(Object.prototype.injected).to.be.undefined
     
    +    objectPath.set({}, ['__proto__'], {})
    +    expect(Object.prototype.toString).to.be.a('function')
    +
         function Clazz() {}
         Clazz.prototype.test = 'original'
     
    +    objectPath.set({}, ['__proto__'], {test: 'this is bad'})
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +
         objectPath.set(new Clazz(), '__proto__.test', 'this is bad')
         expect(Clazz.prototype.test).to.be.equal('original')
     
    @@ -258,9 +301,10 @@ describe('set', function () {
       })
     
       it('[security] should throw an exception if trying to set magic properties in inheritedProps mode', function () {
    -    expect(function() {objectPath.withInheritedProps.set({}, '__proto__.injected', 'this is bad')})
    -      .to.throw('For security reasons')
    -    expect(Object.prototype.injected).to.be.undefined
    +    expect(function() {
    +      objectPath.withInheritedProps.set({}, '__proto__.injected', 'this is bad')
    +      expect(Object.prototype.injected).to.be.undefined
    +    }).to.throw('For security reasons')
     
         expect(function() {
           objectPath.withInheritedProps.set({}, [['__proto__'], 'injected'], 'this is bad')
    @@ -270,21 +314,25 @@ describe('set', function () {
         function Clazz() {}
         Clazz.prototype.test = 'original'
     
    -    expect(function() {objectPath.withInheritedProps.set(new Clazz(), '__proto__.test', 'this is bad')})
    -      .to.throw('For security reasons')
    -    expect(Clazz.prototype.test).to.be.equal('original')
    +    expect(function() {
    +      objectPath.withInheritedProps.set(new Clazz(), '__proto__.test', 'this is bad')
    +      expect(Clazz.prototype.test).to.be.equal('original')
    +    }).to.throw('For security reasons')
     
    -    expect(function() {objectPath.withInheritedProps.set(new Clazz(), 'constructor.prototype.test', 'this is bad')})
    -      .to.throw('For security reasons')
    -    expect(Clazz.prototype.test).to.be.equal('original')
    +    expect(function() {
    +      objectPath.withInheritedProps.set(new Clazz(), 'constructor.prototype.test', 'this is bad')
    +      expect(Clazz.prototype.test).to.be.equal('original')
    +    }).to.throw('For security reasons')
     
    -    expect(function() {objectPath.withInheritedProps.set({}, 'constructor.prototype.injected', 'this is OK')})
    -      .to.throw('For security reasons')
    -    expect(Object.prototype.injected).to.be.undefined
    +    expect(function() {
    +      objectPath.withInheritedProps.set({}, 'constructor.prototype.injected', 'this is OK')
    +      expect(Object.prototype.injected).to.be.undefined
    +    }).to.throw('For security reasons')
     
    -    expect(function() {objectPath.withInheritedProps.set({}, [['constructor'], 'prototype', 'injected'], 'this is bad')})
    -      .to.throw('For security reasons')
    -    expect(Object.prototype.injected).to.be.undefined
    +    expect(function() {
    +      objectPath.withInheritedProps.set({}, [['constructor'], 'prototype', 'injected'], 'this is bad')
    +      expect(Object.prototype.injected).to.be.undefined
    +    }).to.throw('For security reasons')
       })
     })
     
    @@ -328,6 +376,39 @@ describe('push', function () {
         expect(obj).to.have.nested.property('b.e.0.0', 'l')
       })
     
    +  it('[security] should not push within prototype properties in default mode', function () {
    +    function Clazz() {}
    +    Clazz.prototype.test = []
    +
    +    objectPath.push(new Clazz(), '__proto__.test', 'pushed')
    +    expect(Clazz.prototype.test).to.be.deep.equal([])
    +
    +    objectPath.push(new Clazz(), [['__proto__'], 'test'], 'pushed')
    +    expect(Clazz.prototype.test).to.be.deep.equal([])
    +
    +    objectPath.push(new Clazz(), 'constructor.prototype.test', 'pushed')
    +    expect(Clazz.prototype.test).to.be.deep.equal([])
    +  })
    +
    +  it('[security] should not push within prototype properties in inheritedProps mode', function () {
    +    function Clazz() {}
    +    Clazz.prototype.test = []
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.push(new Clazz(), '__proto__.test', 'pushed')
    +      expect(Clazz.prototype.test).to.be.deep.equal([])
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.push(new Clazz(), [['__proto__'], 'test'], 'pushed')
    +      expect(Clazz.prototype.test).to.be.deep.equal([])
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.push(new Clazz(), 'constructor.prototype.test', 'pushed')
    +      expect(Clazz.prototype.test).to.be.deep.equal([])
    +    }).to.throw('For security reasons')
    +  })
     })
     
     
    @@ -361,6 +442,67 @@ describe('ensureExists', function () {
         expect(any[1]).to.be.an('object')
         expect(any[1][1]).to.be.an('object')
       })
    +
    +  it('[security] should not set magic properties in default mode', function () {
    +    objectPath.ensureExists({}, '__proto__.injected', 'this is bad')
    +    expect(Object.prototype.injected).to.be.undefined
    +
    +    objectPath.ensureExists({}, [['__proto__'], 'injected'], 'this is bad')
    +    expect(Object.prototype.injected).to.be.undefined
    +
    +    objectPath.ensureExists({}, ['__proto__'], {})
    +    expect(Object.prototype.toString).to.be.a('function')
    +
    +    function Clazz() {}
    +    Clazz.prototype.test = 'original'
    +
    +    objectPath.ensureExists({}, ['__proto__'], {test: 'this is bad'})
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +
    +    objectPath.ensureExists(new Clazz(), '__proto__.test', 'this is bad')
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +
    +    objectPath.ensureExists(new Clazz(), [['__proto__'], 'test'], 'this is bad')
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +
    +    objectPath.ensureExists(new Clazz(), 'constructor.prototype.test', 'this is bad')
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +  })
    +
    +  it('[security] should throw an exception if trying to set magic properties in inheritedProps mode', function () {
    +    expect(function() {objectPath.withInheritedProps.ensureExists({}, '__proto__.injected', 'this is bad')})
    +      .to.throw('For security reasons')
    +    expect(Object.prototype.injected).to.be.undefined
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.ensureExists({}, [['__proto__'], 'injected'], 'this is bad')
    +      expect(Object.prototype.injected).to.be.undefined
    +    }).to.throw('For security reasons')
    +
    +    function Clazz() {}
    +    Clazz.prototype.test = 'original'
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.ensureExists(new Clazz(), '__proto__.test', 'this is bad')
    +      expect(Clazz.prototype.test).to.be.equal('original')
    +    }).to.throw('For security reasons')
    +
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.ensureExists(new Clazz(), 'constructor.prototype.test', 'this is bad')
    +      expect(Clazz.prototype.test).to.be.equal('original')
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.ensureExists({}, 'constructor.prototype.injected', 'this is OK')
    +      expect(Object.prototype.injected).to.be.undefined
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.ensureExists({}, [['constructor'], 'prototype', 'injected'], 'this is bad')
    +      expect(Object.prototype.injected).to.be.undefined
    +    }).to.throw('For security reasons')
    +  })
     })
     
     describe('coalesce', function () {
    @@ -512,6 +654,40 @@ describe('empty', function () {
         expect(obj.instance.arr).to.be.an('array')
         expect(obj['function']).to.equal(null)
       })
    +
    +  it('[security] should not empty prototype properties in default mode', function () {
    +    function Clazz() {}
    +    Clazz.prototype.test = 'original'
    +
    +    objectPath.empty(new Clazz(), '__proto__')
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +
    +    objectPath.empty(new Clazz(), [['__proto__']])
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +
    +    objectPath.empty(new Clazz(), 'constructor.prototype')
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +  })
    +
    +  it('[security] should throw an exception if trying to delete prototype properties in inheritedProps mode', function () {
    +    function Clazz() {}
    +    Clazz.prototype.test = 'original'
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.empty(new Clazz(), '__proto__')
    +      expect(Clazz.prototype.test).to.be.equal('original')
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.empty(new Clazz(), 'constructor.prototype')
    +      expect(Clazz.prototype.test).to.be.equal('original')
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.empty({}, [['constructor'], 'prototype'])
    +      expect(Clazz.prototype.test).to.be.equal('original')
    +    }).to.throw('For security reasons')
    +  })
     })
     
     describe('del', function () {
    @@ -588,6 +764,56 @@ describe('del', function () {
         expect(obj.b.d).to.have.length(0)
         expect(obj.b.d).to.be.deep.equal([])
       })
    +
    +  it('[security] should not delete prototype properties in default mode', function () {
    +    objectPath.del({}, '__proto__.valueOf')
    +    expect(Object.prototype.valueOf).to.be.a('function')
    +
    +    objectPath.del({}, [['__proto__'], 'valueOf'])
    +    expect(Object.prototype.valueOf).to.be.a('function')
    +
    +    function Clazz() {}
    +    Clazz.prototype.test = 'original'
    +
    +    objectPath.del(new Clazz(), '__proto__.test')
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +
    +    objectPath.del(new Clazz(), [['__proto__'], 'test'])
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +
    +    objectPath.del(new Clazz(), 'constructor.prototype.test')
    +    expect(Clazz.prototype.test).to.be.equal('original')
    +  })
    +
    +  it('[security] should throw an exception if trying to delete prototype properties in inheritedProps mode', function () {
    +    expect(function() {
    +      objectPath.withInheritedProps.del({}, '__proto__.valueOf')
    +      expect(Object.prototype.valueOf).to.be.a('function')
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.del({}, [['__proto__'], 'valueOf'])
    +      expect(Object.prototype.valueOf).to.be.a('function')
    +    }).to.throw('For security reasons')
    +
    +    function Clazz() {}
    +    Clazz.prototype.test = 'original'
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.del(new Clazz(), '__proto__.test')
    +      expect(Clazz.prototype.test).to.be.equal('original')
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.del(new Clazz(), 'constructor.prototype.test', 'this is bad')
    +      expect(Clazz.prototype.test).to.be.equal('original')
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.del({}, [['constructor'], 'prototype', 'test'])
    +      expect(Clazz.prototype.test).to.be.equal('original')
    +    }).to.throw('For security reasons')
    +  })
     })
     
     describe('insert', function () {
    @@ -630,6 +856,45 @@ describe('insert', function () {
           'asdf'
         ])
       })
    +
    +  it('[security] should not insert within prototype properties in default mode', function () {
    +    function Clazz() {}
    +    Clazz.prototype.test = []
    +
    +    objectPath.insert(new Clazz(), '__proto__.test', 'inserted', 0)
    +    expect(Clazz.prototype.test).to.be.deep.equal([])
    +
    +    objectPath.insert(new Clazz(), [['__proto__'], 'test'], 'inserted', 0)
    +    expect(Clazz.prototype.test).to.be.deep.equal([])
    +
    +    objectPath.insert(new Clazz(), 'constructor.prototype.test', 'inserted', 0)
    +    expect(Clazz.prototype.test).to.be.deep.equal([])
    +  })
    +
    +  it('[security] should not insert within prototype properties in inheritedProps mode', function () {
    +    function Clazz() {}
    +    Clazz.prototype.test = []
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.insert(new Clazz(), '__proto__.test', 'inserted', 0)
    +      expect(Clazz.prototype.test).to.be.deep.equal([])
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.insert(new Clazz(), [['__proto__'], 'test'], 'inserted', 0)
    +      expect(Clazz.prototype.test).to.be.deep.equal([])
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.insert(new Clazz(), 'constructor.prototype.test', 'inserted', 0)
    +      expect(Clazz.prototype.test).to.be.deep.equal([])
    +    }).to.throw('For security reasons')
    +
    +    expect(function() {
    +      objectPath.withInheritedProps.insert(new Clazz().constructor, 'prototype.test', 'inserted', 0)
    +      expect(Clazz.prototype.test).to.be.deep.equal([])
    +    }).to.throw('For security reasons')
    +  })
     })
     
     describe('has', function () {
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.