|
| 1 | +From 65c8b89ca07543209451a646348bbadb3ca2a4d0 Mon Sep 17 00:00:00 2001 |
| 2 | +From: David Lord <davidism@gmail.com> |
| 3 | +Date: Fri, 25 Oct 2024 06:46:50 -0700 |
| 4 | +Subject: [PATCH] apply max_form_memory_size another level up in the parser |
| 5 | + |
| 6 | +Upstream patch details are given below. |
| 7 | +https://github.com/pallets/werkzeug/commit/50cfeebcb0727e18cc52ffbeb125f4a66551179b#diff-ff3c479edefad986d2fe6fe7ead575a46b086e3bbcf0ccc86d85efc4a4c63c79 |
| 8 | +--- |
| 9 | + src/werkzeug/formparser.py | 11 +++++++++++ |
| 10 | + src/werkzeug/sansio/multipart.py | 2 ++ |
| 11 | + tests/test_formparser.py | 12 ++++++++++++ |
| 12 | + 3 files changed, 25 insertions(+) |
| 13 | + |
| 14 | +diff --git a/src/werkzeug/formparser.py b/src/werkzeug/formparser.py |
| 15 | +index 25ef0d6..a5838e4 100644 |
| 16 | +--- a/src/werkzeug/formparser.py |
| 17 | ++++ b/src/werkzeug/formparser.py |
| 18 | +@@ -480,6 +480,7 @@ class MultiPartParser: |
| 19 | + self, stream: t.IO[bytes], boundary: bytes, content_length: int | None |
| 20 | + ) -> tuple[MultiDict, MultiDict]: |
| 21 | + current_part: Field | File |
| 22 | ++ field_size: int | None = None |
| 23 | + container: t.IO[bytes] | list[bytes] |
| 24 | + _write: t.Callable[[bytes], t.Any] |
| 25 | + |
| 26 | +@@ -498,13 +499,23 @@ class MultiPartParser: |
| 27 | + while not isinstance(event, (Epilogue, NeedData)): |
| 28 | + if isinstance(event, Field): |
| 29 | + current_part = event |
| 30 | ++ field_size = 0 |
| 31 | + container = [] |
| 32 | + _write = container.append |
| 33 | + elif isinstance(event, File): |
| 34 | + current_part = event |
| 35 | ++ field_size = None |
| 36 | + container = self.start_file_streaming(event, content_length) |
| 37 | + _write = container.write |
| 38 | + elif isinstance(event, Data): |
| 39 | ++ if self.max_form_memory_size is not None and field_size is not None: |
| 40 | ++ # Ensure that accumulated data events do not exceed limit. |
| 41 | ++ # Also checked within single event in MultipartDecoder. |
| 42 | ++ field_size += len(event.data) |
| 43 | ++ |
| 44 | ++ if field_size > self.max_form_memory_size: |
| 45 | ++ raise RequestEntityTooLarge() |
| 46 | ++ |
| 47 | + _write(event.data) |
| 48 | + if not event.more_data: |
| 49 | + if isinstance(current_part, Field): |
| 50 | +diff --git a/src/werkzeug/sansio/multipart.py b/src/werkzeug/sansio/multipart.py |
| 51 | +index fc87353..731be03 100644 |
| 52 | +--- a/src/werkzeug/sansio/multipart.py |
| 53 | ++++ b/src/werkzeug/sansio/multipart.py |
| 54 | +@@ -140,6 +140,8 @@ class MultipartDecoder: |
| 55 | + self.max_form_memory_size is not None |
| 56 | + and len(self.buffer) + len(data) > self.max_form_memory_size |
| 57 | + ): |
| 58 | ++ # Ensure that data within single event does not exceed limit. |
| 59 | ++ # Also checked across accumulated events in MultiPartParser. |
| 60 | + raise RequestEntityTooLarge() |
| 61 | + else: |
| 62 | + self.buffer.extend(data) |
| 63 | +diff --git a/tests/test_formparser.py b/tests/test_formparser.py |
| 64 | +index 1dcb167..d8e18e0 100644 |
| 65 | +--- a/tests/test_formparser.py |
| 66 | ++++ b/tests/test_formparser.py |
| 67 | +@@ -448,3 +448,15 @@ class TestMultiPartParser: |
| 68 | + ) as request: |
| 69 | + assert request.files["rfc2231"].filename == "a b c d e f.txt" |
| 70 | + assert request.files["rfc2231"].read() == b"file contents" |
| 71 | ++ |
| 72 | ++ |
| 73 | ++def test_multipart_max_form_memory_size() -> None: |
| 74 | ++ """max_form_memory_size is tracked across multiple data events.""" |
| 75 | ++ data = b"--bound\r\nContent-Disposition: form-field; name=a\r\n\r\n" |
| 76 | ++ data += b"a" * 15 + b"\r\n--bound--" |
| 77 | ++ # The buffer size is less than the max size, so multiple data events will be |
| 78 | ++ # returned. The field size is greater than the max. |
| 79 | ++ parser = formparser.MultiPartParser(max_form_memory_size=10, buffer_size=5) |
| 80 | ++ |
| 81 | ++ with pytest.raises(RequestEntityTooLarge): |
| 82 | ++ parser.parse(io.BytesIO(data), b"bound", None) |
| 83 | +-- |
| 84 | +2.34.1 |
| 85 | + |
0 commit comments