Skip to content

Commit c67c9e4

Browse files
Reject non-digit Content-Length in multipart body parts (aio-libs#12794)
Co-authored-by: Sam Bull <git@sambull.org>
1 parent 04f3a5f commit c67c9e4

3 files changed

Lines changed: 16 additions & 0 deletions

File tree

CHANGES/12794.bugfix.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Rejected multipart body parts whose ``Content-Length`` header is not a
2+
plain sequence of digits (e.g. ``+5``, ``-1``, ``1_0``), matching the
3+
strictness of the main request parser per :rfc:`9110#section-8.6`
4+
-- by :user:`dxbjavid`.

aiohttp/multipart.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,11 @@ def __init__(
283283
self._is_form_data = subtype == "form-data"
284284
# https://datatracker.ietf.org/doc/html/rfc7578#section-4.8
285285
length = None if self._is_form_data else self.headers.get(CONTENT_LENGTH, None)
286+
if length is not None and not (length.isascii() and length.isdigit()):
287+
# Reject sign prefixes, underscores, whitespace and non-ASCII
288+
# digits that int() would otherwise accept.
289+
# https://www.rfc-editor.org/rfc/rfc9110#section-8.6
290+
raise ValueError(f"invalid Content-Length: {length!r}")
286291
self._length = int(length) if length is not None else None
287292
self._read_bytes = 0
288293
self._unread: deque[bytes] = deque()

tests/test_multipart.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,13 @@ async def test_multi_read_chunk(self) -> None:
293293
assert b"" == result
294294
assert obj.at_eof()
295295

296+
@pytest.mark.parametrize("value", ["-1", "+5", "1_0", " 5", "0x5", "5"])
297+
async def test_rejects_malformed_content_length(self, value: str) -> None:
298+
h = HeadersDictProxy(CIMultiDict({"CONTENT-LENGTH": value}))
299+
with Stream(b"Hello, world!\r\n--:--") as stream:
300+
with pytest.raises(ValueError, match="Content-Length"):
301+
aiohttp.BodyPartReader(BOUNDARY, h, stream)
302+
296303
async def test_read_chunk_properly_counts_read_bytes(self) -> None:
297304
expected = b"." * 10
298305
size = len(expected)

0 commit comments

Comments
 (0)