Collection mocks in meteor: how to predict what an update will do

I recently watched Pete Corey’s talk NoSQL Injection in Modern Web Applications from Crater Remote Conference. It’s a very educational talk which reminds you that just because you’re using noSQL doesn’t mean that your app is immune from various kinds of injection attacks.

One of the interesting points which he made was about the rename operator in mongo. The really interesting thing about the rename operator is that it affects a key other than the key it’s operating on. As an example `docs.update(_id, { $rename: { boringField: “isAdmin” } }` will modify the ‘isAdmin’ field and simply iterating through the keys in this modifier won’t make that obvious.

For a while now I’ve been using a meteor package we wrote called ‘collection-mocks’ to simulate a mongo operation & check what changed.

The collection mocks package uses mini-mongo under the hood to simulate the effect of mongo update & remove operations.

To use collection mocks add the useful:collection-mocks package to your meteor app and start using the mock method. The mock method is attached to Mongo.Collection.prototype so you can use it for any existing Mongo Collection you have.

var Books = new Mongo.Collection(‘books’);
var bookId = Books.insert({ name: 'Little Britches' });
var userProvidedUpdateOperation = {
$set: { author: 'Ralph Moody' }
};
var originalDoc = Books.findOne(bookId);
var changedDoc = Books.mock(bookId, userProvidedUpdateOperation);
// We can now compare the two documents to see what has changed.

Using the collection-mocks package allows us to inspect the changed document instead of the modifier object (which is much harder to reason about). If we consider only certain keys to be privileged we can simply check that none of those keys have been modified. Conversely we could go with a whitelisting approach & check that only non-privileged keys have been modified.

Here’s a simple method which checks to see that only whitelisted properties have been modified.

var allKeys = _.union(_.keys(originalDoc), _.keys(changedDoc));
var changedKeys = _.filter(allKeys, function (key) {
return !_.isEqual(originalDoc[key], changedDoc[key]);
});
var allowedKeys = ["name", "author"];
var disallowedKeys = _.difference(changedKeys, allowedKeys);
if (disallowedKeys.length)
throw new Error("Permission denied.");

This doesn’t solve all no-sql injection attacks, but it’s a very handy way to check if a given field was modified. We’ve also found it extremely useful in before & after hooks, although at last check it wasn’t compatible with the most popular hooks package (matb33:collection-hooks).

Take it for a spin & let us know what you think!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.