]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
httputil: Add CRLF to _FORBIDDEN_HEADER_CHARS_RE
authorBen Darnell <ben@bendarnell.com>
Tue, 10 Mar 2026 16:19:50 +0000 (12:19 -0400)
committerBen Darnell <ben@bendarnell.com>
Tue, 10 Mar 2026 16:48:36 +0000 (12:48 -0400)
I think these were omitted due to quirks of an older version of the
parsing code. Linefeeds are already effectively prohibited within
header values since they are interpreted as delimiters, so the net
effect of this change is to prohibit bare carriage returns within
header values. This RE is used only when parsing headers inside
multipart/form-data bodies; for HTTP headers CR was already prohibited.

tornado/httputil.py
tornado/test/httputil_test.py

index c7208178ec9bb9fa86d0d96226530ef2d8bd8297..0c9ad832431859b7adbb1f4caa7e72741a09b1b1 100644 (file)
@@ -73,7 +73,7 @@ HTTP_WHITESPACE = " \t"
 
 # Roughly the inverse of RequestHandler._VALID_HEADER_CHARS, but permits
 # chars greater than \xFF (which may appear after decoding utf8).
-_FORBIDDEN_HEADER_CHARS_RE = re.compile(r"[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]")
+_FORBIDDEN_HEADER_CHARS_RE = re.compile(r"[\x00-\x08\x0A-\x1F\x7F]")
 
 
 class _ABNF:
index 79ea6ccb031b9685cfbde505c17e7e73fc4d26b4..92683ae9b935bd71e5649c96399175ed3db58a8b 100644 (file)
@@ -136,6 +136,8 @@ Foo
             'a";";.txt',
             'a\\"b.txt',
             "a\\b.txt",
+            "a b.txt",
+            "a\tb.txt",
         ]
         for filename in filenames:
             logging.debug("trying filename %r", filename)
@@ -156,6 +158,29 @@ Foo
             self.assertEqual(file["filename"], filename)
             self.assertEqual(file["body"], b"Foo")
 
+    def test_invalid_chars(self):
+        filenames = [
+            "a\rb.txt",
+            "a\0b.txt",
+            "a\x08b.txt",
+        ]
+        for filename in filenames:
+            str_data = """\
+--1234
+Content-Disposition: form-data; name="files"; filename="%s"
+
+Foo
+--1234--""" % filename.replace(
+                "\\", "\\\\"
+            ).replace(
+                '"', '\\"'
+            )
+            data = utf8(str_data.replace("\n", "\r\n"))
+            args, files = form_data_args()
+            with self.assertRaises(HTTPInputError) as cm:
+                parse_multipart_form_data(b"1234", data, args, files)
+            self.assertIn("Invalid header value", str(cm.exception))
+
     def test_non_ascii_filename_rfc5987(self):
         data = b"""\
 --1234