diff --git a/lib/json0.js b/lib/json0.js index 4c92407..4547e83 100644 --- a/lib/json0.js +++ b/lib/json0.js @@ -34,6 +34,8 @@ var isObject = function(obj) { return (!!obj) && (obj.constructor === Object); }; +var hasOwn = Object.hasOwn || Object.prototype.hasOwnProperty.call; + /** * Clones the passed object using JSON serialization (which is slow). * @@ -57,7 +59,7 @@ var json = { // You can register another OT type as a subtype in a JSON document using // the following function. This allows another type to handle certain // operations instead of the builtin JSON type. -var subtypes = {}; +var subtypes = Object.create(null); json.registerSubtype = function(subtype) { subtypes[subtype.name] = subtype; }; @@ -177,6 +179,9 @@ json.apply = function(snapshot, op) { for (var j = 0; j < c.p.length; j++) { var p = c.p[j]; + if (p in elem && !hasOwn(elem, p)) + throw new Error('Path invalid'); + parent = elem; parentKey = key; elem = elem[key]; diff --git a/test/json0.coffee b/test/json0.coffee index 05294e6..85cbf28 100644 --- a/test/json0.coffee +++ b/test/json0.coffee @@ -452,6 +452,16 @@ genTests = (type) -> assert.throws -> type.apply {x:'a'}, [{p:['x'], oi: 'c', od: 'b'}] assert.throws -> type.apply {x:'a'}, [{p:['x'], oi: 'b'}] + it 'disallows reassignment of special JS property names', -> + assert.throws -> type.apply {x:'a'}, [{p:['__proto__'], oi:'oops'}] + assert.throws -> type.apply {x:{y:'a'}}, [{p:['x', '__proto__'], oi:'oops'}] + assert.throws -> type.apply {x:'a'}, [{p:['constructor'], oi:'oops'}] + assert.throws -> type.apply {x:{y:'a'}}, [{p:['x', 'constructor'], oi:'oops'}] + + it 'disallows modification of prototype property objects', -> + obj = {x:'a'} + assert.throws -> type.apply obj, [{p:['toString', 'name'], oi:'oops'}] + it 'throws when the insertion key is a number', -> assert.throws -> type.apply {'1':'a'}, [{p:[2], oi: 'a'}]