From 4105558a82b9d4fd7d68b1887dc22f6a0b627b5f Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sat, 12 Mar 2022 18:32:24 -0700 Subject: [PATCH 2/8] 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. (cherry picked from commit 1f6059f4c4a3a0b256b4027eda64fb9fc311b0a6) --- waitress/parser.py | 13 +++++++------ waitress/tests/test_parser.py | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/waitress/parser.py b/waitress/parser.py index fef8a3d..500730e 100644 --- a/waitress/parser.py +++ b/waitress/parser.py @@ -20,8 +20,9 @@ import re from io import BytesIO from waitress.buffers import OverflowableBuffer -from waitress.compat import tostr, unquote_bytes_to_wsgi, urlparse +from waitress.compat import tostr, tobytes, 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): @@ -208,7 +208,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") @@ -298,11 +298,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(tobytes(cl)): 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/waitress/tests/test_parser.py b/waitress/tests/test_parser.py index 91837c7..eabf353 100644 --- a/waitress/tests/test_parser.py +++ b/waitress/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.45.2