CVE-2021-25912 (JavaScript, published 2021-02-02T19:15Z)
Description: 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.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-1321 \u2014 Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
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.
Repository: https://github.com/deoxxa/dotty
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 message: Fix prototype pollution (#26)



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);