mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 03:00:23 +00:00
Fix CSRF test isolation
This commit is contained in:
@@ -261,6 +261,8 @@ class TestPasswordCacheInvalidation(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
if self._backup is not None:
|
||||
self._sf.write_text(self._backup, encoding='utf-8')
|
||||
elif self._sf.exists():
|
||||
self._sf.unlink()
|
||||
auth._invalidate_password_hash_cache()
|
||||
os.environ.pop('HERMES_WEBUI_PASSWORD', None)
|
||||
|
||||
|
||||
@@ -68,6 +68,40 @@ def test_authenticated_same_origin_browser_post_requires_session_csrf_token(monk
|
||||
auth._sessions.pop("c" * 64, None)
|
||||
|
||||
|
||||
def test_authenticated_allowed_public_origin_accepts_valid_csrf_token(monkeypatch):
|
||||
cookie = _signed_cookie("f" * 64)
|
||||
token = auth.csrf_token_for_session(cookie)
|
||||
monkeypatch.setattr(auth, "is_auth_enabled", lambda: True)
|
||||
monkeypatch.setenv("HERMES_WEBUI_ALLOWED_ORIGINS", "https://myapp.example.com:8000")
|
||||
try:
|
||||
headers = {
|
||||
"Origin": "https://myapp.example.com:8000",
|
||||
"Host": "proxy.internal",
|
||||
"Cookie": f"{auth.COOKIE_NAME}={cookie}",
|
||||
auth.CSRF_HEADER_NAME: token,
|
||||
}
|
||||
assert routes._check_csrf(_FakeHandler(headers))
|
||||
finally:
|
||||
auth._sessions.pop("f" * 64, None)
|
||||
|
||||
|
||||
def test_authenticated_reverse_proxy_same_origin_accepts_valid_csrf_token(monkeypatch):
|
||||
cookie = _signed_cookie("g" * 64)
|
||||
token = auth.csrf_token_for_session(cookie)
|
||||
monkeypatch.setattr(auth, "is_auth_enabled", lambda: True)
|
||||
try:
|
||||
headers = {
|
||||
"Origin": "https://example.com",
|
||||
"Host": "127.0.0.1:8787",
|
||||
"X-Forwarded-Host": "example.com:443",
|
||||
"Cookie": f"{auth.COOKIE_NAME}={cookie}",
|
||||
auth.CSRF_HEADER_NAME: token,
|
||||
}
|
||||
assert routes._check_csrf(_FakeHandler(headers))
|
||||
finally:
|
||||
auth._sessions.pop("g" * 64, None)
|
||||
|
||||
|
||||
def test_non_browser_mcp_style_authenticated_post_remains_compatible(monkeypatch):
|
||||
cookie = _signed_cookie("d" * 64)
|
||||
monkeypatch.setattr(auth, "is_auth_enabled", lambda: True)
|
||||
|
||||
+16
-2
@@ -18,6 +18,7 @@ Covers:
|
||||
import importlib
|
||||
import json
|
||||
import pathlib
|
||||
import pytest
|
||||
import sys
|
||||
import time
|
||||
import urllib.error
|
||||
@@ -62,6 +63,21 @@ def get_raw_with_headers(path):
|
||||
|
||||
|
||||
class TestCSRF:
|
||||
@pytest.fixture(autouse=True)
|
||||
def _disable_auth_for_origin_unit_checks(self, monkeypatch):
|
||||
"""Keep origin/port CSRF checks isolated from auth stateful tests.
|
||||
|
||||
These Sprint 29 cases predate session-bound CSRF tokens and exercise
|
||||
only Origin/Referer/Host allow/deny behavior. If an earlier test leaves
|
||||
password auth enabled in the shared pytest process, _check_csrf also
|
||||
requires a valid session CSRF token and these origin-only assertions
|
||||
become order-dependent. Auth-enabled token coverage lives in
|
||||
test_issue1909_csrf_token.py.
|
||||
"""
|
||||
import api.auth as auth
|
||||
|
||||
monkeypatch.setattr(auth, "is_auth_enabled", lambda: False)
|
||||
|
||||
@staticmethod
|
||||
def _csrf_allowed(headers):
|
||||
from types import SimpleNamespace
|
||||
@@ -748,8 +764,6 @@ class TestENVLock:
|
||||
|
||||
# ── Fixture ────────────────────────────────────────────────────────────────
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def webui_server():
|
||||
|
||||
Reference in New Issue
Block a user