VYPR
Moderate severityNVD Advisory· Published Feb 18, 2020· Updated Aug 4, 2024

CVE-2019-10793

CVE-2019-10793

Description

CVE-2019-10793: Prototype Pollution in dot-object before 2.1.3 allows attackers to pollute Object.prototype via __proto__ payloads.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

CVE-2019-10793: Prototype Pollution in dot-object before 2.1.3 allows attackers to pollute Object.prototype via __proto__ payloads.

Vulnerability

Overview CVE-2019-10793 is a Prototype Pollution vulnerability in the dot-object npm library, affecting versions before 2.1.3. The set function can be abused to add or modify properties on Object.prototype by crafting a path that includes __proto__. This occurs because the library processes dot-separated path strings and assigns values without sanitizing dangerous keys like __proto__, prototype, or constructor [1][3].

Attack

Vector and Prerequisites The attacker supplies a specially crafted path (e.g., __proto__.polluted) to the dot.object() or dot.set() methods, causing the library to recursively traverse and assign properties. No authentication is required beyond the ability to control path/object inputs. The exploit does not require the attacker to have prior access to the target system's internal objects; rather, any application that uses dot-object to transform untrusted user input into nested objects is vulnerable [2][4].

Impact

Successful exploitation leads to Prototype Pollution, allowing the attacker to inject properties into the global Object.prototype. This can affect all objects in the JavaScript runtime, potentially leading to property injection, bypass of security checks, denial of service, or remote code execution depending on the application's context. The vulnerability does not directly expose data but can be a stepping stone for more severe attacks [3].

Mitigation

The fix was released in version 2.1.3, which adds a blacklist check for __proto__, prototype, and constructor in the parsePath function and throws an error if any of these keys are encountered [1][4]. Users should upgrade to at least version 2.1.3. The vulnerability is also tracked in the Snyk database (SNYK-JS-DOTOBJECT-548905) [3].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
dot-objectnpm
< 2.1.32.1.3

Affected products

2
  • ghsa-coords
    Range: < 2.1.3
  • Snyk/dot-objectv5
    Range: All versions prior to version 2.1.3

Patches

1
f76cff5fe6d0

guard for possible prototype polution

https://github.com/rhalff/dot-objectRob HalffFeb 16, 2020via ghsa
5 files changed · +188 119
  • index.js+80 57 modified
    @@ -45,11 +45,22 @@ function isEmptyObject (val) {
       return Object.keys(val).length === 0
     }
     
    +const blacklist = ['__proto__', 'prototype', 'constructor']
    +
     function parsePath (path, sep) {
       if (path.indexOf('[') >= 0) {
         path = path.replace(/\[/g, '.').replace(/]/g, '')
       }
    -  return path.split(sep)
    +
    +  const parts = path.split(sep)
    +
    +  const check = parts.filter(part => blacklist.indexOf(part) === -1)
    +
    +  if (check.length !== parts.length) {
    +    throw Error('Refusing to update blacklisted property ' + path)
    +  }
    +
    +  return parts
     }
     
     var hasOwnProperty = Object.prototype.hasOwnProperty
    @@ -83,8 +94,7 @@ DotObject.prototype._fill = function (a, obj, v, mod) {
       var k = a.shift()
     
       if (a.length > 0) {
    -    obj[k] = obj[k] ||
    -      (this.useArray && isIndex(a[0]) ? [] : {})
    +    obj[k] = obj[k] || (this.useArray && isIndex(a[0]) ? [] : {})
     
         if (!isArrayOrObject(obj[k])) {
           if (this.override) {
    @@ -102,8 +112,7 @@ DotObject.prototype._fill = function (a, obj, v, mod) {
     
         this._fill(a, obj[k], v, mod)
       } else {
    -    if (!this.override &&
    -      isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
    +    if (!this.override && isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
           if (!(isArrayOrObject(v) && isEmptyObject(v))) {
             throw new Error("Trying to redefine non-empty obj['" + k + "']")
           }
    @@ -195,7 +204,7 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
       for (i = 0; i < keys.length; i++) {
         key = parseKey(keys[i], obj)
         if (obj && typeof obj === 'object' && key in obj) {
    -      if (i === (keys.length - 1)) {
    +      if (i === keys.length - 1) {
             if (remove) {
               val = obj[key]
               if (reindexArray && Array.isArray(obj)) {
    @@ -221,7 +230,9 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
         }
       }
       if (remove && Array.isArray(obj)) {
    -    obj = obj.filter(function (n) { return n !== undefined })
    +    obj = obj.filter(function (n) {
    +      return n !== undefined
    +    })
       }
       return obj
     }
    @@ -279,7 +290,9 @@ DotObject.prototype._cleanup = function (obj) {
           keys = this.cleanup[i].split('.')
           root = keys.splice(0, -1).join('.')
           ret = root ? this.pick(root, obj) : obj
    -      ret = ret[keys[0]].filter(function (v) { return v !== undefined })
    +      ret = ret[keys[0]].filter(function (v) {
    +        return v !== undefined
    +      })
           this.set(this.cleanup[i], ret, obj)
         }
         this.cleanup = []
    @@ -336,13 +349,21 @@ DotObject.prototype.move = function (source, target, obj, mods, merge) {
      * @param {Function|Array} mods
      * @param {Boolean} merge
      */
    -DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge) {
    +DotObject.prototype.transfer = function (
    +  source,
    +  target,
    +  obj1,
    +  obj2,
    +  mods,
    +  merge
    +) {
       if (typeof mods === 'function' || Array.isArray(mods)) {
    -    this.set(target,
    -      _process(
    -        this.pick(source, obj1, true),
    -        mods
    -      ), obj2, merge)
    +    this.set(
    +      target,
    +      _process(this.pick(source, obj1, true), mods),
    +      obj2,
    +      merge
    +    )
       } else {
         merge = mods
         this.set(target, this.pick(source, obj1, true), obj2, merge)
    @@ -367,16 +388,16 @@ DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge
      */
     DotObject.prototype.copy = function (source, target, obj1, obj2, mods, merge) {
       if (typeof mods === 'function' || Array.isArray(mods)) {
    -    this.set(target,
    +    this.set(
    +      target,
           _process(
             // clone what is picked
    -        JSON.parse(
    -          JSON.stringify(
    -            this.pick(source, obj1, false)
    -          )
    -        ),
    +        JSON.parse(JSON.stringify(this.pick(source, obj1, false))),
             mods
    -      ), obj2, merge)
    +      ),
    +      obj2,
    +      merge
    +    )
       } else {
         merge = mods
         this.set(target, this.pick(source, obj1, false), obj2, merge)
    @@ -408,7 +429,7 @@ DotObject.prototype.set = function (path, val, obj, merge) {
     
       for (i = 0; i < keys.length; i++) {
         key = keys[i]
    -    if (i === (keys.length - 1)) {
    +    if (i === keys.length - 1) {
           if (merge && isObject(val) && isObject(obj[key])) {
             for (k in val) {
               if (hasOwnProperty.call(val, k)) {
    @@ -447,14 +468,14 @@ DotObject.prototype.set = function (path, val, obj, merge) {
      *
      *   var obj = {
      *     "id": 1,
    -  *    "some": {
    -  *      "thing": "else"
    -  *    }
    + *    "some": {
    + *      "thing": "else"
    + *    }
      *   }
      *
      *   var transform = {
      *     "id": "nr",
    -  *    "some.thing": "name"
    + *    "some.thing": "name"
      *   }
      *
      *   var tgt = dot.transform(transform, obj)
    @@ -466,9 +487,11 @@ DotObject.prototype.set = function (path, val, obj, merge) {
     DotObject.prototype.transform = function (recipe, obj, tgt) {
       obj = obj || {}
       tgt = tgt || {}
    -  Object.keys(recipe).forEach(function (key) {
    -    this.set(recipe[key], this.pick(key, obj), tgt)
    -  }.bind(this))
    +  Object.keys(recipe).forEach(
    +    function (key) {
    +      this.set(recipe[key], this.pick(key, obj), tgt)
    +    }.bind(this)
    +  )
       return tgt
     }
     
    @@ -494,31 +517,33 @@ DotObject.prototype.dot = function (obj, tgt, path) {
       path = path || []
       var isArray = Array.isArray(obj)
     
    -  Object.keys(obj).forEach(function (key) {
    -    var index = isArray && this.useBrackets ? '[' + key + ']' : key
    -    if (
    -      (
    +  Object.keys(obj).forEach(
    +    function (key) {
    +      var index = isArray && this.useBrackets ? '[' + key + ']' : key
    +      if (
             isArrayOrObject(obj[key]) &&
    -        (
    -          (isObject(obj[key]) && !isEmptyObject(obj[key])) ||
    -          (Array.isArray(obj[key]) && (!this.keepArray && (obj[key].length !== 0)))
    -        )
    -      )
    -    ) {
    -      if (isArray && this.useBrackets) {
    -        var previousKey = path[path.length - 1] || ''
    -        return this.dot(obj[key], tgt, path.slice(0, -1).concat(previousKey + index))
    -      } else {
    -        return this.dot(obj[key], tgt, path.concat(index))
    -      }
    -    } else {
    -      if (isArray && this.useBrackets) {
    -        tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
    +        ((isObject(obj[key]) && !isEmptyObject(obj[key])) ||
    +          (Array.isArray(obj[key]) && !this.keepArray && obj[key].length !== 0))
    +      ) {
    +        if (isArray && this.useBrackets) {
    +          var previousKey = path[path.length - 1] || ''
    +          return this.dot(
    +            obj[key],
    +            tgt,
    +            path.slice(0, -1).concat(previousKey + index)
    +          )
    +        } else {
    +          return this.dot(obj[key], tgt, path.concat(index))
    +        }
           } else {
    -        tgt[path.concat(index).join(this.separator)] = obj[key]
    +        if (isArray && this.useBrackets) {
    +          tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
    +        } else {
    +          tgt[path.concat(index).join(this.separator)] = obj[key]
    +        }
           }
    -    }
    -  }.bind(this))
    +    }.bind(this)
    +  )
       return tgt
     }
     
    @@ -532,9 +557,8 @@ DotObject.str = wrap('str')
     DotObject.set = wrap('set')
     DotObject.delete = wrap('delete')
     DotObject.del = DotObject.remove = wrap('remove')
    -DotObject.dot = wrap('dot')
    -
    -;['override', 'overwrite'].forEach(function (prop) {
    +DotObject.dot = wrap('dot');
    +['override', 'overwrite'].forEach(function (prop) {
       Object.defineProperty(DotObject, prop, {
         get: function () {
           return dotDefault.override
    @@ -543,9 +567,8 @@ DotObject.dot = wrap('dot')
           dotDefault.override = !!val
         }
       })
    -})
    -
    -;['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
    +});
    +['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
       Object.defineProperty(DotObject, prop, {
         get: function () {
           return dotDefault[prop]
    
  • src/dot-object.js+80 57 modified
    @@ -45,11 +45,22 @@ function isEmptyObject (val) {
       return Object.keys(val).length === 0
     }
     
    +const blacklist = ['__proto__', 'prototype', 'constructor']
    +
     function parsePath (path, sep) {
       if (path.indexOf('[') >= 0) {
         path = path.replace(/\[/g, '.').replace(/]/g, '')
       }
    -  return path.split(sep)
    +
    +  const parts = path.split(sep)
    +
    +  const check = parts.filter(part => blacklist.indexOf(part) === -1)
    +
    +  if (check.length !== parts.length) {
    +    throw Error('Refusing to update blacklisted property ' + path)
    +  }
    +
    +  return parts
     }
     
     var hasOwnProperty = Object.prototype.hasOwnProperty
    @@ -83,8 +94,7 @@ DotObject.prototype._fill = function (a, obj, v, mod) {
       var k = a.shift()
     
       if (a.length > 0) {
    -    obj[k] = obj[k] ||
    -      (this.useArray && isIndex(a[0]) ? [] : {})
    +    obj[k] = obj[k] || (this.useArray && isIndex(a[0]) ? [] : {})
     
         if (!isArrayOrObject(obj[k])) {
           if (this.override) {
    @@ -102,8 +112,7 @@ DotObject.prototype._fill = function (a, obj, v, mod) {
     
         this._fill(a, obj[k], v, mod)
       } else {
    -    if (!this.override &&
    -      isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
    +    if (!this.override && isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
           if (!(isArrayOrObject(v) && isEmptyObject(v))) {
             throw new Error("Trying to redefine non-empty obj['" + k + "']")
           }
    @@ -195,7 +204,7 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
       for (i = 0; i < keys.length; i++) {
         key = parseKey(keys[i], obj)
         if (obj && typeof obj === 'object' && key in obj) {
    -      if (i === (keys.length - 1)) {
    +      if (i === keys.length - 1) {
             if (remove) {
               val = obj[key]
               if (reindexArray && Array.isArray(obj)) {
    @@ -221,7 +230,9 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
         }
       }
       if (remove && Array.isArray(obj)) {
    -    obj = obj.filter(function (n) { return n !== undefined })
    +    obj = obj.filter(function (n) {
    +      return n !== undefined
    +    })
       }
       return obj
     }
    @@ -279,7 +290,9 @@ DotObject.prototype._cleanup = function (obj) {
           keys = this.cleanup[i].split('.')
           root = keys.splice(0, -1).join('.')
           ret = root ? this.pick(root, obj) : obj
    -      ret = ret[keys[0]].filter(function (v) { return v !== undefined })
    +      ret = ret[keys[0]].filter(function (v) {
    +        return v !== undefined
    +      })
           this.set(this.cleanup[i], ret, obj)
         }
         this.cleanup = []
    @@ -336,13 +349,21 @@ DotObject.prototype.move = function (source, target, obj, mods, merge) {
      * @param {Function|Array} mods
      * @param {Boolean} merge
      */
    -DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge) {
    +DotObject.prototype.transfer = function (
    +  source,
    +  target,
    +  obj1,
    +  obj2,
    +  mods,
    +  merge
    +) {
       if (typeof mods === 'function' || Array.isArray(mods)) {
    -    this.set(target,
    -      _process(
    -        this.pick(source, obj1, true),
    -        mods
    -      ), obj2, merge)
    +    this.set(
    +      target,
    +      _process(this.pick(source, obj1, true), mods),
    +      obj2,
    +      merge
    +    )
       } else {
         merge = mods
         this.set(target, this.pick(source, obj1, true), obj2, merge)
    @@ -367,16 +388,16 @@ DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge
      */
     DotObject.prototype.copy = function (source, target, obj1, obj2, mods, merge) {
       if (typeof mods === 'function' || Array.isArray(mods)) {
    -    this.set(target,
    +    this.set(
    +      target,
           _process(
             // clone what is picked
    -        JSON.parse(
    -          JSON.stringify(
    -            this.pick(source, obj1, false)
    -          )
    -        ),
    +        JSON.parse(JSON.stringify(this.pick(source, obj1, false))),
             mods
    -      ), obj2, merge)
    +      ),
    +      obj2,
    +      merge
    +    )
       } else {
         merge = mods
         this.set(target, this.pick(source, obj1, false), obj2, merge)
    @@ -408,7 +429,7 @@ DotObject.prototype.set = function (path, val, obj, merge) {
     
       for (i = 0; i < keys.length; i++) {
         key = keys[i]
    -    if (i === (keys.length - 1)) {
    +    if (i === keys.length - 1) {
           if (merge && isObject(val) && isObject(obj[key])) {
             for (k in val) {
               if (hasOwnProperty.call(val, k)) {
    @@ -447,14 +468,14 @@ DotObject.prototype.set = function (path, val, obj, merge) {
      *
      *   var obj = {
      *     "id": 1,
    -  *    "some": {
    -  *      "thing": "else"
    -  *    }
    + *    "some": {
    + *      "thing": "else"
    + *    }
      *   }
      *
      *   var transform = {
      *     "id": "nr",
    -  *    "some.thing": "name"
    + *    "some.thing": "name"
      *   }
      *
      *   var tgt = dot.transform(transform, obj)
    @@ -466,9 +487,11 @@ DotObject.prototype.set = function (path, val, obj, merge) {
     DotObject.prototype.transform = function (recipe, obj, tgt) {
       obj = obj || {}
       tgt = tgt || {}
    -  Object.keys(recipe).forEach(function (key) {
    -    this.set(recipe[key], this.pick(key, obj), tgt)
    -  }.bind(this))
    +  Object.keys(recipe).forEach(
    +    function (key) {
    +      this.set(recipe[key], this.pick(key, obj), tgt)
    +    }.bind(this)
    +  )
       return tgt
     }
     
    @@ -494,31 +517,33 @@ DotObject.prototype.dot = function (obj, tgt, path) {
       path = path || []
       var isArray = Array.isArray(obj)
     
    -  Object.keys(obj).forEach(function (key) {
    -    var index = isArray && this.useBrackets ? '[' + key + ']' : key
    -    if (
    -      (
    +  Object.keys(obj).forEach(
    +    function (key) {
    +      var index = isArray && this.useBrackets ? '[' + key + ']' : key
    +      if (
             isArrayOrObject(obj[key]) &&
    -        (
    -          (isObject(obj[key]) && !isEmptyObject(obj[key])) ||
    -          (Array.isArray(obj[key]) && (!this.keepArray && (obj[key].length !== 0)))
    -        )
    -      )
    -    ) {
    -      if (isArray && this.useBrackets) {
    -        var previousKey = path[path.length - 1] || ''
    -        return this.dot(obj[key], tgt, path.slice(0, -1).concat(previousKey + index))
    -      } else {
    -        return this.dot(obj[key], tgt, path.concat(index))
    -      }
    -    } else {
    -      if (isArray && this.useBrackets) {
    -        tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
    +        ((isObject(obj[key]) && !isEmptyObject(obj[key])) ||
    +          (Array.isArray(obj[key]) && !this.keepArray && obj[key].length !== 0))
    +      ) {
    +        if (isArray && this.useBrackets) {
    +          var previousKey = path[path.length - 1] || ''
    +          return this.dot(
    +            obj[key],
    +            tgt,
    +            path.slice(0, -1).concat(previousKey + index)
    +          )
    +        } else {
    +          return this.dot(obj[key], tgt, path.concat(index))
    +        }
           } else {
    -        tgt[path.concat(index).join(this.separator)] = obj[key]
    +        if (isArray && this.useBrackets) {
    +          tgt[path.join(this.separator).concat('[' + key + ']')] = obj[key]
    +        } else {
    +          tgt[path.concat(index).join(this.separator)] = obj[key]
    +        }
           }
    -    }
    -  }.bind(this))
    +    }.bind(this)
    +  )
       return tgt
     }
     
    @@ -532,9 +557,8 @@ DotObject.str = wrap('str')
     DotObject.set = wrap('set')
     DotObject.delete = wrap('delete')
     DotObject.del = DotObject.remove = wrap('remove')
    -DotObject.dot = wrap('dot')
    -
    -;['override', 'overwrite'].forEach(function (prop) {
    +DotObject.dot = wrap('dot');
    +['override', 'overwrite'].forEach(function (prop) {
       Object.defineProperty(DotObject, prop, {
         get: function () {
           return dotDefault.override
    @@ -543,9 +567,8 @@ DotObject.dot = wrap('dot')
           dotDefault.override = !!val
         }
       })
    -})
    -
    -;['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
    +});
    +['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
       Object.defineProperty(DotObject, prop, {
         get: function () {
           return dotDefault[prop]
    
  • test/array_notation.js+6 0 modified
    @@ -167,4 +167,10 @@ describe('Dotted Array notation', function () {
       describe('with bracket notation', function () {
         runVariant('bracket')
       })
    +
    +  describe('Refuse to update __proto__', function () {
    +    var obj = { path: [] }
    +
    +    ;(() => Dot.set('path[0].__proto__.toString', 'test', obj)).should.throw(/Refusing to update/)
    +  })
     })
    
  • test/dot-json.js+7 0 modified
    @@ -205,4 +205,11 @@ describe('Object test:', function () {
     
         row.should.eql({ page: { name: 'my_page' } })
       })
    +
    +  it('Dot.object should disallow to set __proto__', function () {
    +    var row = { '__proto__.toString': 'hi' }
    +
    +    var dot = new Dot()
    +    ;(() => dot.object(row)).should.throw(/Refusing to update/)
    +  })
     })
    
  • test/str.js+15 5 modified
    @@ -34,11 +34,14 @@ describe('str:', function () {
         obj.should.deepEqual({
           a: 1,
           object: {
    -        fields: [{
    -          subfield: 'value'
    -        }, {
    -          subfield: 'value1'
    -        }]
    +        fields: [
    +          {
    +            subfield: 'value'
    +          },
    +          {
    +            subfield: 'value1'
    +          }
    +        ]
           }
         })
       })
    @@ -50,4 +53,11 @@ describe('str:', function () {
           a: 'b'
         })
       })
    +
    +  it('cannot set __proto__ property', function () {
    +    (() => Dot.str('__proto__.toString', 'hi', {})).should.throw(
    +      /Refusing to update/
    +    );
    +    ({}.toString().should.deepEqual('[object Object]'))
    +  })
     })
    

Vulnerability mechanics

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

References

4

News mentions

0

No linked articles in our index yet.