summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBert JW Regeer <bertjw@regeer.org>2022-03-12 18:32:24 -0700
committerBert JW Regeer <bertjw@regeer.org>2022-03-12 19:48:25 -0700
commit1f6059f4c4a3a0b256b4027eda64fb9fc311b0a6 (patch)
treea0ed0cb92ad1bded4f61a37ab9d8a3e573f84819
parente75b0d9afbea8a933f8f5f11d279e661cbfd676b (diff)
downloadwaitress-1f6059f4c4a3a0b256b4027eda64fb9fc311b0a6.tar.gz
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.
-rw-r--r--src/waitress/parser.py12
-rw-r--r--tests/test_parser.py20
2 files changed, 26 insertions, 6 deletions
diff --git a/src/waitress/parser.py b/src/waitress/parser.py
index a6e4d98..ff16a40 100644
--- a/src/waitress/parser.py
+++ b/src/waitress/parser.py
@@ -23,6 +23,7 @@ from urllib.parse import unquote_to_bytes
from waitress.buffers import OverflowableBuffer
from waitress.receiver import ChunkedReceiver, FixedStreamReceiver
+from waitress.rfc7230 import HEADER_FIELD_RE, ONLY_DIGIT_RE
from waitress.utilities import (
BadRequest,
RequestEntityTooLarge,
@@ -31,8 +32,6 @@ from waitress.utilities import (
find_double_newline,
)
-from .rfc7230 import HEADER_FIELD
-
def unquote_bytes_to_wsgi(bytestring):
return unquote_to_bytes(bytestring).decode("latin-1")
@@ -221,7 +220,7 @@ class HTTPRequestParser:
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")
@@ -317,11 +316,12 @@ class HTTPRequestParser:
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:
diff --git a/tests/test_parser.py b/tests/test_parser.py
index aacef26..868c122 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -193,6 +193,26 @@ class TestHTTPRequestParser(unittest.TestCase):
else: # pragma: nocover
self.assertTrue(False)
+ def test_parse_header_bad_content_length_plus(self):
+ 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):
+ 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):
data = b"GET /foobar HTTP/8.4\r\ncontent-length: 10\r\ncontent-length: 20\r\n"