From: Ben Darnell Date: Tue, 10 Mar 2026 16:19:50 +0000 (-0400) Subject: httputil: Add CRLF to _FORBIDDEN_HEADER_CHARS_RE X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c3290fee1ea9cefa977c052aa2bb75a0d1af96b;p=thirdparty%2Ftornado.git httputil: Add CRLF to _FORBIDDEN_HEADER_CHARS_RE 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. --- diff --git a/tornado/httputil.py b/tornado/httputil.py index e2f45879..19198b7a 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -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: diff --git a/tornado/test/httputil_test.py b/tornado/test/httputil_test.py index 61b37bc6..46034bba 100644 --- a/tornado/test/httputil_test.py +++ b/tornado/test/httputil_test.py @@ -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