From c2188f39de0df7fc488703ebe0ed6e224f7be820 Mon Sep 17 00:00:00 2001 From: Renata Ravanelli Date: Fri, 15 Sep 2023 12:26:52 -0300 Subject: [PATCH 2/6] This patch is a backport of commit: 1f6059f From: Bert JW Regeer Date: Sat, 12 Mar 2022 18:32:24 -0700 Subject: [PATCH] Be more strict in parsing Content-Length Validate that we are only parsing digits and nothing else. RFC7230 is explicit in that the Content-Length can only exist of 1*DIGIT and may not include any additional sign information. The Python int() function parses `+10` as `10` which means we were more lenient than the standard intended. Backport: * Patch refresh - no functional change. Signed-off-by: Renata Ravanelli --- src/waitress/parser.py | 11 ++++++----- tests/test_parser.py | 24 ++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/waitress/parser.py b/src/waitress/parser.py index 765fe59..4c6ebeb 100644 --- a/src/waitress/parser.py +++ b/src/waitress/parser.py @@ -22,6 +22,7 @@ from io import BytesIO from waitress.buffers import OverflowableBuffer from waitress.compat import tostr, unquote_bytes_to_wsgi, urlparse from waitress.receiver import ChunkedReceiver, FixedStreamReceiver +from waitress.rfc7230 import HEADER_FIELD_RE, ONLY_DIGIT_RE from waitress.utilities import ( BadRequest, RequestEntityTooLarge, @@ -29,7 +30,6 @@ from waitress.utilities import ( ServerNotImplemented, find_double_newline, ) -from .rfc7230 import HEADER_FIELD class ParsingError(Exception): @@ -209,7 +209,7 @@ class HTTPRequestParser(object): headers = self.headers for line in lines: - header = HEADER_FIELD.match(line) + header = HEADER_FIELD_RE.match(line) if not header: raise ParsingError("Invalid header") @@ -299,11 +299,12 @@ class HTTPRequestParser(object): self.connection_close = True if not self.chunked: - try: - cl = int(headers.get("CONTENT_LENGTH", 0)) - except ValueError: + cl = headers.get("CONTENT_LENGTH", "0") + + if not ONLY_DIGIT_RE.match(cl.encode("latin-1")): raise ParsingError("Content-Length is invalid") + cl = int(cl) self.content_length = cl if cl > 0: buf = OverflowableBuffer(self.adj.inbuf_overflow) diff --git a/tests/test_parser.py b/tests/test_parser.py index 91837c7..eabf353 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -194,6 +194,30 @@ class TestHTTPRequestParser(unittest.TestCase): else: # pragma: nocover self.assertTrue(False) + def test_parse_header_bad_content_length_plus(self): + from waitress.parser import ParsingError + + data = b"GET /foobar HTTP/8.4\r\ncontent-length: +10\r\n" + + try: + self.parser.parse_header(data) + except ParsingError as e: + self.assertIn("Content-Length is invalid", e.args[0]) + else: # pragma: nocover + self.assertTrue(False) + + def test_parse_header_bad_content_length_minus(self): + from waitress.parser import ParsingError + + data = b"GET /foobar HTTP/8.4\r\ncontent-length: -10\r\n" + + try: + self.parser.parse_header(data) + except ParsingError as e: + self.assertIn("Content-Length is invalid", e.args[0]) + else: # pragma: nocover + self.assertTrue(False) + def test_parse_header_multiple_content_length(self): from waitress.parser import ParsingError -- 2.39.2 (Apple Git-143)