Skip to content

Commit 6e46f9e

Browse files
Merge pull request from GHSA-73m2-3pwg-5fgc
Fix catastrophic backtracking in regular expression
2 parents 8af9adb + 51b9bd4 commit 6e46f9e

File tree

2 files changed

+28
-6
lines changed

2 files changed

+28
-6
lines changed

waitress/rfc7230.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,13 @@
4040
# field-vchar ]
4141

4242
FIELD_VCHAR = "[" + VCHAR + OBS_TEXT + "]"
43-
FIELD_CONTENT = FIELD_VCHAR + "([ \t" + VCHAR + OBS_TEXT + "]+" + FIELD_VCHAR + "){,1}"
44-
FIELD_VALUE = "(" + FIELD_CONTENT + "){0,}"
43+
# Field content is more greedy than the ABNF, in that it will match the whole value
44+
FIELD_CONTENT = FIELD_VCHAR + "+(?:[ \t]+" + FIELD_VCHAR + "+)*"
45+
# Which allows the field value here to just see if there is even a value in the first place
46+
FIELD_VALUE = "(?:" + FIELD_CONTENT + ")?"
4547

4648
HEADER_FIELD = re.compile(
4749
tobytes(
4850
"^(?P<name>" + TOKEN + "):" + OWS + "(?P<value>" + FIELD_VALUE + ")" + OWS + "$"
4951
)
5052
)
51-
52-
OWS_STRIP = re.compile(OWS + "(?P<value>.*?)" + OWS)

waitress/tests/test_parser.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ def test_received_bad_host_header(self):
5555

5656
def test_received_bad_transfer_encoding(self):
5757
from waitress.utilities import ServerNotImplemented
58+
5859
data = (
5960
b"GET /foobar HTTP/1.1\r\n"
6061
b"Transfer-Encoding: foo\r\n"
@@ -211,7 +212,6 @@ def test_parse_header_11_te_chunked(self):
211212
self.parser.parse_header(data)
212213
self.assertEqual(self.parser.body_rcv.__class__.__name__, "ChunkedReceiver")
213214

214-
215215
def test_parse_header_transfer_encoding_invalid(self):
216216
from waitress.parser import TransferEncodingNotImplemented
217217

@@ -377,7 +377,7 @@ def test_parse_header_invalid_folding_spacing(self):
377377
def test_parse_header_invalid_chars(self):
378378
from waitress.parser import ParsingError
379379

380-
data = b"GET /foobar HTTP/1.1\r\nfoo: bar\r\n\foo: \x0bbaz\r\n"
380+
data = b"GET /foobar HTTP/1.1\r\nfoo: bar\r\nfoo: \x0bbaz\r\n"
381381
try:
382382
self.parser.parse_header(data)
383383
except ParsingError as e:
@@ -433,6 +433,28 @@ def test_parse_header_multiple_values_extra_space(self):
433433
self.assertIn("FOO", self.parser.headers)
434434
self.assertEqual(self.parser.headers["FOO"], "abrowser/0.001 (C O M M E N T)")
435435

436+
def test_parse_header_invalid_backtrack_bad(self):
437+
from waitress.parser import ParsingError
438+
439+
data = b"GET /foobar HTTP/1.1\r\nfoo: bar\r\nfoo: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\x10\r\n"
440+
try:
441+
self.parser.parse_header(data)
442+
except ParsingError as e:
443+
self.assertIn("Invalid header", e.args[0])
444+
else: # pragma: nocover
445+
self.assertTrue(False)
446+
447+
def test_parse_header_short_values(self):
448+
from waitress.parser import ParsingError
449+
450+
data = b"GET /foobar HTTP/1.1\r\none: 1\r\ntwo: 22\r\n"
451+
self.parser.parse_header(data)
452+
453+
self.assertIn("ONE", self.parser.headers)
454+
self.assertIn("TWO", self.parser.headers)
455+
self.assertEqual(self.parser.headers["ONE"], "1")
456+
self.assertEqual(self.parser.headers["TWO"], "22")
457+
436458

437459
class Test_split_uri(unittest.TestCase):
438460
def _callFUT(self, uri):

0 commit comments

Comments
 (0)