Skip to content

Commit 50cfeeb

Browse files
authored
Merge commit from fork
apply max_form_memory_size another level up in the parser
2 parents 8d6a12e + 8760275 commit 50cfeeb

File tree

4 files changed

+28
-0
lines changed

4 files changed

+28
-0
lines changed

CHANGES.rst

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ Version 3.0.6
55

66
Unreleased
77

8+
- Fix how ``max_form_memory_size`` is applied when parsing large non-file
9+
fields. :ghsa:`q34m-jh98-gwm2`
10+
811

912
Version 3.0.5
1013
-------------

src/werkzeug/formparser.py

+11
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ def parse(
352352
self, stream: t.IO[bytes], boundary: bytes, content_length: int | None
353353
) -> tuple[MultiDict[str, str], MultiDict[str, FileStorage]]:
354354
current_part: Field | File
355+
field_size: int | None = None
355356
container: t.IO[bytes] | list[bytes]
356357
_write: t.Callable[[bytes], t.Any]
357358

@@ -370,13 +371,23 @@ def parse(
370371
while not isinstance(event, (Epilogue, NeedData)):
371372
if isinstance(event, Field):
372373
current_part = event
374+
field_size = 0
373375
container = []
374376
_write = container.append
375377
elif isinstance(event, File):
376378
current_part = event
379+
field_size = None
377380
container = self.start_file_streaming(event, content_length)
378381
_write = container.write
379382
elif isinstance(event, Data):
383+
if self.max_form_memory_size is not None and field_size is not None:
384+
# Ensure that accumulated data events do not exceed limit.
385+
# Also checked within single event in MultipartDecoder.
386+
field_size += len(event.data)
387+
388+
if field_size > self.max_form_memory_size:
389+
raise RequestEntityTooLarge()
390+
380391
_write(event.data)
381392
if not event.more_data:
382393
if isinstance(current_part, Field):

src/werkzeug/sansio/multipart.py

+2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ def receive_data(self, data: bytes | None) -> None:
140140
self.max_form_memory_size is not None
141141
and len(self.buffer) + len(data) > self.max_form_memory_size
142142
):
143+
# Ensure that data within single event does not exceed limit.
144+
# Also checked across accumulated events in MultiPartParser.
143145
raise RequestEntityTooLarge()
144146
else:
145147
self.buffer.extend(data)

tests/test_formparser.py

+12
Original file line numberDiff line numberDiff line change
@@ -456,3 +456,15 @@ def test_file_rfc2231_filename_continuations(self):
456456
) as request:
457457
assert request.files["rfc2231"].filename == "a b c d e f.txt"
458458
assert request.files["rfc2231"].read() == b"file contents"
459+
460+
461+
def test_multipart_max_form_memory_size() -> None:
462+
"""max_form_memory_size is tracked across multiple data events."""
463+
data = b"--bound\r\nContent-Disposition: form-field; name=a\r\n\r\n"
464+
data += b"a" * 15 + b"\r\n--bound--"
465+
# The buffer size is less than the max size, so multiple data events will be
466+
# returned. The field size is greater than the max.
467+
parser = formparser.MultiPartParser(max_form_memory_size=10, buffer_size=5)
468+
469+
with pytest.raises(RequestEntityTooLarge):
470+
parser.parse(io.BytesIO(data), b"bound", None)

0 commit comments

Comments
 (0)