]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
httputil: Add CRLF to _FORBIDDEN_HEADER_CHARS_RE 3584/head
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:19:50 +0000 (12:19 -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 e2f458795db03ef0022c89ac6daae5dc56afa654..19198b7a48399ce3ac975aec6d204040e50c0869 100644 (file)
@@ -67,7 +67,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 61b37bc6baada2a3b3f9edc475ba3d9a0a5ab90b..46034bba13221fa05a426d36687d7e1667cf2b47 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