@@ -1756,6 +1756,32 @@ def test_array_of_documents_to_buffer(self):
17561756 with self .assertRaises (InvalidBSON ):
17571757 _array_of_documents_to_buffer (buf )
17581758
1759+ def test_array_of_documents_to_buffer_rejects_oversized_element (self ):
1760+ # Regression test for the bound check in
1761+ # _cbson_array_of_documents_to_buffer: an embedded document whose
1762+ # declared length exceeds the bytes remaining in the array document
1763+ # must raise InvalidBSON before any copy, rather than reading out of
1764+ # bounds (CWE-125).
1765+ doc = dict (a = 1 )
1766+ valid = encode ({"0" : doc })
1767+ # A well-formed buffer is unaffected by the guard.
1768+ self .assertEqual (_array_of_documents_to_buffer (valid ), encode (doc ))
1769+ # The first embedded document starts after the array header:
1770+ # 4-byte array length + 1-byte element type (0x03) + "0\x00" key = 7.
1771+ # Its leading 4 bytes are the embedded document's declared length.
1772+ offset = 4 + 1 + 2
1773+ malformed = bytearray (valid )
1774+ # Claim a length far larger than what remains (but >= BSON_MIN_SIZE so
1775+ # the existing lower-bound check still passes), forcing the new
1776+ # upper-bound guard to reject it.
1777+ malformed [offset :offset + 4 ] = struct .pack ("<i" , 0x7FFF )
1778+ # The C fast-path raises a dedicated "invalid array content" error from the
1779+ # new bound check; the pure-Python path reaches the same invariant through
1780+ # _get_object_size, which raises "invalid object length".
1781+ expected = "invalid array content" if bson .has_c () else "invalid object length"
1782+ with self .assertRaisesRegex (InvalidBSON , expected ):
1783+ _array_of_documents_to_buffer (bytes (malformed ))
1784+
17591785
17601786class TestLongLongToString (unittest .TestCase ):
17611787 def test_long_long_to_string (self ):
0 commit comments