Files
hermes-webui/tests/test_rejected_write_connection_close.py
T
Rodrigo Gomes da Silva 1bd3c72e17 fix: close connection on extension-sidecar provenance rejection
Same class as the auth/CSRF close-on-reject: the sidecar proxy's
same-origin provenance check runs before read_body(), so an HTTP/1.1
connection reused after a 403 would parse the unread body as the next
request line. Flag close_connection on this path too.

Found by Greptile review of #6658; covered by a new unit test.
2026-09-22 12:06:08 +00:00

75 lines
2.9 KiB
Python

"""Regression tests for HTTP/1.1 write rejection framing.
A rejected POST can happen before its request body is consumed (authentication
or CSRF failure). Keeping that connection alive leaves the body bytes in
``rfile``; BaseHTTPRequestHandler then parses them as the next request method.
The connection must close after those early rejections.
"""
from types import SimpleNamespace
from typing import Any, cast
def test_auth_rejected_write_closes_connection(monkeypatch):
import api.auth as auth
import server
handler = SimpleNamespace(
path="/api/session/new",
command="POST",
close_connection=False,
)
monkeypatch.setattr(server, "reset_trusted_auth_request_state", lambda _handler: None)
monkeypatch.setattr(server, "get_profile_cookie", lambda _handler: None)
monkeypatch.setattr(server, "clear_request_profile", lambda: None)
monkeypatch.setattr(auth, "check_auth", lambda _handler, _parsed: False)
def route_must_not_run(_handler, _parsed):
raise AssertionError("route ran after auth rejection")
server.Handler._handle_write(cast(Any, handler), route_must_not_run)
assert handler.close_connection is True
def test_origin_rejected_csrf_closes_connection(monkeypatch):
import api.routes as routes
handler = SimpleNamespace(close_connection=False)
monkeypatch.setattr(routes, "_check_same_origin_browser_request", lambda _handler: False)
assert routes._check_csrf(handler) is False
assert handler.close_connection is True
def test_token_rejected_csrf_closes_connection(monkeypatch):
import api.auth as auth
import api.routes as routes
handler = SimpleNamespace(headers={}, close_connection=False)
monkeypatch.setattr(routes, "_check_same_origin_browser_request", lambda _handler: True)
monkeypatch.setattr(routes, "_is_browser_unsafe_request", lambda _handler: True)
monkeypatch.setattr(auth, "is_auth_enabled", lambda: True)
monkeypatch.setattr(auth, "parse_cookie", lambda _handler: "session-cookie")
monkeypatch.setattr(auth, "verify_csrf_token", lambda _cookie, _token: False)
assert routes._check_csrf(handler) is False
assert handler.close_connection is True
def test_sidecar_provenance_rejected_closes_connection(monkeypatch):
"""Sidecar proxy provenance rejection also runs before read_body()."""
import api.routes as routes
handler = SimpleNamespace(
path="/api/extensions/ext1/sidecar/proxy",
command="POST",
close_connection=False,
)
monkeypatch.setattr(routes, "_match_extension_sidecar_proxy_path", lambda _path: ("ext1", "/proxy"))
monkeypatch.setattr(routes, "_check_same_origin_browser_request", lambda _handler, **_: False)
monkeypatch.setattr(routes, "j", lambda _handler, _payload, status=200: None)
routes._handle_extension_sidecar_proxy(handler, SimpleNamespace(path=handler.path, query=""), "POST")
assert handler.close_connection is True