diff --git a/lib/datastore.js b/lib/datastore.js index 4adba0a..28ab4a4 100644 --- a/lib/datastore.js +++ b/lib/datastore.js @@ -105,7 +105,6 @@ Datastore.prototype.insert = function (newDoc, cb) { */ Datastore.match = function (obj, query) { var match = true - //, queryKeys = Object.keys(query) , i, k; Object.keys(query).forEach(function (k) { @@ -223,14 +222,18 @@ Datastore.prototype.update = function (query, newDoc, options, cb) { }); } , function () { // Perform the update - self.data.forEach(function (d) { - if (Datastore.match(d, query) && (multi || numReplaced === 0)) { - numReplaced += 1; - newData.push(model.modify(d, newDoc)); - } else { - newData.push(d); - } - }); + try { + self.data.forEach(function (d) { + if (Datastore.match(d, query) && (multi || numReplaced === 0)) { + numReplaced += 1; + newData.push(model.modify(d, newDoc)); + } else { + newData.push(d); + } + }); + } catch (err) { + return callback(err); + } self.persistWholeDatabase(newData, function (err) { if (err) { return callback(err); } diff --git a/lib/model.js b/lib/model.js index cc40126..46893fb 100644 --- a/lib/model.js +++ b/lib/model.js @@ -7,6 +7,22 @@ var dateToJSON = function () { return { $$date: this.getTime() }; } , originalDateToJSON = Date.prototype.toJSON + +/** + * Check a key, throw an error if the key is non valid + * @param {String} k key + * @param {Model} v value, needed to treat the Date edge case + * Non-treatable edge case here: if part of the object if of the form { $$date: number } + * Its serialized-then-deserialized version it will transformed into a Date object + * But you really need to want it to trigger such behaviour, even when warned not to use '$' at the beginning of the field names... + */ +function checkKey (k, v) { + if (k[0] === '$' && !(k === '$$date' && typeof v === 'number')) { + throw 'Keys cannot begin with the $ character'; + } +} + + /** * Serialize an object to be persisted to a one-line string * Accepted primitive types: Number, String, Boolean, Date, null @@ -19,10 +35,7 @@ function serialize (obj) { Date.prototype.toJSON = dateToJSON; res = JSON.stringify(obj, function (k, v) { - // Non-treatable edge case here: if part of the object if of the form { $$date: number } - // Its serialized-then-deserialized version it will transformed into a Date object - // But you really need to want it to trigger such behaviour, even when warned not to use '$' at the beginning of the field names... - if (k[0] === '$' && !(k === '$$date' && typeof v === 'number')) { throw 'Keys cannot begin with the $ character'; } + checkKey(k, v); if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null) { return v; } //if (v && v.constructor && v.constructor.name === 'Date') { console.log("==============="); return { $$date: v.toString() }; } @@ -75,6 +88,7 @@ function deepCopy (obj) { if (typeof obj === 'object') { res = {}; Object.keys(obj).forEach(function (k) { + checkKey(k, obj[k]); res[k] = deepCopy(obj[k]); }); return res; diff --git a/test/db.test.js b/test/db.test.js index 4d5a8df..61c4967 100644 --- a/test/db.test.js +++ b/test/db.test.js @@ -422,6 +422,15 @@ describe('Database', function () { }); }); + it('Cannot perform update if the new document contains a field beginning by $', function (done) { + d.insert({ something: 'yup' }, function () { + d.update({}, { boom: { $badfield: 5 } }, { multi: false }, function (err) { + assert.isDefined(err); + done(); + }); + }); + }); + }); // ==== End of 'Update' ==== // diff --git a/test/model.test.js b/test/model.test.js index f31e0ae..914c934 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -157,7 +157,15 @@ describe('Model', function () { res.subobj.b.should.equal('c'); }); - + it('Will throw an error if obj contains a field beginning by the $ sign', function () { + (function () { + model.deepCopy({ $something: true }); + }).should.throw(); + + (function () { + model.deepCopy({ something: true, another: { $badfield: 'rrr' } }); + }).should.throw(); + }); }); // ==== End of 'Deep copying' ==== //