Only set CSRF cookie on http.response.start

ASGI spec only allows headers on the http.response.start message;
appending Set-Cookie to body messages is out-of-spec and may break on
some servers. Early-return for non-start messages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Georges-Antoine Assi
2026-05-24 15:46:50 -04:00
parent f94206aa53
commit be476cb7dc
2 changed files with 14 additions and 2 deletions

View File

@@ -90,6 +90,10 @@ class CSRFMiddleware:
await self.app(scope, receive, send)
async def send(self, message: Message, send: Send, scope: Scope) -> None:
if message["type"] != "http.response.start":
await send(message)
return
request = Request(scope)
csrf_cookie = request.cookies.get(self.cookie_name)
current_user_id = request.user.id if request.user.is_authenticated else None

View File

@@ -242,8 +242,12 @@ class TestCSRFMiddleware:
def test_user_id_mismatch_fails(self) -> None:
"""Tokens issued for one user must not validate for another."""
# We simulate two users by calling _generate_csrf_token with different IDs
mw = CSRFMiddleware(app=lambda s, r, se: None, secret="test")
async def noop_app(scope, receive, send):
pass
mw = CSRFMiddleware(app=noop_app, secret="test")
user1_token = mw._generate_csrf_token(user_id=1)
# user1_token should not validate for user_id=2
@@ -293,6 +297,10 @@ class TestCSRFMiddleware:
def test_bad_signature_returns_false(self) -> None:
"""_csrf_tokens_match should return False on BadSignature."""
mw = CSRFMiddleware(app=lambda s, r, se: None, secret="test")
async def noop_app(scope, receive, send):
pass
mw = CSRFMiddleware(app=noop_app, secret="test")
ok = mw._csrf_tokens_match("bad-token", "bad-token", user_id=None)
assert ok is False