Skip to content

Commit 1f6059f

Browse files
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.
1 parent e75b0d9 commit 1f6059f

File tree

2 files changed

+26
-6
lines changed

2 files changed

+26
-6
lines changed

src/waitress/parser.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
from waitress.buffers import OverflowableBuffer
2525
from waitress.receiver import ChunkedReceiver, FixedStreamReceiver
26+
from waitress.rfc7230 import HEADER_FIELD_RE, ONLY_DIGIT_RE
2627
from waitress.utilities import (
2728
BadRequest,
2829
RequestEntityTooLarge,
@@ -31,8 +32,6 @@
3132
find_double_newline,
3233
)
3334

34-
from .rfc7230 import HEADER_FIELD
35-
3635

3736
def unquote_bytes_to_wsgi(bytestring):
3837
return unquote_to_bytes(bytestring).decode("latin-1")
@@ -221,7 +220,7 @@ def parse_header(self, header_plus):
221220
headers = self.headers
222221

223222
for line in lines:
224-
header = HEADER_FIELD.match(line)
223+
header = HEADER_FIELD_RE.match(line)
225224

226225
if not header:
227226
raise ParsingError("Invalid header")
@@ -317,11 +316,12 @@ def parse_header(self, header_plus):
317316
self.connection_close = True
318317

319318
if not self.chunked:
320-
try:
321-
cl = int(headers.get("CONTENT_LENGTH", 0))
322-
except ValueError:
319+
cl = headers.get("CONTENT_LENGTH", "0")
320+
321+
if not ONLY_DIGIT_RE.match(cl.encode("latin-1")):
323322
raise ParsingError("Content-Length is invalid")
324323

324+
cl = int(cl)
325325
self.content_length = cl
326326

327327
if cl > 0:

tests/test_parser.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,26 @@ def test_parse_header_bad_content_length(self):
193193
else: # pragma: nocover
194194
self.assertTrue(False)
195195

196+
def test_parse_header_bad_content_length_plus(self):
197+
data = b"GET /foobar HTTP/8.4\r\ncontent-length: +10\r\n"
198+
199+
try:
200+
self.parser.parse_header(data)
201+
except ParsingError as e:
202+
self.assertIn("Content-Length is invalid", e.args[0])
203+
else: # pragma: nocover
204+
self.assertTrue(False)
205+
206+
def test_parse_header_bad_content_length_minus(self):
207+
data = b"GET /foobar HTTP/8.4\r\ncontent-length: -10\r\n"
208+
209+
try:
210+
self.parser.parse_header(data)
211+
except ParsingError as e:
212+
self.assertIn("Content-Length is invalid", e.args[0])
213+
else: # pragma: nocover
214+
self.assertTrue(False)
215+
196216
def test_parse_header_multiple_content_length(self):
197217
data = b"GET /foobar HTTP/8.4\r\ncontent-length: 10\r\ncontent-length: 20\r\n"
198218

0 commit comments

Comments
 (0)