Skip to content

Commit

Permalink
Disallow newlines in reason (#9167) (#9198)
Browse files Browse the repository at this point in the history
(cherry picked from commit 88f3834)
  • Loading branch information
Dreamsorcerer committed Sep 19, 2024
1 parent 8e4678a commit e9609ad
Show file tree
Hide file tree
Showing 4 changed files with 17 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGES/9167.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rejected `\n` in `reason` values to avoid sending broken HTTP messages -- by :user:`Dreamsorcerer`.
2 changes: 2 additions & 0 deletions aiohttp/web_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ def set_status(
reason = HTTPStatus(self._status).phrase
except ValueError:
reason = ""
if "\n" in reason:
raise ValueError("Reason cannot contain \\n")
self._reason = reason

@property
Expand Down
5 changes: 5 additions & 0 deletions tests/test_web_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,3 +270,8 @@ def test_unicode_text_body_unauthorized() -> None:
):
resp = web.HTTPUnauthorized(body="text")
assert resp.status == 401


def test_multiline_reason() -> None:
with pytest.raises(ValueError, match=r"Reason cannot contain \\n"):
web.HTTPOk(reason="Bad\r\nInjected-header: foo")
13 changes: 9 additions & 4 deletions tests/test_web_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -945,14 +945,14 @@ async def test_start_force_close() -> None:

async def test___repr__() -> None:
req = make_request("GET", "/path/to")
resp = StreamResponse(reason=301)
resp = StreamResponse(reason="foo")
await resp.prepare(req)
assert "<StreamResponse 301 GET /path/to >" == repr(resp)
assert "<StreamResponse foo GET /path/to >" == repr(resp)


def test___repr___not_prepared() -> None:
resp = StreamResponse(reason=301)
assert "<StreamResponse 301 not prepared>" == repr(resp)
resp = StreamResponse(reason="foo")
assert "<StreamResponse foo not prepared>" == repr(resp)


async def test_keep_alive_http10_default() -> None:
Expand Down Expand Up @@ -1226,6 +1226,11 @@ async def test_render_with_body(buf, writer) -> None:
)


async def test_multiline_reason(buf, writer) -> None:
with pytest.raises(ValueError, match=r"Reason cannot contain \\n"):
Response(reason="Bad\r\nInjected-header: foo")


async def test_send_set_cookie_header(buf, writer) -> None:
resp = Response()
resp.cookies["name"] = "value"
Expand Down

0 comments on commit e9609ad

Please sign in to comment.