CVE-2019-10793: Improper Neutralization of Special Elements in Output Used by a Downstream Component ('Injection') (CWE-74)
value: dot-object before 2.1.3 is vulnerable to Prototype Pollution. The set function could be tricked into adding or modifying properties of Object.prototype using a __proto__ payload.
CWE description: The product constructs all or part of a command, data structure, or record using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify how it is parsed or interpreted when it is sent to a downstream component.
CVSS v3 base score: 6.3
Published: 2020-02-18T16:15Z
Repository: https://github.com/rhalff/dot-object
Language: JavaScript
Files changed: index.js, src/dot-object.js, test/array_notation.js, test/dot-json.js, test/str.js
Commit date: 2020-02-16 19:07:45 +0100
Commit message: guard for possible prototype polution

Diff: diff --git a/index.js b/index.js
index c5dc7ca..ae87252 100644
--- a/index.js
+++ b/index.js
@@ -43,15 +43,26 @@ function isArrayOrObject (val) {
 
 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
 
 function DotObject (separator, override, useArray, useBrackets) {
@@ -81,12 +92,11 @@ function wrap (method) {
 
 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) {
         obj[k] = {}
       } else {
@@ -100,12 +110,11 @@ 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 + "']")
       }
 
       return
@@ -193,11 +202,11 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
 
   keys = parsePath(path, this.separator)
   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)) {
             obj.splice(key, 1)
           } else {
@@ -219,11 +228,13 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
     } else {
       return undefined
     }
   }
   if (remove && Array.isArray(obj)) {
-    obj = obj.filter(function (n) { return n !== undefined })
+    obj = obj.filter(function (n) {
+      return n !== undefined
+    })
   }
   return obj
 }
 /**
  *
@@ -277,11 +288,13 @@ DotObject.prototype._cleanup = function (obj) {
   if (this.cleanup.length) {
     for (i = 0; i < this.cleanup.length; i++) {
       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 = []
   }
 }
@@ -334,17 +347,25 @@ DotObject.prototype.move = function (source, target, obj, mods, merge) {
  * @param {Object} obj1
  * @param {Object} obj2
  * @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)
   }
 
@@ -365,20 +386,20 @@ DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge
  * @param {Function|Array} mods
  * @param {Boolean} 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)
   }
 
@@ -406,11 +427,11 @@ DotObject.prototype.set = function (path, val, obj, merge) {
   }
   keys = parsePath(path, this.separator)
 
   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)) {
             obj[key][k] = val[k]
           }
@@ -445,18 +466,18 @@ DotObject.prototype.set = function (path, val, obj, merge) {
  *
  * Usage:
  *
  *   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)
  *
  * @param {Object} recipe Transform recipe
@@ -464,13 +485,15 @@ DotObject.prototype.set = function (path, val, obj, merge) {
  * @param {Array} mods modifiers for the target
  */
 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
 }
 
 /**
  *
@@ -492,35 +515,37 @@ DotObject.prototype.transform = function (recipe, obj, tgt) {
 DotObject.prototype.dot = function (obj, tgt, path) {
   tgt = tgt || {}
   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
 }
 
 DotObject.pick = wrap('pick')
 DotObject.move = wrap('move')
@@ -530,24 +555,22 @@ DotObject.copy = wrap('copy')
 DotObject.object = wrap('object')
 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
     },
     set: function (val) {
       dotDefault.override = !!val
     }
   })
-})
-
-;['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
+});
+['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
   Object.defineProperty(DotObject, prop, {
     get: function () {
       return dotDefault[prop]
     },
     set: function (val) {
diff --git a/src/dot-object.js b/src/dot-object.js
index 311b7a4..a7f5c9e 100644
--- a/src/dot-object.js
+++ b/src/dot-object.js
@@ -43,15 +43,26 @@ function isArrayOrObject (val) {
 
 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
 
 function DotObject (separator, override, useArray, useBrackets) {
@@ -81,12 +92,11 @@ function wrap (method) {
 
 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) {
         obj[k] = {}
       } else {
@@ -100,12 +110,11 @@ 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 + "']")
       }
 
       return
@@ -193,11 +202,11 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
 
   keys = parsePath(path, this.separator)
   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)) {
             obj.splice(key, 1)
           } else {
@@ -219,11 +228,13 @@ DotObject.prototype.pick = function (path, obj, remove, reindexArray) {
     } else {
       return undefined
     }
   }
   if (remove && Array.isArray(obj)) {
-    obj = obj.filter(function (n) { return n !== undefined })
+    obj = obj.filter(function (n) {
+      return n !== undefined
+    })
   }
   return obj
 }
 /**
  *
@@ -277,11 +288,13 @@ DotObject.prototype._cleanup = function (obj) {
   if (this.cleanup.length) {
     for (i = 0; i < this.cleanup.length; i++) {
       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 = []
   }
 }
@@ -334,17 +347,25 @@ DotObject.prototype.move = function (source, target, obj, mods, merge) {
  * @param {Object} obj1
  * @param {Object} obj2
  * @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)
   }
 
@@ -365,20 +386,20 @@ DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge
  * @param {Function|Array} mods
  * @param {Boolean} 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)
   }
 
@@ -406,11 +427,11 @@ DotObject.prototype.set = function (path, val, obj, merge) {
   }
   keys = parsePath(path, this.separator)
 
   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)) {
             obj[key][k] = val[k]
           }
@@ -445,18 +466,18 @@ DotObject.prototype.set = function (path, val, obj, merge) {
  *
  * Usage:
  *
  *   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)
  *
  * @param {Object} recipe Transform recipe
@@ -464,13 +485,15 @@ DotObject.prototype.set = function (path, val, obj, merge) {
  * @param {Array} mods modifiers for the target
  */
 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
 }
 
 /**
  *
@@ -492,35 +515,37 @@ DotObject.prototype.transform = function (recipe, obj, tgt) {
 DotObject.prototype.dot = function (obj, tgt, path) {
   tgt = tgt || {}
   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
 }
 
 DotObject.pick = wrap('pick')
 DotObject.move = wrap('move')
@@ -530,24 +555,22 @@ DotObject.copy = wrap('copy')
 DotObject.object = wrap('object')
 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
     },
     set: function (val) {
       dotDefault.override = !!val
     }
   })
-})
-
-;['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
+});
+['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
   Object.defineProperty(DotObject, prop, {
     get: function () {
       return dotDefault[prop]
     },
     set: function (val) {
diff --git a/test/array_notation.js b/test/array_notation.js
index 4a94c6b..e5b261a 100644
--- a/test/array_notation.js
+++ b/test/array_notation.js
@@ -165,6 +165,12 @@ describe('Dotted Array notation', function () {
 
   // extra logic no real benefit.
   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/)
+  })
 })
diff --git a/test/dot-json.js b/test/dot-json.js
index f237ede..edfafb1 100644
--- a/test/dot-json.js
+++ b/test/dot-json.js
@@ -203,6 +203,13 @@ describe('Object test:', function () {
     var dot = new Dot('=>', false)
     dot.object(row, mods)
 
     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/)
+  })
 })
diff --git a/test/str.js b/test/str.js
index ecae17f..302f9c6 100644
--- a/test/str.js
+++ b/test/str.js
@@ -32,15 +32,18 @@ describe('str:', function () {
     Dot.str('object.fields[1].subfield', 'value1', obj)
 
     obj.should.deepEqual({
       a: 1,
       object: {
-        fields: [{
-          subfield: 'value'
-        }, {
-          subfield: 'value1'
-        }]
+        fields: [
+          {
+            subfield: 'value'
+          },
+          {
+            subfield: 'value1'
+          }
+        ]
       }
     })
   })
 
   it('can set root level property regardless whether override is set', function () {
@@ -48,6 +51,13 @@ describe('str:', function () {
       a: 1
     }).should.deepEqual({
       a: 'b'
     })
   })
+
+  it('cannot set __proto__ property', function () {
+    (() => Dot.str('__proto__.toString', 'hi', {})).should.throw(
+      /Refusing to update/
+    );
+    ({}.toString().should.deepEqual('[object Object]'))
+  })
 })

Vulnerable code:   return path.split(sep)
    obj[k] = obj[k] ||
      (this.useArray && isIndex(a[0]) ? [] : {})
    if (!this.override &&
      isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
      if (i === (keys.length - 1)) {
    obj = obj.filter(function (n) { return n !== undefined })
      ret = ret[keys[0]].filter(function (v) { return v !== undefined })
DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge) {
    this.set(target,
      _process(
        this.pick(source, obj1, true),
        mods
      ), obj2, merge)
    this.set(target,
        JSON.parse(
          JSON.stringify(
            this.pick(source, obj1, false)
          )
        ),
      ), obj2, merge)
    if (i === (keys.length - 1)) {
  *    "some": {
  *      "thing": "else"
  *    }
  *    "some.thing": "name"
  Object.keys(recipe).forEach(function (key) {
    this.set(recipe[key], this.pick(key, obj), tgt)
  }.bind(this))
  Object.keys(obj).forEach(function (key) {
    var index = isArray && this.useBrackets ? '[' + key + ']' : key
    if (
      (
        (
          (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]
        tgt[path.concat(index).join(this.separator)] = obj[key]
    }
  }.bind(this))
DotObject.dot = wrap('dot')

;['override', 'overwrite'].forEach(function (prop) {
})

;['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
  return path.split(sep)
    obj[k] = obj[k] ||
      (this.useArray && isIndex(a[0]) ? [] : {})
    if (!this.override &&
      isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
      if (i === (keys.length - 1)) {
    obj = obj.filter(function (n) { return n !== undefined })
      ret = ret[keys[0]].filter(function (v) { return v !== undefined })
DotObject.prototype.transfer = function (source, target, obj1, obj2, mods, merge) {
    this.set(target,
      _process(
        this.pick(source, obj1, true),
        mods
      ), obj2, merge)
    this.set(target,
        JSON.parse(
          JSON.stringify(
            this.pick(source, obj1, false)
          )
        ),
      ), obj2, merge)
    if (i === (keys.length - 1)) {
  *    "some": {
  *      "thing": "else"
  *    }
  *    "some.thing": "name"
  Object.keys(recipe).forEach(function (key) {
    this.set(recipe[key], this.pick(key, obj), tgt)
  }.bind(this))
  Object.keys(obj).forEach(function (key) {
    var index = isArray && this.useBrackets ? '[' + key + ']' : key
    if (
      (
        (
          (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]
        tgt[path.concat(index).join(this.separator)] = obj[key]
    }
  }.bind(this))
DotObject.dot = wrap('dot')

;['override', 'overwrite'].forEach(function (prop) {
})

;['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
        fields: [{
          subfield: 'value'
        }, {
          subfield: 'value1'
        }]
Fixed code: const blacklist = ['__proto__', 'prototype', 'constructor']


  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
    obj[k] = obj[k] || (this.useArray && isIndex(a[0]) ? [] : {})
    if (!this.override && isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
      if (i === keys.length - 1) {
    obj = obj.filter(function (n) {
      return n !== undefined
    })
      ret = ret[keys[0]].filter(function (v) {
        return v !== undefined
      })
DotObject.prototype.transfer = function (
  source,
  target,
  obj1,
  obj2,
  mods,
  merge
) {
    this.set(
      target,
      _process(this.pick(source, obj1, true), mods),
      obj2,
      merge
    )
    this.set(
      target,
        JSON.parse(JSON.stringify(this.pick(source, obj1, false))),
      ),
      obj2,
      merge
    )
    if (i === keys.length - 1) {
 *    "some": {
 *      "thing": "else"
 *    }
 *    "some.thing": "name"
  Object.keys(recipe).forEach(
    function (key) {
      this.set(recipe[key], this.pick(key, obj), tgt)
    }.bind(this)
  )
  Object.keys(obj).forEach(
    function (key) {
      var index = isArray && this.useBrackets ? '[' + key + ']' : key
      if (
        ((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))
        }
        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)
  )
DotObject.dot = wrap('dot');
['override', 'overwrite'].forEach(function (prop) {
});
['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {
const blacklist = ['__proto__', 'prototype', 'constructor']


  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
    obj[k] = obj[k] || (this.useArray && isIndex(a[0]) ? [] : {})
    if (!this.override && isArrayOrObject(obj[k]) && !isEmptyObject(obj[k])) {
      if (i === keys.length - 1) {
    obj = obj.filter(function (n) {
      return n !== undefined
    })
      ret = ret[keys[0]].filter(function (v) {
        return v !== undefined
      })
DotObject.prototype.transfer = function (
  source,
  target,
  obj1,
  obj2,
  mods,
  merge
) {
    this.set(
      target,
      _process(this.pick(source, obj1, true), mods),
      obj2,
      merge
    )
    this.set(
      target,
        JSON.parse(JSON.stringify(this.pick(source, obj1, false))),
      ),
      obj2,
      merge
    )
    if (i === keys.length - 1) {
 *    "some": {
 *      "thing": "else"
 *    }
 *    "some.thing": "name"
  Object.keys(recipe).forEach(
    function (key) {
      this.set(recipe[key], this.pick(key, obj), tgt)
    }.bind(this)
  )
  Object.keys(obj).forEach(
    function (key) {
      var index = isArray && this.useBrackets ? '[' + key + ']' : key
      if (
        ((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))
        }
        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)
  )
DotObject.dot = wrap('dot');
['override', 'overwrite'].forEach(function (prop) {
});
['useArray', 'keepArray', 'useBrackets'].forEach(function (prop) {

  describe('Refuse to update __proto__', function () {
    var obj = { path: [] }

    ;(() => Dot.set('path[0].__proto__.toString', 'test', obj)).should.throw(/Refusing to update/)
  })

  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/)
  })
        fields: [
          {
            subfield: 'value'
          },
          {
            subfield: 'value1'
          }
        ]

  it('cannot set __proto__ property', function () {
    (() => Dot.str('__proto__.toString', 'hi', {})).should.throw(
      /Refusing to update/
    );
    ({}.toString().should.deepEqual('[object Object]'))
  })