Skip to content

Commit 0f5338c

Browse files
authored
Merge branch 'main' into PYTHON-5040
2 parents 789b8aa + 48238a1 commit 0f5338c

4 files changed

Lines changed: 31 additions & 0 deletions

File tree

bson/json_util.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,13 @@ def _parse_binary(doc: Any, json_options: JSONOptions) -> Union[Binary, uuid.UUI
788788

789789

790790
def _parse_timestamp(doc: Any, dummy0: Any) -> Timestamp:
791+
if len(doc) != 1:
792+
raise TypeError(f"Bad $timestamp, extra field(s): {doc}")
791793
tsp = doc["$timestamp"]
794+
if not isinstance(tsp, Mapping):
795+
raise TypeError(f'$timestamp value must be a document with "t" and "i" components: {doc}')
796+
if set(tsp) != {"t", "i"}:
797+
raise TypeError(f'$timestamp must include exactly "t" and "i" components: {doc}')
792798
return Timestamp(tsp["t"], tsp["i"])
793799

794800

doc/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ PyMongo 4.18 brings a number of changes including:
2525
the bytes remaining in the array now raises
2626
:class:`~bson.errors.InvalidBSON` instead of reading past the end of the
2727
buffer.
28+
- Fixed :func:`bson.json_util.loads` to reject ``$timestamp`` values containing
29+
fields other than ``t`` and ``i``.
2830

2931
Changes in Version 4.17.0 (2026/04/20)
3032
--------------------------------------

doc/contributors.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,3 +109,4 @@ The following is a list of people who have contributed to
109109
- Noah Stapp (NoahStapp)
110110
- Cal Jacobson (cj81499)
111111
- Sophia Yang (sophiayangDB)
112+
- Madan Kumar (winklemad)

test/test_json_util.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,28 @@ def test_timestamp(self):
405405
self.assertEqual(dct, rtdct)
406406
self.assertEqual('{"ts": {"$timestamp": {"t": 4, "i": 13}}}', res)
407407

408+
def test_timestamp_with_invalid_fields(self):
409+
invalid_values = [
410+
'{"t": 4, "i": 13, "extra": 1}',
411+
'{"t": 4, "unexpected": 13}',
412+
]
413+
for value in invalid_values:
414+
with self.subTest(value=value):
415+
with self.assertRaisesRegex(
416+
TypeError, r'\$timestamp must include exactly "t" and "i" components'
417+
):
418+
json_util.loads(f'{{"ts": {{"$timestamp": {value}}}}}')
419+
420+
def test_timestamp_with_extra_wrapper_fields(self):
421+
with self.assertRaisesRegex(TypeError, r"Bad \$timestamp, extra field\(s\)"):
422+
json_util.loads('{"ts": {"$timestamp": {"t": 4, "i": 13}, "extra": 1}}')
423+
424+
def test_timestamp_with_non_document_value(self):
425+
for value in ('["t", "i"]', '"ti"', "5"):
426+
with self.subTest(value=value):
427+
with self.assertRaisesRegex(TypeError, r"\$timestamp value must be a document"):
428+
json_util.loads(f'{{"ts": {{"$timestamp": {value}}}}}')
429+
408430
def test_uuid_default(self):
409431
# Cannot directly encode native UUIDs with the default
410432
# uuid_representation.

0 commit comments

Comments
 (0)