11from __future__ import annotations
22
3- import logging
43import os
54import random
65import sys
@@ -734,6 +733,14 @@ def test_not_aligned(self) -> None:
734733 "single_field_single_file" ,
735734]
736735
736+ EPILOGUE_TEST_HEAD = (
737+ "--boundary\r \n "
738+ 'Content-Disposition: form-data; name="file"; filename="filename.txt"\r \n '
739+ "Content-Type: text/plain\r \n \r \n "
740+ "hello\r \n "
741+ "--boundary--"
742+ ).encode ("latin-1" )
743+
737744
738745def split_all (val : bytes ) -> Iterator [tuple [bytes , bytes ]]:
739746 """
@@ -1386,7 +1393,7 @@ def on_file(f: File) -> None:
13861393 self .assert_file_data (files [0 ], b"hello" )
13871394
13881395 def test_multipart_parser_data_after_last_boundary (self ) -> None :
1389- """This test makes sure that the parser does not handle when there is junk data after the last boundary."""
1396+ """Parser must short-circuit on arbitrary epilogue data after the closing boundary (no O(N) scan) ."""
13901397 num = 50_000_000
13911398 data = (
13921399 "--boundary\r \n "
@@ -1404,29 +1411,32 @@ def on_file(f: File) -> None:
14041411 f = FormParser ("multipart/form-data" , on_field = Mock (), on_file = on_file , boundary = "boundary" )
14051412 f .write (data .encode ("latin-1" ))
14061413
1407- @pytest .fixture (autouse = True )
1408- def inject_fixtures (self , caplog : pytest .LogCaptureFixture ) -> None :
1409- self ._caplog = caplog
1410-
1411- def test_multipart_parser_data_end_with_crlf_without_warnings (self ) -> None :
1412- """This test makes sure that the parser does not handle when the data ends with a CRLF."""
1413- data = (
1414- "--boundary\r \n "
1415- 'Content-Disposition: form-data; name="file"; filename="filename.txt"\r \n '
1416- "Content-Type: text/plain\r \n \r \n "
1417- "hello\r \n "
1418- "--boundary--\r \n "
1419- )
1414+ @parametrize (
1415+ "chunks" ,
1416+ [
1417+ [EPILOGUE_TEST_HEAD + b"\r \n " ],
1418+ [EPILOGUE_TEST_HEAD + b"\r " , b"\n " ],
1419+ [EPILOGUE_TEST_HEAD , b"\r \n " ],
1420+ [EPILOGUE_TEST_HEAD + b"\r \n --boundary\r \n this is not a valid header\r \n \r \n not a real part" ],
1421+ ],
1422+ )
1423+ def test_multipart_parser_ignores_epilogue (self , chunks : list [bytes ]) -> None :
1424+ """Epilogue data after the closing boundary must be ignored.
14201425
1426+ Covers both the single-chunk case and the case where trailing CRLF is split across `write()` calls.
1427+ The final case asserts that epilogue bytes are not parsed or validated.
1428+ """
14211429 files : list [File ] = []
14221430
14231431 def on_file (f : File ) -> None :
14241432 files .append (f )
14251433
14261434 f = FormParser ("multipart/form-data" , on_field = Mock (), on_file = on_file , boundary = "boundary" )
1427- with self ._caplog .at_level (logging .WARNING ):
1428- f .write (data .encode ("latin-1" ))
1429- assert len (self ._caplog .records ) == 0
1435+ for chunk in chunks :
1436+ f .write (chunk )
1437+
1438+ assert len (files ) == 1
1439+ self .assert_file_data (files [0 ], b"hello" )
14301440
14311441 def test_max_size_multipart (self ) -> None :
14321442 # Load test data.
0 commit comments