https://jira.mongodb.org/browse/PYTHON-532

Short summary:

Step 1. Use Mongo as WEB SCALE DOCUMENT STORE OF CHOICE LOL
Step 2. Assume basic engineering principles applied throughout due to
HEAVY MARKETING SUGGESTING AWESOMENESS.
Step 3. Spend 6 months fighting plebbery across the spectrum, mostly
succeed.
Step 4. NIGHT BEFORE INVESTOR DEMO, TRY UPLOADING SOME DATA WITH
"{$ref: '#/mongodb/plebtastic'"
Step 5. LOL WTF?!?!? PYMONGO CRASH?? :OOO LOOOL WEBSCALE
Step 6. It's 4am now. STILL INVESTIGATING
b4cb9be0 pymongo/_cbsonmodule.c (Mike Dirolf 2009-11-10 14:54:39 -0500
1196) /* Decoding for DBRefs */
Oh Mike!!!


3. ADD process_dbrefs=False TO ALL THE DRIVERS

To reproduce:
- in mongo shell:
db.python532.insert({x : {"$ref" : "whatever"} });
- in python shell
import pymongo
pymongo.MongoClient().test.python532.find_one()

Fix:
https://github.com/mongodb/mongo-python-driver/commit/a060c15ef87e0f0e72974c7c0e57fe811bbd06a2

BTW can someone from 10gen contact me so we can start doing the CVEs
for MongoDB properly? Thanks.

- -- 
Kurt Seifried Red Hat Security Response Team (SRT)

bson/__init__.py View file @ a060c15
@@ -150,7 +150,7 @@ def _get_object(data, position, as_class, tz_aware, uuid_subtype):
     object = _elements_to_dict(encoded, as_class, tz_aware, uuid_subtype)
     position += obj_size
     if "$ref" in object:
-        return (DBRef(object.pop("$ref"), object.pop("$id"),
+        return (DBRef(object.pop("$ref"), object.pop("$id", None),
                       object.pop("$db", None), object), position)
     return object, position


bson/_cbsonmodule.c View file @ a060c15
@@ -1202,8 +1202,14 @@ static PyObject* get_value(PyObject* self, const char* buffer, int* position,
 
                 Py_INCREF(collection);
                 PyDict_DelItemString(value, "$ref");
-                Py_INCREF(id);
-                PyDict_DelItemString(value, "$id");
+
+                if (id == NULL) {
+                    id = Py_None;
+                    Py_INCREF(id);
+                } else {
+                    Py_INCREF(id);
+                    PyDict_DelItemString(value, "$id");
+                }
 
                 if (database == NULL) {
                     database = Py_None;


test/test_collection.py View file @ a060c15
@@ -30,6 +30,7 @@
 
 from bson.binary import Binary, UUIDLegacy, OLD_UUID_SUBTYPE, UUID_SUBTYPE
 from bson.code import Code
+from bson.dbref import DBRef
 from bson.objectid import ObjectId
 from bson.py3compat import b
 from bson.son import SON
@@ -1675,6 +1676,31 @@ def test_bad_encode(self):
         self.assertRaises(InvalidDocument, c.save, {"x": c})
         warnings.simplefilter("default")
 
+    def test_bad_dbref(self):
+        c = self.db.test
+        c.drop()
+
+        # Incomplete DBRefs.
+        self.assertRaises(
+            InvalidDocument,
+            c.insert, {'ref': {'$ref': 'collection'}})
+
+        self.assertRaises(
+            InvalidDocument,
+            c.insert, {'ref': {'$id': ObjectId()}})
+
+        ref_only = {'ref': {'$ref': 'collection'}}
+        id_only = {'ref': {'$id': ObjectId()}}
+
+        # Force insert of ref without $id.
+        c.insert(ref_only, check_keys=False)
+        self.assertEqual(DBRef('collection', id=None), c.find_one()['ref'])
+        c.drop()
+
+        # DBRef without $ref is decoded as normal subdocument.
+        c.insert(id_only, check_keys=False)
+        self.assertEqual(id_only, c.find_one())
+
     def test_as_class(self):
         c = self.db.test
         c.drop()