Skip to content

Commit

Permalink
Merge pull request from GHSA-73m2-3pwg-5fgc
Browse files Browse the repository at this point in the history
Fix catastrophic backtracking in regular expression
  • Loading branch information
digitalresistor committed Feb 3, 2020
2 parents 8af9adb + 51b9bd4 commit 6e46f9e
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 6 deletions.
8 changes: 4 additions & 4 deletions waitress/rfc7230.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@
# field-vchar ]

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

HEADER_FIELD = re.compile(
tobytes(
"^(?P<name>" + TOKEN + "):" + OWS + "(?P<value>" + FIELD_VALUE + ")" + OWS + "$"
)
)

OWS_STRIP = re.compile(OWS + "(?P<value>.*?)" + OWS)
26 changes: 24 additions & 2 deletions waitress/tests/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def test_received_bad_host_header(self):

def test_received_bad_transfer_encoding(self):
from waitress.utilities import ServerNotImplemented

data = (
b"GET /foobar HTTP/1.1\r\n"
b"Transfer-Encoding: foo\r\n"
Expand Down Expand Up @@ -211,7 +212,6 @@ def test_parse_header_11_te_chunked(self):
self.parser.parse_header(data)
self.assertEqual(self.parser.body_rcv.__class__.__name__, "ChunkedReceiver")


def test_parse_header_transfer_encoding_invalid(self):
from waitress.parser import TransferEncodingNotImplemented

Expand Down Expand Up @@ -377,7 +377,7 @@ def test_parse_header_invalid_folding_spacing(self):
def test_parse_header_invalid_chars(self):
from waitress.parser import ParsingError

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

def test_parse_header_invalid_backtrack_bad(self):
from waitress.parser import ParsingError

data = b"GET /foobar HTTP/1.1\r\nfoo: bar\r\nfoo: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\x10\r\n"
try:
self.parser.parse_header(data)
except ParsingError as e:
self.assertIn("Invalid header", e.args[0])
else: # pragma: nocover
self.assertTrue(False)

def test_parse_header_short_values(self):
from waitress.parser import ParsingError

data = b"GET /foobar HTTP/1.1\r\none: 1\r\ntwo: 22\r\n"
self.parser.parse_header(data)

self.assertIn("ONE", self.parser.headers)
self.assertIn("TWO", self.parser.headers)
self.assertEqual(self.parser.headers["ONE"], "1")
self.assertEqual(self.parser.headers["TWO"], "22")


class Test_split_uri(unittest.TestCase):
def _callFUT(self, uri):
Expand Down

0 comments on commit 6e46f9e

Please sign in to comment.