CVE-2021-25912: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') (CWE-1321)
value: Prototype pollution vulnerability in 'dotty' versions 0.0.1 through 0.1.0 allows attackers to cause a denial of service and may lead to remote code execution.
CWE description: The product receives input from an upstream component that specifies attributes that are to be initialized or updated in an object, but it does not properly control modifications of attributes of the object prototype.
CVSS v3 base score: 9.8
Published: 2021-02-02T19:15Z
Repository: https://github.com/deoxxa/dotty
Language: JavaScript
Files changed: docs/lib/index.html, lib/index.js, package-lock.json, test/exists-test.js, test/get-test.js, test/put-test.js, test/remove-test.js, test/removesearch-test.js, test/search-test.js, test/security-test.js
Commit date: 2021-01-25 12:20:41 -0600
Commit message: Fix prototype pollution (#26)


Diff: diff --git a/docs/lib/index.html b/docs/lib/index.html
index 6aa9938..5303ef7 100644
--- a/docs/lib/index.html
+++ b/docs/lib/index.html
@@ -3,11 +3,11 @@
 <html>
 <head>
   <title>index.js</title>
   <meta http-equiv="content-type" content="text/html; charset=UTF-8">
   <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
-  <link rel="stylesheet" media="all" href="..\docco.css" />
+  <link rel="stylesheet" media="all" href="../docco.css" />
 </head>
 <body>
   <div id="container">
     <div id="background"></div>
     
diff --git a/docs/public/fonts/aller-bold.eot b/docs/public/fonts/aller-bold.eot
old mode 100755
new mode 100644
diff --git a/docs/public/fonts/aller-bold.ttf b/docs/public/fonts/aller-bold.ttf
old mode 100755
new mode 100644
diff --git a/docs/public/fonts/aller-bold.woff b/docs/public/fonts/aller-bold.woff
old mode 100755
new mode 100644
diff --git a/docs/public/fonts/aller-light.eot b/docs/public/fonts/aller-light.eot
old mode 100755
new mode 100644
diff --git a/docs/public/fonts/aller-light.ttf b/docs/public/fonts/aller-light.ttf
old mode 100755
new mode 100644
diff --git a/docs/public/fonts/aller-light.woff b/docs/public/fonts/aller-light.woff
old mode 100755
new mode 100644
diff --git a/docs/public/fonts/roboto-black.eot b/docs/public/fonts/roboto-black.eot
old mode 100644
new mode 100755
diff --git a/docs/public/fonts/roboto-black.ttf b/docs/public/fonts/roboto-black.ttf
old mode 100644
new mode 100755
diff --git a/docs/public/fonts/roboto-black.woff b/docs/public/fonts/roboto-black.woff
old mode 100644
new mode 100755
diff --git a/lib/index.js b/lib/index.js
index cfc8062..c87cb22 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -11,11 +11,11 @@
 // `["an", "array"]`.
 //
 // Returns `true` if the path can be completely resolved, `false` otherwise.
 //
 
-var exists = module.exports.exists = function exists(object, path) {
+var exists = (module.exports.exists = function exists(object, path) {
   if (typeof path === "string") {
     path = path.split(".");
   }
 
   if (!(path instanceof Array) || path.length === 0) {
@@ -33,23 +33,23 @@ var exists = module.exports.exists = function exists(object, path) {
   if (path.length === 0) {
     return Object.hasOwnProperty.apply(object, [key]);
   } else {
     return exists(object[key], path);
   }
-};
+});
 
 //
 // These arguments are the same as those for `exists`.
 //
 // The return value, however, is the property you're trying to access, or
 // `undefined` if it can't be found. This means you won't be able to tell
-// the difference between an unresolved path and an undefined property, so you 
+// the difference between an unresolved path and an undefined property, so you
 // should not use `get` to check for the existence of a property. Use `exists`
 // instead.
 //
 
-var get = module.exports.get = function get(object, path) {
+var get = (module.exports.get = function get(object, path) {
   if (typeof path === "string") {
     path = path.split(".");
   }
 
   if (!(path instanceof Array) || path.length === 0) {
@@ -69,11 +69,11 @@ var get = module.exports.get = function get(object, path) {
   }
 
   if (path.length) {
     return get(object[key], path);
   }
-};
+});
 
 //
 // Arguments are similar to `exists` and `get`, with the exception that path
 // components are regexes with some special cases. If a path component is `"*"`
 // on its own, it'll be converted to `/.*/`.
@@ -83,11 +83,11 @@ var get = module.exports.get = function get(object, path) {
 //
 // If an action function is specified, that action will be applied to each
 // match. Action params are value, parent and key.
 //
 
-var search = module.exports.search = function search(object, path, action) {
+var search = (module.exports.search = function search(object, path, action) {
   if (typeof path === "string") {
     path = path.split(".");
   }
 
   if (!(path instanceof Array) || path.length === 0) {
@@ -109,33 +109,45 @@ var search = module.exports.search = function search(object, path, action) {
   if (typeof key === "string") {
     key = new RegExp(key);
   }
 
   if (path.length === 0) {
-    return Object.keys(object).filter(key.test.bind(key)).map(function(k) {
-      var value = object[k];
-      if(action){
-        action(value, object, k);
-      }
-      return value;
-    });
+    return Object.keys(object)
+      .filter(key.test.bind(key))
+      .map(function (k) {
+        var value = object[k];
+        if (action) {
+          action(value, object, k);
+        }
+        return value;
+      });
   } else {
-    return Array.prototype.concat.apply([], Object.keys(object).filter(key.test.bind(key)).map(function(k) { return search(object[k], path, action); }));
+    return Array.prototype.concat.apply(
+      [],
+      Object.keys(object)
+        .filter(key.test.bind(key))
+        .map(function (k) {
+          return search(object[k], path, action);
+        })
+    );
   }
-};
+});
 
 //
 // Perform a search and remove the matched keys.
 // The return value is the same object argument with modifications.
 //
 
-var removeSearch = module.exports.removeSearch = function removeSearch(object, path){
-  search(object, path, function(value, object, key){
+var removeSearch = (module.exports.removeSearch = function removeSearch(
+  object,
+  path
+) {
+  search(object, path, function (value, object, key) {
     delete object[key];
   });
   return object;
-};
+});
 
 //
 // The first two arguments for `put` are the same as `exists` and `get`.
 //
 // The third argument is a value to `put` at the `path` of the `object`.
@@ -145,27 +157,26 @@ var removeSearch = module.exports.removeSearch = function removeSearch(object, p
 //
 // The return value is `true` in the case that the value was `put`
 // successfully, or `false` otherwise.
 //
 
-var put = module.exports.put = function put(object, path, value) {
+var put = (module.exports.put = function put(object, path, value) {
   if (typeof path === "string") {
     path = path.split(".");
   }
 
   if (!(path instanceof Array) || path.length === 0) {
     return false;
   }
-  
+
   path = path.slice();
 
   var key = path.shift();
 
-  if (typeof object !== "object" || object === null) {
+  if (typeof object !== "object" || object === null || key === "__proto__") {
     return false;
   }
-
   if (path.length === 0) {
     object[key] = value;
   } else {
     if (typeof object[key] === "undefined") {
       object[key] = {};
@@ -175,28 +186,28 @@ var put = module.exports.put = function put(object, path, value) {
       return false;
     }
 
     return put(object[key], path, value);
   }
-};
+});
 
 //
 // `remove` is like `put` in reverse!
 //
 // The return value is `true` in the case that the value existed and was removed
 // successfully, or `false` otherwise.
 //
 
-var remove = module.exports.remove = function remove(object, path, value) {
+var remove = (module.exports.remove = function remove(object, path, value) {
   if (typeof path === "string") {
     path = path.split(".");
   }
 
   if (!(path instanceof Array) || path.length === 0) {
     return false;
   }
-  
+
   path = path.slice();
 
   var key = path.shift();
 
   if (typeof object !== "object" || object === null) {
@@ -212,11 +223,11 @@ var remove = module.exports.remove = function remove(object, path, value) {
 
     return true;
   } else {
     return remove(object[key], path, value);
   }
-};
+});
 
 //
 // `deepKeys` creates a list of all possible key paths for a given object.
 //
 // The return value is always an array, the members of which are paths in array
@@ -229,11 +240,15 @@ var remove = module.exports.remove = function remove(object, path, value) {
 // ```
 //
 // *Note: this will probably explode on recursive objects. Be careful.*
 //
 
-var deepKeys = module.exports.deepKeys = function deepKeys(object, options, prefix) {
+var deepKeys = (module.exports.deepKeys = function deepKeys(
+  object,
+  options,
+  prefix
+) {
   options = options || {};
 
   if (typeof prefix === "undefined") {
     prefix = [];
   }
@@ -248,15 +263,23 @@ var deepKeys = module.exports.deepKeys = function deepKeys(object, options, pref
     if (!options.leavesOnly || typeof object[k] !== "object") {
       keys.push(prefix.concat([k]));
     }
 
     if (typeof object[k] === "object" && object[k] !== null) {
-      keys = keys.concat(deepKeys(object[k], {leavesOnly: options.leavesOnly}, prefix.concat([k])));
+      keys = keys.concat(
+        deepKeys(
+          object[k],
+          { leavesOnly: options.leavesOnly },
+          prefix.concat([k])
+        )
+      );
     }
   }
 
   if (options.asStrings) {
-    keys = keys.map(function(e) { return e.join("."); });
+    keys = keys.map(function (e) {
+      return e.join(".");
+    });
   }
 
   return keys;
-};
+});
diff --git a/package-lock.json b/package-lock.json
index f35046e..ee8763a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,15 +20,15 @@
       "version": "0.8.0",
       "resolved": "https://registry.npmjs.org/docco/-/docco-0.8.0.tgz",
       "integrity": "sha512-QcWBDnnGaT+rgC0wqynznXv0/4hd6nAFdWNs2fN4FvkH2yAnCYVeRU7GIZXNCeUQ955Lufq+TmZcSXiBa1cGQQ==",
       "dev": true,
       "requires": {
-        "commander": "2.14.1",
-        "fs-extra": "5.0.0",
-        "highlight.js": "9.12.0",
-        "marked": "0.3.16",
-        "underscore": "1.8.3"
+        "commander": ">= 0.5.2",
+        "fs-extra": ">= 0.6.0",
+        "highlight.js": ">= 8.0.x",
+        "marked": ">= 0.2.7",
+        "underscore": ">= 1.0.0"
       }
     },
     "eyes": {
       "version": "0.1.8",
       "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
@@ -39,34 +39,34 @@
       "version": "5.0.0",
       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
       "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
       "dev": true,
       "requires": {
-        "graceful-fs": "4.1.11",
-        "jsonfile": "4.0.0",
-        "universalify": "0.1.1"
+        "graceful-fs": "^4.1.2",
+        "jsonfile": "^4.0.0",
+        "universalify": "^0.1.0"
       }
     },
     "glob": {
       "version": "4.0.6",
       "resolved": "https://registry.npmjs.org/glob/-/glob-4.0.6.tgz",
       "integrity": "sha1-aVxQvdTi+1xdNwsJHziNNwfikac=",
       "dev": true,
       "requires": {
-        "graceful-fs": "3.0.11",
-        "inherits": "2.0.3",
-        "minimatch": "1.0.0",
-        "once": "1.4.0"
+        "graceful-fs": "^3.0.2",
+        "inherits": "2",
+        "minimatch": "^1.0.0",
+        "once": "^1.3.0"
       },
       "dependencies": {
         "graceful-fs": {
           "version": "3.0.11",
           "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz",
           "integrity": "sha1-dhPHeKGv6mLyXGMKCG1/Osu92Bg=",
           "dev": true,
           "requires": {
-            "natives": "1.1.1"
+            "natives": "^1.1.0"
           }
         }
       }
     },
     "graceful-fs": {
@@ -91,11 +91,11 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
       "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
       "dev": true,
       "requires": {
-        "graceful-fs": "4.1.11"
+        "graceful-fs": "^4.1.6"
       }
     },
     "lru-cache": {
       "version": "2.7.3",
       "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
@@ -112,12 +112,12 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-1.0.0.tgz",
       "integrity": "sha1-4N0hILSeG3JM6NcUxSCCKpQ4V20=",
       "dev": true,
       "requires": {
-        "lru-cache": "2.7.3",
-        "sigmund": "1.0.1"
+        "lru-cache": "2",
+        "sigmund": "~1.0.0"
       }
     },
     "natives": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.1.tgz",
@@ -128,11 +128,11 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
       "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
       "dev": true,
       "requires": {
-        "wrappy": "1.0.2"
+        "wrappy": "1"
       }
     },
     "sigmund": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
@@ -155,13 +155,13 @@
       "version": "0.8.1",
       "resolved": "https://registry.npmjs.org/vows/-/vows-0.8.1.tgz",
       "integrity": "sha1-4J6YjOWUygWgjXKrzKNOiNtVkTE=",
       "dev": true,
       "requires": {
-        "diff": "1.0.8",
-        "eyes": "0.1.8",
-        "glob": "4.0.6"
+        "diff": "~1.0.8",
+        "eyes": "~0.1.6",
+        "glob": "~4.0.6"
       }
     },
     "wrappy": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
diff --git a/test/exists-test.js b/test/exists-test.js
index e4abca7..ff08074 100644
--- a/test/exists-test.js
+++ b/test/exists-test.js
@@ -1,62 +1,65 @@
 var dotty = require("../lib/index"),
-    vows = require("vows"),
-    assert = require("assert");
+  vows = require("vows"),
+  assert = require("assert");
 
-vows.describe("exists").addBatch({
-  "A simple path": {
-    "as a string": {
-      topic: dotty.exists({"a": "b"}, "a"),
-      "should return true": function(res) {
-        assert.isTrue(res);
+vows
+  .describe("exists")
+  .addBatch({
+    "A simple path": {
+      "as a string": {
+        topic: dotty.exists({ a: "b" }, "a"),
+        "should return true": function (res) {
+          assert.isTrue(res);
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.exists({"a": "b"}, ["a"]),
-      "should return true": function(res) {
-        assert.isTrue(res);
+      "as an array": {
+        topic: dotty.exists({ a: "b" }, ["a"]),
+        "should return true": function (res) {
+          assert.isTrue(res);
+        },
       },
     },
-  },
-  "A two-level path": {
-    "as a string": {
-      topic: dotty.exists({"a": {"b": "c"}}, "a.b"),
-      "should return true": function(res) {
-        assert.isTrue(res);
+    "A two-level path": {
+      "as a string": {
+        topic: dotty.exists({ a: { b: "c" } }, "a.b"),
+        "should return true": function (res) {
+          assert.isTrue(res);
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.exists({"a": {"b": "c"}}, ["a", "b"]),
-      "should return true": function(res) {
-        assert.isTrue(res);
+      "as an array": {
+        topic: dotty.exists({ a: { b: "c" } }, ["a", "b"]),
+        "should return true": function (res) {
+          assert.isTrue(res);
+        },
       },
     },
-  },
-  "An unresolved path": {
-    "as a string": {
-      topic: dotty.exists({"a": {"b": "c"}}, "a.x"),
-      "should return false": function(res) {
-        assert.isFalse(res);
+    "An unresolved path": {
+      "as a string": {
+        topic: dotty.exists({ a: { b: "c" } }, "a.x"),
+        "should return false": function (res) {
+          assert.isFalse(res);
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.exists({"a": {"b": "c"}}, ["a", "x"]),
-      "should return false": function(res) {
-        assert.isFalse(res);
+      "as an array": {
+        topic: dotty.exists({ a: { b: "c" } }, ["a", "x"]),
+        "should return false": function (res) {
+          assert.isFalse(res);
+        },
       },
     },
-  },
-  "A property which is literally undefined, but with a resolved path": {
-    "as a string": {
-      topic: dotty.exists({"a": {"b": undefined}}, "a.b"),
-      "should return true": function(res) {
-        assert.isTrue(res);
+    "A property which is literally undefined, but with a resolved path": {
+      "as a string": {
+        topic: dotty.exists({ a: { b: undefined } }, "a.b"),
+        "should return true": function (res) {
+          assert.isTrue(res);
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.exists({"a": {"b": undefined}}, ["a", "b"]),
-      "should return true": function(res) {
-        assert.isTrue(res);
+      "as an array": {
+        topic: dotty.exists({ a: { b: undefined } }, ["a", "b"]),
+        "should return true": function (res) {
+          assert.isTrue(res);
+        },
       },
     },
-  },
-}).export(module);
+  })
+  .export(module);
diff --git a/test/get-test.js b/test/get-test.js
index e92adea..03ebdd6 100644
--- a/test/get-test.js
+++ b/test/get-test.js
@@ -1,62 +1,65 @@
 var dotty = require("../lib/index"),
-    vows = require("vows"),
-    assert = require("assert");
+  vows = require("vows"),
+  assert = require("assert");
 
-vows.describe("get").addBatch({
-  "A simple path": {
-    "as a string": {
-      topic: dotty.get({"a": "b"}, "a"),
-      "should return the correct value": function(res) {
-        assert.equal(res, "b");
+vows
+  .describe("get")
+  .addBatch({
+    "A simple path": {
+      "as a string": {
+        topic: dotty.get({ a: "b" }, "a"),
+        "should return the correct value": function (res) {
+          assert.equal(res, "b");
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.get({"a": "b"}, ["a"]),
-      "should return the correct value": function(res) {
-        assert.equal(res, "b");
+      "as an array": {
+        topic: dotty.get({ a: "b" }, ["a"]),
+        "should return the correct value": function (res) {
+          assert.equal(res, "b");
+        },
       },
     },
-  },
-  "A two-level path": {
-    "as a string": {
-      topic: dotty.get({"a": {"b": "c"}}, "a.b"),
-      "should return the correct value": function(res) {
-        assert.equal(res, "c");
+    "A two-level path": {
+      "as a string": {
+        topic: dotty.get({ a: { b: "c" } }, "a.b"),
+        "should return the correct value": function (res) {
+          assert.equal(res, "c");
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.get({"a": {"b": "c"}}, ["a", "b"]),
-      "should return the correct value": function(res) {
-        assert.equal(res, "c");
+      "as an array": {
+        topic: dotty.get({ a: { b: "c" } }, ["a", "b"]),
+        "should return the correct value": function (res) {
+          assert.equal(res, "c");
+        },
       },
     },
-  },
-  "An unresolved path": {
-    "as a string": {
-      topic: dotty.get({"a": {"b": "c"}}, "a.x"),
-      "should return undefined": function(res) {
-        assert.isUndefined(res);
+    "An unresolved path": {
+      "as a string": {
+        topic: dotty.get({ a: { b: "c" } }, "a.x"),
+        "should return undefined": function (res) {
+          assert.isUndefined(res);
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.get({"a": {"b": "c"}}, ["a", "x"]),
-      "should return undefined": function(res) {
-        assert.isUndefined(res);
+      "as an array": {
+        topic: dotty.get({ a: { b: "c" } }, ["a", "x"]),
+        "should return undefined": function (res) {
+          assert.isUndefined(res);
+        },
       },
     },
-  },
-  "A property which is literally undefined, but with a resolved path": {
-    "as a string": {
-      topic: dotty.get({"a": {"b": undefined}}, "a.b"),
-      "should return undefined": function(res) {
-        assert.isUndefined(res);
+    "A property which is literally undefined, but with a resolved path": {
+      "as a string": {
+        topic: dotty.get({ a: { b: undefined } }, "a.b"),
+        "should return undefined": function (res) {
+          assert.isUndefined(res);
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.get({"a": {"b": undefined}}, ["a", "b"]),
-      "should return undefined": function(res) {
-        assert.isUndefined(res);
+      "as an array": {
+        topic: dotty.get({ a: { b: undefined } }, ["a", "b"]),
+        "should return undefined": function (res) {
+          assert.isUndefined(res);
+        },
       },
     },
-  },
-}).export(module);
+  })
+  .export(module);
diff --git a/test/put-test.js b/test/put-test.js
index 05ab546..d16f541 100644
--- a/test/put-test.js
+++ b/test/put-test.js
@@ -1,48 +1,73 @@
 var dotty = require("../lib/index"),
-    vows = require("vows"),
-    assert = require("assert");
+  vows = require("vows"),
+  assert = require("assert");
 
-vows.describe("put").addBatch({
-  "A simple path": {
-    "as a string": {
-      topic: (function() { var x = {}; dotty.put(x, "a", "b"); return x; }()),
-      "should set the correct value": function(res) {
-        assert.equal(res.a, "b");
+vows
+  .describe("put")
+  .addBatch({
+    "A simple path": {
+      "as a string": {
+        topic: (function () {
+          var x = {};
+          dotty.put(x, "a", "b");
+          return x;
+        })(),
+        "should set the correct value": function (res) {
+          assert.equal(res.a, "b");
+        },
       },
-    },
-    "as an array": {
-      topic: (function() { var x = {}; dotty.put(x, ["a"], "b"); return x; }()),
-      "should set the correct value": function(res) {
-        assert.equal(res.a, "b");
+      "as an array": {
+        topic: (function () {
+          var x = {};
+          dotty.put(x, ["a"], "b");
+          return x;
+        })(),
+        "should set the correct value": function (res) {
+          assert.equal(res.a, "b");
+        },
       },
     },
-  },
-  "A two-level path": {
-    "as a string": {
-      topic: (function() { var x = {}; dotty.put(x, "a.b", "c"); return x; }()),
-      "should set the correct value": function(res) {
-        assert.equal(res.a.b, "c");
+    "A two-level path": {
+      "as a string": {
+        topic: (function () {
+          var x = {};
+          dotty.put(x, "a.b", "c");
+          return x;
+        })(),
+        "should set the correct value": function (res) {
+          assert.equal(res.a.b, "c");
+        },
       },
-    },
-    "as an array": {
-      topic: (function() { var x = {}; dotty.put(x, ["a", "b"], "c"); return x; }()),
-      "should set the correct value": function(res) {
-        assert.equal(res.a.b, "c");
+      "as an array": {
+        topic: (function () {
+          var x = {};
+          dotty.put(x, ["a", "b"], "c");
+          return x;
+        })(),
+        "should set the correct value": function (res) {
+          assert.equal(res.a.b, "c");
+        },
       },
     },
-  },
-  "An interrupted path": {
-    "as a string": {
-      topic: (function() { var x = {a: 1}; return dotty.put(x, "a.b", "c"); }()),
-      "should return false": function(res) {
-        assert.isFalse(res);
+    "An interrupted path": {
+      "as a string": {
+        topic: (function () {
+          var x = { a: 1 };
+          return dotty.put(x, "a.b", "c");
+        })(),
+        "should return false": function (res) {
+          assert.isFalse(res);
+        },
       },
-    },
-    "as an array": {
-      topic: (function() { var x = {a: 1}; return dotty.put(x, ["a", "b"], "c"); }()),
-      "should return false": function(res) {
-        assert.isFalse(res);
+      "as an array": {
+        topic: (function () {
+          var x = { a: 1 };
+          return dotty.put(x, ["a", "b"], "c");
+        })(),
+        "should return false": function (res) {
+          assert.isFalse(res);
+        },
       },
     },
-  },
-}).export(module);
+  })
+  .export(module);
diff --git a/test/remove-test.js b/test/remove-test.js
index 22eb13e..d23ea1e 100644
--- a/test/remove-test.js
+++ b/test/remove-test.js
@@ -1,48 +1,73 @@
 var dotty = require("../lib/index"),
-    vows = require("vows"),
-    assert = require("assert");
+  vows = require("vows"),
+  assert = require("assert");
 
-vows.describe("remove").addBatch({
-  "A simple path": {
-    "as a string": {
-      topic: (function() { var x = {a: 1}; dotty.remove(x, "a"); return x; }()),
-      "should remove the property": function(res) {
-        assert.isUndefined(res.a);
+vows
+  .describe("remove")
+  .addBatch({
+    "A simple path": {
+      "as a string": {
+        topic: (function () {
+          var x = { a: 1 };
+          dotty.remove(x, "a");
+          return x;
+        })(),
+        "should remove the property": function (res) {
+          assert.isUndefined(res.a);
+        },
       },
-    },
-    "as an array": {
-      topic: (function() { var x = {a: 1}; dotty.remove(x, ["a"]); return x; }()),
-      "should remove the property": function(res) {
-        assert.isUndefined(res.a);
+      "as an array": {
+        topic: (function () {
+          var x = { a: 1 };
+          dotty.remove(x, ["a"]);
+          return x;
+        })(),
+        "should remove the property": function (res) {
+          assert.isUndefined(res.a);
+        },
       },
     },
-  },
-  "A two-level path": {
-    "as a string": {
-      topic: (function() { var x = {a: {b: 1}}; dotty.remove(x, "a.b"); return x; }()),
-      "should remove the property": function(res) {
-        assert.isUndefined(res.a.b);
+    "A two-level path": {
+      "as a string": {
+        topic: (function () {
+          var x = { a: { b: 1 } };
+          dotty.remove(x, "a.b");
+          return x;
+        })(),
+        "should remove the property": function (res) {
+          assert.isUndefined(res.a.b);
+        },
       },
-    },
-    "as an array": {
-      topic: (function() { var x = {a: {b: 1}}; dotty.remove(x, ["a", "b"]); return x; }()),
-      "should remove the property": function(res) {
-        assert.isUndefined(res.a.b);
+      "as an array": {
+        topic: (function () {
+          var x = { a: { b: 1 } };
+          dotty.remove(x, ["a", "b"]);
+          return x;
+        })(),
+        "should remove the property": function (res) {
+          assert.isUndefined(res.a.b);
+        },
       },
     },
-  },
-  "An interrupted path": {
-    "as a string": {
-      topic: (function() { var x = {a: 1}; return dotty.remove(x, "a.b"); }()),
-      "should return false": function(res) {
-        assert.isFalse(res);
+    "An interrupted path": {
+      "as a string": {
+        topic: (function () {
+          var x = { a: 1 };
+          return dotty.remove(x, "a.b");
+        })(),
+        "should return false": function (res) {
+          assert.isFalse(res);
+        },
       },
-    },
-    "as an array": {
-      topic: (function() { var x = {a: 1}; return dotty.remove(x, ["a", "b"]); }()),
-      "should return false": function(res) {
-        assert.isFalse(res);
+      "as an array": {
+        topic: (function () {
+          var x = { a: 1 };
+          return dotty.remove(x, ["a", "b"]);
+        })(),
+        "should return false": function (res) {
+          assert.isFalse(res);
+        },
       },
     },
-  },
-}).export(module);
+  })
+  .export(module);
diff --git a/test/removesearch-test.js b/test/removesearch-test.js
index 4e5e7ca..a01be3d 100644
--- a/test/removesearch-test.js
+++ b/test/removesearch-test.js
@@ -1,145 +1,171 @@
 var dotty = require("../lib/index"),
-    vows = require("vows"),
-    assert = require("assert");
+  vows = require("vows"),
+  assert = require("assert");
 
-vows.describe("search").addBatch({
-  "A simple path": {
-    "as a string": {
-      topic: dotty.removeSearch({"a": "b"}, "a"),
-      "should return an object with no keys": function(res) {
-        assert.isObject(res);
-        assert.equal(Object.keys(res).length, 0, "Object should have no keys");
+vows
+  .describe("search")
+  .addBatch({
+    "A simple path": {
+      "as a string": {
+        topic: dotty.removeSearch({ a: "b" }, "a"),
+        "should return an object with no keys": function (res) {
+          assert.isObject(res);
+          assert.equal(
+            Object.keys(res).length,
+            0,
+            "Object should have no keys"
+          );
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.removeSearch({"a": "b"}, ["a"]),
-      "should return an object with no keys": function(res) {
-        assert.isObject(res);
-        assert.equal(Object.keys(res).length, 0, "Object should have no keys");
+      "as an array": {
+        topic: dotty.removeSearch({ a: "b" }, ["a"]),
+        "should return an object with no keys": function (res) {
+          assert.isObject(res);
+          assert.equal(
+            Object.keys(res).length,
+            0,
+            "Object should have no keys"
+          );
+        },
       },
-    },
-    "as an array with a regex": {
-      topic: dotty.removeSearch({"a": "b"}, [/a/]),
-      "should return an object with no keys": function(res) {
-        assert.isObject(res);
-        assert.equal(Object.keys(res).length, 0, "Object should have no keys");
+      "as an array with a regex": {
+        topic: dotty.removeSearch({ a: "b" }, [/a/]),
+        "should return an object with no keys": function (res) {
+          assert.isObject(res);
+          assert.equal(
+            Object.keys(res).length,
+            0,
+            "Object should have no keys"
+          );
+        },
       },
     },
-  },
-  "A two-level path": {
-    "as a string": {
-      topic: dotty.removeSearch({"a": {"b": "c"}}, "a.b"),
-      "should return an object with b removed": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.equal(Object.keys(res.a), 0, "a has no keys");
+    "A two-level path": {
+      "as a string": {
+        topic: dotty.removeSearch({ a: { b: "c" } }, "a.b"),
+        "should return an object with b removed": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.equal(Object.keys(res.a), 0, "a has no keys");
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.removeSearch({"a": {"b": "c"}}, ["a", "b"]),
-      "should return an object with b removed": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.equal(Object.keys(res.a), 0, "a has no keys");
+      "as an array": {
+        topic: dotty.removeSearch({ a: { b: "c" } }, ["a", "b"]),
+        "should return an object with b removed": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.equal(Object.keys(res.a), 0, "a has no keys");
+        },
       },
-    },
-    "as an array with regexes": {
-      topic: dotty.removeSearch({"a": {"b": "c"}}, [/a/, /b/]),
-      "should return an object with b removed": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.equal(Object.keys(res.a), 0, "a has no keys");
+      "as an array with regexes": {
+        topic: dotty.removeSearch({ a: { b: "c" } }, [/a/, /b/]),
+        "should return an object with b removed": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.equal(Object.keys(res.a), 0, "a has no keys");
+        },
       },
     },
-  },
-  "A two-level path matching two values": {
-    "as a string": {
-      topic: dotty.removeSearch({"a": {"b": "c", "d": "e"}}, "a.*"),
-      "should return an object with b & d removed": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.equal(Object.keys(res.a), 0, "a has no keys");
+    "A two-level path matching two values": {
+      "as a string": {
+        topic: dotty.removeSearch({ a: { b: "c", d: "e" } }, "a.*"),
+        "should return an object with b & d removed": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.equal(Object.keys(res.a), 0, "a has no keys");
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.removeSearch({"a": {"b": "c", "d": "e"}}, ["a", "*"]),
-      "should return an object with b & d removed": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.equal(Object.keys(res.a), 0, "a has no keys");
+      "as an array": {
+        topic: dotty.removeSearch({ a: { b: "c", d: "e" } }, ["a", "*"]),
+        "should return an object with b & d removed": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.equal(Object.keys(res.a), 0, "a has no keys");
+        },
       },
-    },
-    "as an array with regexes": {
-      topic: dotty.removeSearch({"a": {"b": "c", "d": "e"}}, [/a/, /.*/]),
-      "should return an object with b & d removed": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.equal(Object.keys(res.a), 0, "a has no keys");
+      "as an array with regexes": {
+        topic: dotty.removeSearch({ a: { b: "c", d: "e" } }, [/a/, /.*/]),
+        "should return an object with b & d removed": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.equal(Object.keys(res.a), 0, "a has no keys");
+        },
       },
     },
-  },
-  "A three-level mixed path matching two values": {
-    "as a string": {
-      topic: dotty.removeSearch({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, "a.*.x"),
-      "should return an object with x removed": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.isObject(res.a.b);
-        assert.isObject(res.a.c);
-        assert.equal(Object.keys(res.a.b), 0, "b has no keys");
-        assert.equal(Object.keys(res.a.c), 0, "c has no keys");
+    "A three-level mixed path matching two values": {
+      "as a string": {
+        topic: dotty.removeSearch(
+          { a: { b: { x: "y" }, c: { x: "z" } } },
+          "a.*.x"
+        ),
+        "should return an object with x removed": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.isObject(res.a.b);
+          assert.isObject(res.a.c);
+          assert.equal(Object.keys(res.a.b), 0, "b has no keys");
+          assert.equal(Object.keys(res.a.c), 0, "c has no keys");
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.removeSearch({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, ["a", "*", "x"]),
-      "should return an object with x removed": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.isObject(res.a.b);
-        assert.isObject(res.a.c);
-        assert.equal(Object.keys(res.a.b), 0, "b has no keys");
-        assert.equal(Object.keys(res.a.c), 0, "c has no keys");
+      "as an array": {
+        topic: dotty.removeSearch({ a: { b: { x: "y" }, c: { x: "z" } } }, [
+          "a",
+          "*",
+          "x",
+        ]),
+        "should return an object with x removed": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.isObject(res.a.b);
+          assert.isObject(res.a.c);
+          assert.equal(Object.keys(res.a.b), 0, "b has no keys");
+          assert.equal(Object.keys(res.a.c), 0, "c has no keys");
+        },
       },
-    },
-    "as an array with regexes": {
-      topic: dotty.removeSearch({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, [/a/, /.*/, /x/]),
-      "should return an object with x removed": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.isObject(res.a.b);
-        assert.isObject(res.a.c);
-        assert.equal(Object.keys(res.a.b), 0, "b has no keys");
-        assert.equal(Object.keys(res.a.c), 0, "c has no keys");
+      "as an array with regexes": {
+        topic: dotty.removeSearch({ a: { b: { x: "y" }, c: { x: "z" } } }, [
+          /a/,
+          /.*/,
+          /x/,
+        ]),
+        "should return an object with x removed": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.isObject(res.a.b);
+          assert.isObject(res.a.c);
+          assert.equal(Object.keys(res.a.b), 0, "b has no keys");
+          assert.equal(Object.keys(res.a.c), 0, "c has no keys");
+        },
       },
     },
-  },
-  "An unresolved path": {
-    "as a string": {
-      topic: dotty.removeSearch({"a": {"b": "c"}}, "a.x"),
-      "should return unmodified object": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.isString(res.a.b);
-        assert.equal(res.a.b, 'c');
+    "An unresolved path": {
+      "as a string": {
+        topic: dotty.removeSearch({ a: { b: "c" } }, "a.x"),
+        "should return unmodified object": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.isString(res.a.b);
+          assert.equal(res.a.b, "c");
+        },
       },
-    },
-    "as an array": {
-      topic: dotty.removeSearch({"a": {"b": "c"}}, ["a", "x"]),
-      "should return unmodified object": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.isString(res.a.b);
-        assert.equal(res.a.b, 'c');
+      "as an array": {
+        topic: dotty.removeSearch({ a: { b: "c" } }, ["a", "x"]),
+        "should return unmodified object": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.isString(res.a.b);
+          assert.equal(res.a.b, "c");
+        },
       },
-    },
-    "as an array with regexes": {
-      topic: dotty.removeSearch({"a": {"b": "c"}}, [/a/, /x/]),
-      "should return unmodified object": function(res) {
-        assert.isObject(res);
-        assert.isObject(res.a);
-        assert.isString(res.a.b);
-        assert.equal(res.a.b, 'c');
+      "as an array with regexes": {
+        topic: dotty.removeSearch({ a: { b: "c" } }, [/a/, /x/]),
+        "should return unmodified object": function (res) {
+          assert.isObject(res);
+          assert.isObject(res.a);
+          assert.isString(res.a.b);
+          assert.equal(res.a.b, "c");
+        },
       },
     },
-  },
-}).export(module);
+  })
+  .export(module);
diff --git a/test/search-test.js b/test/search-test.js
index fab47a9..13f06dc 100644
--- a/test/search-test.js
+++ b/test/search-test.js
@@ -1,193 +1,204 @@
 var dotty = require("../lib/index"),
-    vows = require("vows"),
-    assert = require("assert");
+  vows = require("vows"),
+  assert = require("assert");
 
-vows.describe("search").addBatch({
-  "A simple path": {
-    "as a string": {
-      topic: dotty.search({"a": "b"}, "a"),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return one value": function(res) {
-        assert.equal(res.length, 1);
-      },
-      "should return the correct value": function(res) {
-        assert.equal(res[0], "b");
-      },
-    },
-    "as an array": {
-      topic: dotty.search({"a": "b"}, ["a"]),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return one value": function(res) {
-        assert.equal(res.length, 1);
-      },
-      "should return the correct value": function(res) {
-        assert.equal(res[0], "b");
-      },
-    },
-    "as an array with a regex": {
-      topic: dotty.search({"a": "b"}, [/a/]),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return one value": function(res) {
-        assert.equal(res.length, 1);
-      },
-      "should return the correct value": function(res) {
-        assert.equal(res[0], "b");
-      },
-    },
-  },
-  "A two-level path": {
-    "as a string": {
-      topic: dotty.search({"a": {"b": "c"}}, "a.b"),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return one value": function(res) {
-        assert.equal(res.length, 1);
-      },
-      "should return the correct value": function(res) {
-        assert.equal(res[0], "c");
-      },
-    },
-    "as an array": {
-      topic: dotty.search({"a": {"b": "c"}}, ["a", "b"]),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return one value": function(res) {
-        assert.equal(res.length, 1);
-      },
-      "should return the correct value": function(res) {
-        assert.equal(res[0], "c");
+vows
+  .describe("search")
+  .addBatch({
+    "A simple path": {
+      "as a string": {
+        topic: dotty.search({ a: "b" }, "a"),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return one value": function (res) {
+          assert.equal(res.length, 1);
+        },
+        "should return the correct value": function (res) {
+          assert.equal(res[0], "b");
+        },
+      },
+      "as an array": {
+        topic: dotty.search({ a: "b" }, ["a"]),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return one value": function (res) {
+          assert.equal(res.length, 1);
+        },
+        "should return the correct value": function (res) {
+          assert.equal(res[0], "b");
+        },
+      },
+      "as an array with a regex": {
+        topic: dotty.search({ a: "b" }, [/a/]),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return one value": function (res) {
+          assert.equal(res.length, 1);
+        },
+        "should return the correct value": function (res) {
+          assert.equal(res[0], "b");
+        },
       },
     },
-    "as an array with regexes": {
-      topic: dotty.search({"a": {"b": "c"}}, [/a/, /b/]),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return one value": function(res) {
-        assert.equal(res.length, 1);
-      },
-      "should return the correct value": function(res) {
-        assert.equal(res[0], "c");
+    "A two-level path": {
+      "as a string": {
+        topic: dotty.search({ a: { b: "c" } }, "a.b"),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return one value": function (res) {
+          assert.equal(res.length, 1);
+        },
+        "should return the correct value": function (res) {
+          assert.equal(res[0], "c");
+        },
+      },
+      "as an array": {
+        topic: dotty.search({ a: { b: "c" } }, ["a", "b"]),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return one value": function (res) {
+          assert.equal(res.length, 1);
+        },
+        "should return the correct value": function (res) {
+          assert.equal(res[0], "c");
+        },
+      },
+      "as an array with regexes": {
+        topic: dotty.search({ a: { b: "c" } }, [/a/, /b/]),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return one value": function (res) {
+          assert.equal(res.length, 1);
+        },
+        "should return the correct value": function (res) {
+          assert.equal(res[0], "c");
+        },
       },
     },
-  },
-  "A two-level path matching two values": {
-    "as a string": {
-      topic: dotty.search({"a": {"b": "c", "d": "e"}}, "a.*"),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return two values": function(res) {
-        assert.equal(res.length, 2);
-      },
-      "should return the correct value": function(res) {
-        assert.equal(res[0], "c");
-        assert.equal(res[1], "e");
+    "A two-level path matching two values": {
+      "as a string": {
+        topic: dotty.search({ a: { b: "c", d: "e" } }, "a.*"),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return two values": function (res) {
+          assert.equal(res.length, 2);
+        },
+        "should return the correct value": function (res) {
+          assert.equal(res[0], "c");
+          assert.equal(res[1], "e");
+        },
+      },
+      "as an array": {
+        topic: dotty.search({ a: { b: "c", d: "e" } }, ["a", "*"]),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return two values": function (res) {
+          assert.equal(res.length, 2);
+        },
+        "should return the correct values": function (res) {
+          assert.equal(res[0], "c");
+          assert.equal(res[1], "e");
+        },
+      },
+      "as an array with regexes": {
+        topic: dotty.search({ a: { b: "c", d: "e" } }, [/a/, /.*/]),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return two values": function (res) {
+          assert.equal(res.length, 2);
+        },
+        "should return the correct values": function (res) {
+          assert.equal(res[0], "c");
+          assert.equal(res[1], "e");
+        },
       },
     },
-    "as an array": {
-      topic: dotty.search({"a": {"b": "c", "d": "e"}}, ["a", "*"]),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return two values": function(res) {
-        assert.equal(res.length, 2);
-      },
-      "should return the correct values": function(res) {
-        assert.equal(res[0], "c");
-        assert.equal(res[1], "e");
+    "A three-level mixed path matching two values": {
+      "as a string": {
+        topic: dotty.search({ a: { b: { x: "y" }, c: { x: "z" } } }, "a.*.x"),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return two values": function (res) {
+          assert.equal(res.length, 2);
+        },
+        "should return the correct value": function (res) {
+          assert.equal(res[0], "y");
+          assert.equal(res[1], "z");
+        },
+      },
+      "as an array": {
+        topic: dotty.search({ a: { b: { x: "y" }, c: { x: "z" } } }, [
+          "a",
+          "*",
+          "x",
+        ]),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return two values": function (res) {
+          assert.equal(res.length, 2);
+        },
+        "should return the correct values": function (res) {
+          assert.equal(res[0], "y");
+          assert.equal(res[1], "z");
+        },
+      },
+      "as an array with regexes": {
+        topic: dotty.search({ a: { b: { x: "y" }, c: { x: "z" } } }, [
+          /a/,
+          /.*/,
+          /x/,
+        ]),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return two values": function (res) {
+          assert.equal(res.length, 2);
+        },
+        "should return the correct values": function (res) {
+          assert.equal(res[0], "y");
+          assert.equal(res[1], "z");
+        },
       },
     },
-    "as an array with regexes": {
-      topic: dotty.search({"a": {"b": "c", "d": "e"}}, [/a/, /.*/]),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return two values": function(res) {
-        assert.equal(res.length, 2);
-      },
-      "should return the correct values": function(res) {
-        assert.equal(res[0], "c");
-        assert.equal(res[1], "e");
-      },
-    },
-  },
-  "A three-level mixed path matching two values": {
-    "as a string": {
-      topic: dotty.search({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, "a.*.x"),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return two values": function(res) {
-        assert.equal(res.length, 2);
-      },
-      "should return the correct value": function(res) {
-        assert.equal(res[0], "y");
-        assert.equal(res[1], "z");
-      },
-    },
-    "as an array": {
-      topic: dotty.search({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, ["a", "*", "x"]),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return two values": function(res) {
-        assert.equal(res.length, 2);
-      },
-      "should return the correct values": function(res) {
-        assert.equal(res[0], "y");
-        assert.equal(res[1], "z");
-      },
-    },
-    "as an array with regexes": {
-      topic: dotty.search({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, [/a/, /.*/, /x/]),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return two values": function(res) {
-        assert.equal(res.length, 2);
-      },
-      "should return the correct values": function(res) {
-        assert.equal(res[0], "y");
-        assert.equal(res[1], "z");
-      },
-    },
-  },
-  "An unresolved path": {
-    "as a string": {
-      topic: dotty.search({"a": {"b": "c"}}, "a.x"),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return zero values": function(res) {
-        assert.equal(res.length, 0);
-      },
-    },
-    "as an array": {
-      topic: dotty.search({"a": {"b": "c"}}, ["a", "x"]),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return zero values": function(res) {
-        assert.equal(res.length, 0);
-      },
-    },
-    "as an array with regexes": {
-      topic: dotty.search({"a": {"b": "c"}}, [/a/, /x/]),
-      "should return an array": function(res) {
-        assert.isArray(res);
-      },
-      "should return zero values": function(res) {
-        assert.equal(res.length, 0);
+    "An unresolved path": {
+      "as a string": {
+        topic: dotty.search({ a: { b: "c" } }, "a.x"),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return zero values": function (res) {
+          assert.equal(res.length, 0);
+        },
+      },
+      "as an array": {
+        topic: dotty.search({ a: { b: "c" } }, ["a", "x"]),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return zero values": function (res) {
+          assert.equal(res.length, 0);
+        },
+      },
+      "as an array with regexes": {
+        topic: dotty.search({ a: { b: "c" } }, [/a/, /x/]),
+        "should return an array": function (res) {
+          assert.isArray(res);
+        },
+        "should return zero values": function (res) {
+          assert.equal(res.length, 0);
+        },
       },
     },
-  },
-}).export(module);
+  })
+  .export(module);
diff --git a/test/security-test.js b/test/security-test.js
new file mode 100644
index 0000000..10a1670
--- /dev/null
+++ b/test/security-test.js
@@ -0,0 +1,29 @@
+var dotty = require("../lib/index"),
+  vows = require("vows"),
+  assert = require("assert");
+
+vows
+  .describe("security")
+  .addBatch({
+    "When we attempt to update the prototype": {
+      topic() {
+        var obj = {};
+        dotty.put(obj, "__proto__.polluted", "Muhahahaha");
+        return obj;
+      },
+      "it should not update": function (res) {
+        assert.equal(res.polluted, undefined);
+      },
+    },
+    "When we attempt to update the constructor prototype": {
+      topic() {
+        var obj = {};
+        dotty.put(obj, "constructor.prototype.polluted", "Muhahahaha");
+        return obj;
+      },
+      "it should not update": function (res) {
+        assert.equal(res.polluted, undefined);
+      },
+    },
+  })
+  .export(module);

Vulnerable code:   <link rel="stylesheet" media="all" href="..\docco.css" />
var exists = module.exports.exists = function exists(object, path) {
};
// the difference between an unresolved path and an undefined property, so you 
var get = module.exports.get = function get(object, path) {
};
var search = module.exports.search = function search(object, path, action) {
    return Object.keys(object).filter(key.test.bind(key)).map(function(k) {
      var value = object[k];
      if(action){
        action(value, object, k);
      }
      return value;
    });
    return Array.prototype.concat.apply([], Object.keys(object).filter(key.test.bind(key)).map(function(k) { return search(object[k], path, action); }));
};
var removeSearch = module.exports.removeSearch = function removeSearch(object, path){
  search(object, path, function(value, object, key){
};
var put = module.exports.put = function put(object, path, value) {
  
  if (typeof object !== "object" || object === null) {

};
var remove = module.exports.remove = function remove(object, path, value) {
  
};
var deepKeys = module.exports.deepKeys = function deepKeys(object, options, prefix) {
      keys = keys.concat(deepKeys(object[k], {leavesOnly: options.leavesOnly}, prefix.concat([k])));
    keys = keys.map(function(e) { return e.join("."); });
};
        "commander": "2.14.1",
        "fs-extra": "5.0.0",
        "highlight.js": "9.12.0",
        "marked": "0.3.16",
        "underscore": "1.8.3"
        "graceful-fs": "4.1.11",
        "jsonfile": "4.0.0",
        "universalify": "0.1.1"
        "graceful-fs": "3.0.11",
        "inherits": "2.0.3",
        "minimatch": "1.0.0",
        "once": "1.4.0"
            "natives": "1.1.1"
        "graceful-fs": "4.1.11"
        "lru-cache": "2.7.3",
        "sigmund": "1.0.1"
        "wrappy": "1.0.2"
        "diff": "1.0.8",
        "eyes": "0.1.8",
        "glob": "4.0.6"
    vows = require("vows"),
    assert = require("assert");
vows.describe("exists").addBatch({
  "A simple path": {
    "as a string": {
      topic: dotty.exists({"a": "b"}, "a"),
      "should return true": function(res) {
        assert.isTrue(res);
    },
    "as an array": {
      topic: dotty.exists({"a": "b"}, ["a"]),
      "should return true": function(res) {
        assert.isTrue(res);
  },
  "A two-level path": {
    "as a string": {
      topic: dotty.exists({"a": {"b": "c"}}, "a.b"),
      "should return true": function(res) {
        assert.isTrue(res);
    },
    "as an array": {
      topic: dotty.exists({"a": {"b": "c"}}, ["a", "b"]),
      "should return true": function(res) {
        assert.isTrue(res);
  },
  "An unresolved path": {
    "as a string": {
      topic: dotty.exists({"a": {"b": "c"}}, "a.x"),
      "should return false": function(res) {
        assert.isFalse(res);
    },
    "as an array": {
      topic: dotty.exists({"a": {"b": "c"}}, ["a", "x"]),
      "should return false": function(res) {
        assert.isFalse(res);
  },
  "A property which is literally undefined, but with a resolved path": {
    "as a string": {
      topic: dotty.exists({"a": {"b": undefined}}, "a.b"),
      "should return true": function(res) {
        assert.isTrue(res);
    },
    "as an array": {
      topic: dotty.exists({"a": {"b": undefined}}, ["a", "b"]),
      "should return true": function(res) {
        assert.isTrue(res);
  },
}).export(module);
    vows = require("vows"),
    assert = require("assert");
vows.describe("get").addBatch({
  "A simple path": {
    "as a string": {
      topic: dotty.get({"a": "b"}, "a"),
      "should return the correct value": function(res) {
        assert.equal(res, "b");
    },
    "as an array": {
      topic: dotty.get({"a": "b"}, ["a"]),
      "should return the correct value": function(res) {
        assert.equal(res, "b");
  },
  "A two-level path": {
    "as a string": {
      topic: dotty.get({"a": {"b": "c"}}, "a.b"),
      "should return the correct value": function(res) {
        assert.equal(res, "c");
    },
    "as an array": {
      topic: dotty.get({"a": {"b": "c"}}, ["a", "b"]),
      "should return the correct value": function(res) {
        assert.equal(res, "c");
  },
  "An unresolved path": {
    "as a string": {
      topic: dotty.get({"a": {"b": "c"}}, "a.x"),
      "should return undefined": function(res) {
        assert.isUndefined(res);
    },
    "as an array": {
      topic: dotty.get({"a": {"b": "c"}}, ["a", "x"]),
      "should return undefined": function(res) {
        assert.isUndefined(res);
  },
  "A property which is literally undefined, but with a resolved path": {
    "as a string": {
      topic: dotty.get({"a": {"b": undefined}}, "a.b"),
      "should return undefined": function(res) {
        assert.isUndefined(res);
    },
    "as an array": {
      topic: dotty.get({"a": {"b": undefined}}, ["a", "b"]),
      "should return undefined": function(res) {
        assert.isUndefined(res);
  },
}).export(module);
    vows = require("vows"),
    assert = require("assert");
vows.describe("put").addBatch({
  "A simple path": {
    "as a string": {
      topic: (function() { var x = {}; dotty.put(x, "a", "b"); return x; }()),
      "should set the correct value": function(res) {
        assert.equal(res.a, "b");
    },
    "as an array": {
      topic: (function() { var x = {}; dotty.put(x, ["a"], "b"); return x; }()),
      "should set the correct value": function(res) {
        assert.equal(res.a, "b");
  },
  "A two-level path": {
    "as a string": {
      topic: (function() { var x = {}; dotty.put(x, "a.b", "c"); return x; }()),
      "should set the correct value": function(res) {
        assert.equal(res.a.b, "c");
    },
    "as an array": {
      topic: (function() { var x = {}; dotty.put(x, ["a", "b"], "c"); return x; }()),
      "should set the correct value": function(res) {
        assert.equal(res.a.b, "c");
  },
  "An interrupted path": {
    "as a string": {
      topic: (function() { var x = {a: 1}; return dotty.put(x, "a.b", "c"); }()),
      "should return false": function(res) {
        assert.isFalse(res);
    },
    "as an array": {
      topic: (function() { var x = {a: 1}; return dotty.put(x, ["a", "b"], "c"); }()),
      "should return false": function(res) {
        assert.isFalse(res);
  },
}).export(module);
    vows = require("vows"),
    assert = require("assert");
vows.describe("remove").addBatch({
  "A simple path": {
    "as a string": {
      topic: (function() { var x = {a: 1}; dotty.remove(x, "a"); return x; }()),
      "should remove the property": function(res) {
        assert.isUndefined(res.a);
    },
    "as an array": {
      topic: (function() { var x = {a: 1}; dotty.remove(x, ["a"]); return x; }()),
      "should remove the property": function(res) {
        assert.isUndefined(res.a);
  },
  "A two-level path": {
    "as a string": {
      topic: (function() { var x = {a: {b: 1}}; dotty.remove(x, "a.b"); return x; }()),
      "should remove the property": function(res) {
        assert.isUndefined(res.a.b);
    },
    "as an array": {
      topic: (function() { var x = {a: {b: 1}}; dotty.remove(x, ["a", "b"]); return x; }()),
      "should remove the property": function(res) {
        assert.isUndefined(res.a.b);
  },
  "An interrupted path": {
    "as a string": {
      topic: (function() { var x = {a: 1}; return dotty.remove(x, "a.b"); }()),
      "should return false": function(res) {
        assert.isFalse(res);
    },
    "as an array": {
      topic: (function() { var x = {a: 1}; return dotty.remove(x, ["a", "b"]); }()),
      "should return false": function(res) {
        assert.isFalse(res);
  },
}).export(module);
    vows = require("vows"),
    assert = require("assert");
vows.describe("search").addBatch({
  "A simple path": {
    "as a string": {
      topic: dotty.removeSearch({"a": "b"}, "a"),
      "should return an object with no keys": function(res) {
        assert.isObject(res);
        assert.equal(Object.keys(res).length, 0, "Object should have no keys");
    },
    "as an array": {
      topic: dotty.removeSearch({"a": "b"}, ["a"]),
      "should return an object with no keys": function(res) {
        assert.isObject(res);
        assert.equal(Object.keys(res).length, 0, "Object should have no keys");
    },
    "as an array with a regex": {
      topic: dotty.removeSearch({"a": "b"}, [/a/]),
      "should return an object with no keys": function(res) {
        assert.isObject(res);
        assert.equal(Object.keys(res).length, 0, "Object should have no keys");
  },
  "A two-level path": {
    "as a string": {
      topic: dotty.removeSearch({"a": {"b": "c"}}, "a.b"),
      "should return an object with b removed": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.equal(Object.keys(res.a), 0, "a has no keys");
    },
    "as an array": {
      topic: dotty.removeSearch({"a": {"b": "c"}}, ["a", "b"]),
      "should return an object with b removed": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.equal(Object.keys(res.a), 0, "a has no keys");
    },
    "as an array with regexes": {
      topic: dotty.removeSearch({"a": {"b": "c"}}, [/a/, /b/]),
      "should return an object with b removed": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.equal(Object.keys(res.a), 0, "a has no keys");
  },
  "A two-level path matching two values": {
    "as a string": {
      topic: dotty.removeSearch({"a": {"b": "c", "d": "e"}}, "a.*"),
      "should return an object with b & d removed": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.equal(Object.keys(res.a), 0, "a has no keys");
    },
    "as an array": {
      topic: dotty.removeSearch({"a": {"b": "c", "d": "e"}}, ["a", "*"]),
      "should return an object with b & d removed": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.equal(Object.keys(res.a), 0, "a has no keys");
    },
    "as an array with regexes": {
      topic: dotty.removeSearch({"a": {"b": "c", "d": "e"}}, [/a/, /.*/]),
      "should return an object with b & d removed": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.equal(Object.keys(res.a), 0, "a has no keys");
  },
  "A three-level mixed path matching two values": {
    "as a string": {
      topic: dotty.removeSearch({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, "a.*.x"),
      "should return an object with x removed": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.isObject(res.a.b);
        assert.isObject(res.a.c);
        assert.equal(Object.keys(res.a.b), 0, "b has no keys");
        assert.equal(Object.keys(res.a.c), 0, "c has no keys");
    },
    "as an array": {
      topic: dotty.removeSearch({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, ["a", "*", "x"]),
      "should return an object with x removed": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.isObject(res.a.b);
        assert.isObject(res.a.c);
        assert.equal(Object.keys(res.a.b), 0, "b has no keys");
        assert.equal(Object.keys(res.a.c), 0, "c has no keys");
    },
    "as an array with regexes": {
      topic: dotty.removeSearch({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, [/a/, /.*/, /x/]),
      "should return an object with x removed": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.isObject(res.a.b);
        assert.isObject(res.a.c);
        assert.equal(Object.keys(res.a.b), 0, "b has no keys");
        assert.equal(Object.keys(res.a.c), 0, "c has no keys");
  },
  "An unresolved path": {
    "as a string": {
      topic: dotty.removeSearch({"a": {"b": "c"}}, "a.x"),
      "should return unmodified object": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.isString(res.a.b);
        assert.equal(res.a.b, 'c');
    },
    "as an array": {
      topic: dotty.removeSearch({"a": {"b": "c"}}, ["a", "x"]),
      "should return unmodified object": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.isString(res.a.b);
        assert.equal(res.a.b, 'c');
    },
    "as an array with regexes": {
      topic: dotty.removeSearch({"a": {"b": "c"}}, [/a/, /x/]),
      "should return unmodified object": function(res) {
        assert.isObject(res);
        assert.isObject(res.a);
        assert.isString(res.a.b);
        assert.equal(res.a.b, 'c');
  },
}).export(module);
    vows = require("vows"),
    assert = require("assert");
vows.describe("search").addBatch({
  "A simple path": {
    "as a string": {
      topic: dotty.search({"a": "b"}, "a"),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return one value": function(res) {
        assert.equal(res.length, 1);
      },
      "should return the correct value": function(res) {
        assert.equal(res[0], "b");
      },
    },
    "as an array": {
      topic: dotty.search({"a": "b"}, ["a"]),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return one value": function(res) {
        assert.equal(res.length, 1);
      },
      "should return the correct value": function(res) {
        assert.equal(res[0], "b");
      },
    },
    "as an array with a regex": {
      topic: dotty.search({"a": "b"}, [/a/]),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return one value": function(res) {
        assert.equal(res.length, 1);
      },
      "should return the correct value": function(res) {
        assert.equal(res[0], "b");
      },
    },
  },
  "A two-level path": {
    "as a string": {
      topic: dotty.search({"a": {"b": "c"}}, "a.b"),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return one value": function(res) {
        assert.equal(res.length, 1);
      },
      "should return the correct value": function(res) {
        assert.equal(res[0], "c");
      },
    },
    "as an array": {
      topic: dotty.search({"a": {"b": "c"}}, ["a", "b"]),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return one value": function(res) {
        assert.equal(res.length, 1);
      },
      "should return the correct value": function(res) {
        assert.equal(res[0], "c");
    "as an array with regexes": {
      topic: dotty.search({"a": {"b": "c"}}, [/a/, /b/]),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return one value": function(res) {
        assert.equal(res.length, 1);
      },
      "should return the correct value": function(res) {
        assert.equal(res[0], "c");
  },
  "A two-level path matching two values": {
    "as a string": {
      topic: dotty.search({"a": {"b": "c", "d": "e"}}, "a.*"),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return two values": function(res) {
        assert.equal(res.length, 2);
      },
      "should return the correct value": function(res) {
        assert.equal(res[0], "c");
        assert.equal(res[1], "e");
    "as an array": {
      topic: dotty.search({"a": {"b": "c", "d": "e"}}, ["a", "*"]),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return two values": function(res) {
        assert.equal(res.length, 2);
      },
      "should return the correct values": function(res) {
        assert.equal(res[0], "c");
        assert.equal(res[1], "e");
    "as an array with regexes": {
      topic: dotty.search({"a": {"b": "c", "d": "e"}}, [/a/, /.*/]),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return two values": function(res) {
        assert.equal(res.length, 2);
      },
      "should return the correct values": function(res) {
        assert.equal(res[0], "c");
        assert.equal(res[1], "e");
      },
    },
  },
  "A three-level mixed path matching two values": {
    "as a string": {
      topic: dotty.search({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, "a.*.x"),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return two values": function(res) {
        assert.equal(res.length, 2);
      },
      "should return the correct value": function(res) {
        assert.equal(res[0], "y");
        assert.equal(res[1], "z");
      },
    },
    "as an array": {
      topic: dotty.search({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, ["a", "*", "x"]),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return two values": function(res) {
        assert.equal(res.length, 2);
      },
      "should return the correct values": function(res) {
        assert.equal(res[0], "y");
        assert.equal(res[1], "z");
      },
    },
    "as an array with regexes": {
      topic: dotty.search({"a": {"b": {"x": "y"}, "c": {"x": "z"}}}, [/a/, /.*/, /x/]),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return two values": function(res) {
        assert.equal(res.length, 2);
      },
      "should return the correct values": function(res) {
        assert.equal(res[0], "y");
        assert.equal(res[1], "z");
      },
    },
  },
  "An unresolved path": {
    "as a string": {
      topic: dotty.search({"a": {"b": "c"}}, "a.x"),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return zero values": function(res) {
        assert.equal(res.length, 0);
      },
    },
    "as an array": {
      topic: dotty.search({"a": {"b": "c"}}, ["a", "x"]),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return zero values": function(res) {
        assert.equal(res.length, 0);
      },
    },
    "as an array with regexes": {
      topic: dotty.search({"a": {"b": "c"}}, [/a/, /x/]),
      "should return an array": function(res) {
        assert.isArray(res);
      },
      "should return zero values": function(res) {
        assert.equal(res.length, 0);
  },
}).export(module);
Fixed code:   <link rel="stylesheet" media="all" href="../docco.css" />
var exists = (module.exports.exists = function exists(object, path) {
});
// the difference between an unresolved path and an undefined property, so you
var get = (module.exports.get = function get(object, path) {
});
var search = (module.exports.search = function search(object, path, action) {
    return Object.keys(object)
      .filter(key.test.bind(key))
      .map(function (k) {
        var value = object[k];
        if (action) {
          action(value, object, k);
        }
        return value;
      });
    return Array.prototype.concat.apply(
      [],
      Object.keys(object)
        .filter(key.test.bind(key))
        .map(function (k) {
          return search(object[k], path, action);
        })
    );
});
var removeSearch = (module.exports.removeSearch = function removeSearch(
  object,
  path
) {
  search(object, path, function (value, object, key) {
});
var put = (module.exports.put = function put(object, path, value) {

  if (typeof object !== "object" || object === null || key === "__proto__") {
});
var remove = (module.exports.remove = function remove(object, path, value) {

});
var deepKeys = (module.exports.deepKeys = function deepKeys(
  object,
  options,
  prefix
) {
      keys = keys.concat(
        deepKeys(
          object[k],
          { leavesOnly: options.leavesOnly },
          prefix.concat([k])
        )
      );
    keys = keys.map(function (e) {
      return e.join(".");
    });
});
        "commander": ">= 0.5.2",
        "fs-extra": ">= 0.6.0",
        "highlight.js": ">= 8.0.x",
        "marked": ">= 0.2.7",
        "underscore": ">= 1.0.0"
        "graceful-fs": "^4.1.2",
        "jsonfile": "^4.0.0",
        "universalify": "^0.1.0"
        "graceful-fs": "^3.0.2",
        "inherits": "2",
        "minimatch": "^1.0.0",
        "once": "^1.3.0"
            "natives": "^1.1.0"
        "graceful-fs": "^4.1.6"
        "lru-cache": "2",
        "sigmund": "~1.0.0"
        "wrappy": "1"
        "diff": "~1.0.8",
        "eyes": "~0.1.6",
        "glob": "~4.0.6"
  vows = require("vows"),
  assert = require("assert");
vows
  .describe("exists")
  .addBatch({
    "A simple path": {
      "as a string": {
        topic: dotty.exists({ a: "b" }, "a"),
        "should return true": function (res) {
          assert.isTrue(res);
        },
      "as an array": {
        topic: dotty.exists({ a: "b" }, ["a"]),
        "should return true": function (res) {
          assert.isTrue(res);
        },
    "A two-level path": {
      "as a string": {
        topic: dotty.exists({ a: { b: "c" } }, "a.b"),
        "should return true": function (res) {
          assert.isTrue(res);
        },
      "as an array": {
        topic: dotty.exists({ a: { b: "c" } }, ["a", "b"]),
        "should return true": function (res) {
          assert.isTrue(res);
        },
    "An unresolved path": {
      "as a string": {
        topic: dotty.exists({ a: { b: "c" } }, "a.x"),
        "should return false": function (res) {
          assert.isFalse(res);
        },
      "as an array": {
        topic: dotty.exists({ a: { b: "c" } }, ["a", "x"]),
        "should return false": function (res) {
          assert.isFalse(res);
        },
    "A property which is literally undefined, but with a resolved path": {
      "as a string": {
        topic: dotty.exists({ a: { b: undefined } }, "a.b"),
        "should return true": function (res) {
          assert.isTrue(res);
        },
      "as an array": {
        topic: dotty.exists({ a: { b: undefined } }, ["a", "b"]),
        "should return true": function (res) {
          assert.isTrue(res);
        },
  })
  .export(module);
  vows = require("vows"),
  assert = require("assert");
vows
  .describe("get")
  .addBatch({
    "A simple path": {
      "as a string": {
        topic: dotty.get({ a: "b" }, "a"),
        "should return the correct value": function (res) {
          assert.equal(res, "b");
        },
      "as an array": {
        topic: dotty.get({ a: "b" }, ["a"]),
        "should return the correct value": function (res) {
          assert.equal(res, "b");
        },
    "A two-level path": {
      "as a string": {
        topic: dotty.get({ a: { b: "c" } }, "a.b"),
        "should return the correct value": function (res) {
          assert.equal(res, "c");
        },
      "as an array": {
        topic: dotty.get({ a: { b: "c" } }, ["a", "b"]),
        "should return the correct value": function (res) {
          assert.equal(res, "c");
        },
    "An unresolved path": {
      "as a string": {
        topic: dotty.get({ a: { b: "c" } }, "a.x"),
        "should return undefined": function (res) {
          assert.isUndefined(res);
        },
      "as an array": {
        topic: dotty.get({ a: { b: "c" } }, ["a", "x"]),
        "should return undefined": function (res) {
          assert.isUndefined(res);
        },
    "A property which is literally undefined, but with a resolved path": {
      "as a string": {
        topic: dotty.get({ a: { b: undefined } }, "a.b"),
        "should return undefined": function (res) {
          assert.isUndefined(res);
        },
      "as an array": {
        topic: dotty.get({ a: { b: undefined } }, ["a", "b"]),
        "should return undefined": function (res) {
          assert.isUndefined(res);
        },
  })
  .export(module);
  vows = require("vows"),
  assert = require("assert");
vows
  .describe("put")
  .addBatch({
    "A simple path": {
      "as a string": {
        topic: (function () {
          var x = {};
          dotty.put(x, "a", "b");
          return x;
        })(),
        "should set the correct value": function (res) {
          assert.equal(res.a, "b");
        },
      "as an array": {
        topic: (function () {
          var x = {};
          dotty.put(x, ["a"], "b");
          return x;
        })(),
        "should set the correct value": function (res) {
          assert.equal(res.a, "b");
        },
    "A two-level path": {
      "as a string": {
        topic: (function () {
          var x = {};
          dotty.put(x, "a.b", "c");
          return x;
        })(),
        "should set the correct value": function (res) {
          assert.equal(res.a.b, "c");
        },
      "as an array": {
        topic: (function () {
          var x = {};
          dotty.put(x, ["a", "b"], "c");
          return x;
        })(),
        "should set the correct value": function (res) {
          assert.equal(res.a.b, "c");
        },
    "An interrupted path": {
      "as a string": {
        topic: (function () {
          var x = { a: 1 };
          return dotty.put(x, "a.b", "c");
        })(),
        "should return false": function (res) {
          assert.isFalse(res);
        },
      "as an array": {
        topic: (function () {
          var x = { a: 1 };
          return dotty.put(x, ["a", "b"], "c");
        })(),
        "should return false": function (res) {
          assert.isFalse(res);
        },
  })
  .export(module);
  vows = require("vows"),
  assert = require("assert");
vows
  .describe("remove")
  .addBatch({
    "A simple path": {
      "as a string": {
        topic: (function () {
          var x = { a: 1 };
          dotty.remove(x, "a");
          return x;
        })(),
        "should remove the property": function (res) {
          assert.isUndefined(res.a);
        },
      "as an array": {
        topic: (function () {
          var x = { a: 1 };
          dotty.remove(x, ["a"]);
          return x;
        })(),
        "should remove the property": function (res) {
          assert.isUndefined(res.a);
        },
    "A two-level path": {
      "as a string": {
        topic: (function () {
          var x = { a: { b: 1 } };
          dotty.remove(x, "a.b");
          return x;
        })(),
        "should remove the property": function (res) {
          assert.isUndefined(res.a.b);
        },
      "as an array": {
        topic: (function () {
          var x = { a: { b: 1 } };
          dotty.remove(x, ["a", "b"]);
          return x;
        })(),
        "should remove the property": function (res) {
          assert.isUndefined(res.a.b);
        },
    "An interrupted path": {
      "as a string": {
        topic: (function () {
          var x = { a: 1 };
          return dotty.remove(x, "a.b");
        })(),
        "should return false": function (res) {
          assert.isFalse(res);
        },
      "as an array": {
        topic: (function () {
          var x = { a: 1 };
          return dotty.remove(x, ["a", "b"]);
        })(),
        "should return false": function (res) {
          assert.isFalse(res);
        },
  })
  .export(module);
  vows = require("vows"),
  assert = require("assert");
vows
  .describe("search")
  .addBatch({
    "A simple path": {
      "as a string": {
        topic: dotty.removeSearch({ a: "b" }, "a"),
        "should return an object with no keys": function (res) {
          assert.isObject(res);
          assert.equal(
            Object.keys(res).length,
            0,
            "Object should have no keys"
          );
        },
      "as an array": {
        topic: dotty.removeSearch({ a: "b" }, ["a"]),
        "should return an object with no keys": function (res) {
          assert.isObject(res);
          assert.equal(
            Object.keys(res).length,
            0,
            "Object should have no keys"
          );
        },
      "as an array with a regex": {
        topic: dotty.removeSearch({ a: "b" }, [/a/]),
        "should return an object with no keys": function (res) {
          assert.isObject(res);
          assert.equal(
            Object.keys(res).length,
            0,
            "Object should have no keys"
          );
        },
    "A two-level path": {
      "as a string": {
        topic: dotty.removeSearch({ a: { b: "c" } }, "a.b"),
        "should return an object with b removed": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.equal(Object.keys(res.a), 0, "a has no keys");
        },
      "as an array": {
        topic: dotty.removeSearch({ a: { b: "c" } }, ["a", "b"]),
        "should return an object with b removed": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.equal(Object.keys(res.a), 0, "a has no keys");
        },
      "as an array with regexes": {
        topic: dotty.removeSearch({ a: { b: "c" } }, [/a/, /b/]),
        "should return an object with b removed": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.equal(Object.keys(res.a), 0, "a has no keys");
        },
    "A two-level path matching two values": {
      "as a string": {
        topic: dotty.removeSearch({ a: { b: "c", d: "e" } }, "a.*"),
        "should return an object with b & d removed": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.equal(Object.keys(res.a), 0, "a has no keys");
        },
      "as an array": {
        topic: dotty.removeSearch({ a: { b: "c", d: "e" } }, ["a", "*"]),
        "should return an object with b & d removed": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.equal(Object.keys(res.a), 0, "a has no keys");
        },
      "as an array with regexes": {
        topic: dotty.removeSearch({ a: { b: "c", d: "e" } }, [/a/, /.*/]),
        "should return an object with b & d removed": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.equal(Object.keys(res.a), 0, "a has no keys");
        },
    "A three-level mixed path matching two values": {
      "as a string": {
        topic: dotty.removeSearch(
          { a: { b: { x: "y" }, c: { x: "z" } } },
          "a.*.x"
        ),
        "should return an object with x removed": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.isObject(res.a.b);
          assert.isObject(res.a.c);
          assert.equal(Object.keys(res.a.b), 0, "b has no keys");
          assert.equal(Object.keys(res.a.c), 0, "c has no keys");
        },
      "as an array": {
        topic: dotty.removeSearch({ a: { b: { x: "y" }, c: { x: "z" } } }, [
          "a",
          "*",
          "x",
        ]),
        "should return an object with x removed": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.isObject(res.a.b);
          assert.isObject(res.a.c);
          assert.equal(Object.keys(res.a.b), 0, "b has no keys");
          assert.equal(Object.keys(res.a.c), 0, "c has no keys");
        },
      "as an array with regexes": {
        topic: dotty.removeSearch({ a: { b: { x: "y" }, c: { x: "z" } } }, [
          /a/,
          /.*/,
          /x/,
        ]),
        "should return an object with x removed": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.isObject(res.a.b);
          assert.isObject(res.a.c);
          assert.equal(Object.keys(res.a.b), 0, "b has no keys");
          assert.equal(Object.keys(res.a.c), 0, "c has no keys");
        },
    "An unresolved path": {
      "as a string": {
        topic: dotty.removeSearch({ a: { b: "c" } }, "a.x"),
        "should return unmodified object": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.isString(res.a.b);
          assert.equal(res.a.b, "c");
        },
      "as an array": {
        topic: dotty.removeSearch({ a: { b: "c" } }, ["a", "x"]),
        "should return unmodified object": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.isString(res.a.b);
          assert.equal(res.a.b, "c");
        },
      "as an array with regexes": {
        topic: dotty.removeSearch({ a: { b: "c" } }, [/a/, /x/]),
        "should return unmodified object": function (res) {
          assert.isObject(res);
          assert.isObject(res.a);
          assert.isString(res.a.b);
          assert.equal(res.a.b, "c");
        },
  })
  .export(module);
  vows = require("vows"),
  assert = require("assert");
vows
  .describe("search")
  .addBatch({
    "A simple path": {
      "as a string": {
        topic: dotty.search({ a: "b" }, "a"),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return one value": function (res) {
          assert.equal(res.length, 1);
        },
        "should return the correct value": function (res) {
          assert.equal(res[0], "b");
        },
      },
      "as an array": {
        topic: dotty.search({ a: "b" }, ["a"]),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return one value": function (res) {
          assert.equal(res.length, 1);
        },
        "should return the correct value": function (res) {
          assert.equal(res[0], "b");
        },
      },
      "as an array with a regex": {
        topic: dotty.search({ a: "b" }, [/a/]),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return one value": function (res) {
          assert.equal(res.length, 1);
        },
        "should return the correct value": function (res) {
          assert.equal(res[0], "b");
        },
    "A two-level path": {
      "as a string": {
        topic: dotty.search({ a: { b: "c" } }, "a.b"),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return one value": function (res) {
          assert.equal(res.length, 1);
        },
        "should return the correct value": function (res) {
          assert.equal(res[0], "c");
        },
      },
      "as an array": {
        topic: dotty.search({ a: { b: "c" } }, ["a", "b"]),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return one value": function (res) {
          assert.equal(res.length, 1);
        },
        "should return the correct value": function (res) {
          assert.equal(res[0], "c");
        },
      },
      "as an array with regexes": {
        topic: dotty.search({ a: { b: "c" } }, [/a/, /b/]),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return one value": function (res) {
          assert.equal(res.length, 1);
        },
        "should return the correct value": function (res) {
          assert.equal(res[0], "c");
        },
    "A two-level path matching two values": {
      "as a string": {
        topic: dotty.search({ a: { b: "c", d: "e" } }, "a.*"),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return two values": function (res) {
          assert.equal(res.length, 2);
        },
        "should return the correct value": function (res) {
          assert.equal(res[0], "c");
          assert.equal(res[1], "e");
        },
      },
      "as an array": {
        topic: dotty.search({ a: { b: "c", d: "e" } }, ["a", "*"]),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return two values": function (res) {
          assert.equal(res.length, 2);
        },
        "should return the correct values": function (res) {
          assert.equal(res[0], "c");
          assert.equal(res[1], "e");
        },
      },
      "as an array with regexes": {
        topic: dotty.search({ a: { b: "c", d: "e" } }, [/a/, /.*/]),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return two values": function (res) {
          assert.equal(res.length, 2);
        },
        "should return the correct values": function (res) {
          assert.equal(res[0], "c");
          assert.equal(res[1], "e");
        },
    "A three-level mixed path matching two values": {
      "as a string": {
        topic: dotty.search({ a: { b: { x: "y" }, c: { x: "z" } } }, "a.*.x"),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return two values": function (res) {
          assert.equal(res.length, 2);
        },
        "should return the correct value": function (res) {
          assert.equal(res[0], "y");
          assert.equal(res[1], "z");
        },
      },
      "as an array": {
        topic: dotty.search({ a: { b: { x: "y" }, c: { x: "z" } } }, [
          "a",
          "*",
          "x",
        ]),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return two values": function (res) {
          assert.equal(res.length, 2);
        },
        "should return the correct values": function (res) {
          assert.equal(res[0], "y");
          assert.equal(res[1], "z");
        },
      },
      "as an array with regexes": {
        topic: dotty.search({ a: { b: { x: "y" }, c: { x: "z" } } }, [
          /a/,
          /.*/,
          /x/,
        ]),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return two values": function (res) {
          assert.equal(res.length, 2);
        },
        "should return the correct values": function (res) {
          assert.equal(res[0], "y");
          assert.equal(res[1], "z");
        },
    "An unresolved path": {
      "as a string": {
        topic: dotty.search({ a: { b: "c" } }, "a.x"),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return zero values": function (res) {
          assert.equal(res.length, 0);
        },
      },
      "as an array": {
        topic: dotty.search({ a: { b: "c" } }, ["a", "x"]),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return zero values": function (res) {
          assert.equal(res.length, 0);
        },
      },
      "as an array with regexes": {
        topic: dotty.search({ a: { b: "c" } }, [/a/, /x/]),
        "should return an array": function (res) {
          assert.isArray(res);
        },
        "should return zero values": function (res) {
          assert.equal(res.length, 0);
        },
  })
  .export(module);
var dotty = require("../lib/index"),
  vows = require("vows"),
  assert = require("assert");

vows
  .describe("security")
  .addBatch({
    "When we attempt to update the prototype": {
      topic() {
        var obj = {};
        dotty.put(obj, "__proto__.polluted", "Muhahahaha");
        return obj;
      },
      "it should not update": function (res) {
        assert.equal(res.polluted, undefined);
      },
    },
    "When we attempt to update the constructor prototype": {
      topic() {
        var obj = {};
        dotty.put(obj, "constructor.prototype.polluted", "Muhahahaha");
        return obj;
      },
      "it should not update": function (res) {
        assert.equal(res.polluted, undefined);
      },
    },
  })
  .export(module);