From eef2c6dea74cab268d7f22d2f0878f0b805fe6ed Mon Sep 17 00:00:00 2001 From: Ben Kallus Date: Mon, 15 Jun 2026 11:33:37 -0400 Subject: [PATCH 1/2] Catch when the first header line begins with whitespace When the first header field line begins with whitespace, cheroot responds 500 due to an UnboundLocalError. This patch checks that at least one header key has been received before processing folded lines. --- cheroot/server.py | 3 +++ cheroot/test/test_core.py | 12 ++++++++++++ docs/changelog-fragments.d/728.bugfix.rst | 4 ++++ 3 files changed, 19 insertions(+) create mode 100644 docs/changelog-fragments.d/728.bugfix.rst diff --git a/cheroot/server.py b/cheroot/server.py index 27e9173b15..bc478f2e2c 100644 --- a/cheroot/server.py +++ b/cheroot/server.py @@ -221,6 +221,7 @@ def __call__(self, rfile, hdict=None): # noqa: C901 # FIXME if hdict is None: hdict = {} + k = None hname = None while True: line = rfile.readline() @@ -240,6 +241,8 @@ def __call__(self, rfile, hdict=None): # noqa: C901 # FIXME # NOTE: `BytesWarning('Comparison between bytes and int')` # NOTE: The latter is equivalent and does not. # It's a continuation line. + if k is None: + raise ValueError('Illegal continuation line.') v = line.strip() else: try: diff --git a/cheroot/test/test_core.py b/cheroot/test/test_core.py index cd3841d428..201a958d16 100644 --- a/cheroot/test/test_core.py +++ b/cheroot/test/test_core.py @@ -399,6 +399,18 @@ def test_request_line_split_issue_1220(test_client): assert actual_resp_body == b'Hello world!' +def test_parse_invalid_line_fold(test_client): + """Check that the first field line can't begin with whitespace.""" + c = test_client.get_connection() + c._output(u'GET / HTTP/1.1\r\n invalid\r\n\r\n'.encode('utf-8')) + c._send_output() + response = _get_http_response(c, method='GET') + response.begin() + assert response.status == HTTP_BAD_REQUEST + assert response.read(26) == b'Illegal continuation line.' + c.close() + + def test_garbage_in(test_client): """Test that server sends an error for garbage received over TCP.""" # Connect without SSL regardless of server.scheme diff --git a/docs/changelog-fragments.d/728.bugfix.rst b/docs/changelog-fragments.d/728.bugfix.rst new file mode 100644 index 0000000000..da10a4e257 --- /dev/null +++ b/docs/changelog-fragments.d/728.bugfix.rst @@ -0,0 +1,4 @@ +The server has been updated to respond 400 to requests in +which the first header field line begins with whitespace, +instead of 500. +by :user:`kenballus` From 6a9fac096d8e959fefd0490054effbd59dc4c4f5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 15:44:50 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- cheroot/test/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cheroot/test/test_core.py b/cheroot/test/test_core.py index 201a958d16..478566099e 100644 --- a/cheroot/test/test_core.py +++ b/cheroot/test/test_core.py @@ -402,7 +402,7 @@ def test_request_line_split_issue_1220(test_client): def test_parse_invalid_line_fold(test_client): """Check that the first field line can't begin with whitespace.""" c = test_client.get_connection() - c._output(u'GET / HTTP/1.1\r\n invalid\r\n\r\n'.encode('utf-8')) + c._output(b'GET / HTTP/1.1\r\n invalid\r\n\r\n') c._send_output() response = _get_http_response(c, method='GET') response.begin()