From 9aabe7eae3eeb3e64c5a0f3687118cd806158550 Mon Sep 17 00:00:00 2001 From: Khudyakov Artem Date: Mon, 29 Jul 2024 22:05:10 +0300 Subject: Fixed #35440 -- Simplified parse_header_parameters by leveraging stdlid's Message. The `parse_header_parameters` function historically used Python's `cgi` module (now deprecated). In 34e2148fc725e7200050f74130d7523e3cd8507a, the logic was inlined to work around this deprecation ( #33173). Later, in d4d5427571b4bf3a21c902276c2a00215c2a37cc, the header parsing logic was further cleaned up to align with `multipartparser.py` (#33697). This change takes it a step further by replacing the copied `cgi` logic with Python's `email.message.Message` API for a more robust and maintainable header parsing implementation. Thanks to Raphael Gaschignard for testing, and to Adam Johnson and Shai Berger for reviews. Co-authored-by: Ben Cail Co-authored-by: Natalia <124304+nessita@users.noreply.github.com> --- tests/utils_tests/test_http.py | 45 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'tests/utils_tests') diff --git a/tests/utils_tests/test_http.py b/tests/utils_tests/test_http.py index d18fb63c0c..3730c2fcf5 100644 --- a/tests/utils_tests/test_http.py +++ b/tests/utils_tests/test_http.py @@ -6,6 +6,7 @@ from unittest import mock from django.test import SimpleTestCase from django.utils.datastructures import MultiValueDict from django.utils.http import ( + MAX_HEADER_LENGTH, base36_to_int, content_disposition_header, escape_leading_slashes, @@ -424,6 +425,8 @@ class EscapeLeadingSlashesTests(unittest.TestCase): class ParseHeaderParameterTests(unittest.TestCase): def test_basic(self): tests = [ + ("", ("", {})), + (None, ("none", {})), ("text/plain", ("text/plain", {})), ("text/vnd.just.made.this.up ; ", ("text/vnd.just.made.this.up", {})), ("text/plain;charset=us-ascii", ("text/plain", {"charset": "us-ascii"})), @@ -447,10 +450,18 @@ class ParseHeaderParameterTests(unittest.TestCase): 'attachment; filename="strange;name";size=123;', ("attachment", {"filename": "strange;name", "size": "123"}), ), + ( + 'attachment; filename="strange;name";;;;size=123;;;', + ("attachment", {"filename": "strange;name", "size": "123"}), + ), ( 'form-data; name="files"; filename="fo\\"o;bar"', ("form-data", {"name": "files", "filename": 'fo"o;bar'}), ), + ( + 'form-data; name="files"; filename="\\"fo\\"o;b\\\\ar\\""', + ("form-data", {"name": "files", "filename": '"fo"o;b\\ar"'}), + ), ] for header, expected in tests: with self.subTest(header=header): @@ -480,12 +491,13 @@ class ParseHeaderParameterTests(unittest.TestCase): """ Test wrongly formatted RFC 2231 headers (missing double single quotes). Parsing should not crash (#24209). + But stdlib email still decodes (#35440). """ test_data = ( ( "Content-Type: application/x-stuff; " "title*='This%20is%20%2A%2A%2Afun%2A%2A%2A", - "'This%20is%20%2A%2A%2Afun%2A%2A%2A", + "'This is ***fun***", ), ("Content-Type: application/x-stuff; title*='foo.html", "'foo.html"), ("Content-Type: application/x-stuff; title*=bar.html", "bar.html"), @@ -494,6 +506,37 @@ class ParseHeaderParameterTests(unittest.TestCase): parsed = parse_header_parameters(raw_line) self.assertEqual(parsed[1]["title"], expected_title) + def test_header_max_length(self): + base_header = "Content-Type: application/x-stuff; title*=" + base_header_len = len(base_header) + + test_data = [ + (MAX_HEADER_LENGTH, {}), + (MAX_HEADER_LENGTH, {"max_length": None}), + (MAX_HEADER_LENGTH + 1, {"max_length": None}), + (100, {"max_length": 100}), + ] + for line_length, kwargs in test_data: + with self.subTest(line_length=line_length, kwargs=kwargs): + title = "x" * (line_length - base_header_len) + line = base_header + title + assert len(line) == line_length + + parsed = parse_header_parameters(line, **kwargs) + + expected = ("content-type: application/x-stuff", {"title": title}) + self.assertEqual(parsed, expected) + + def test_header_too_long(self): + test_data = [ + ("x" * (MAX_HEADER_LENGTH + 1), {}), + ("x" * 101, {"max_length": 100}), + ] + for line, kwargs in test_data: + with self.subTest(line_length=len(line), kwargs=kwargs): + with self.assertRaises(ValueError): + parse_header_parameters(line, **kwargs) + class ContentDispositionHeaderTests(unittest.TestCase): def test_basic(self): -- cgit v1.3