Fix CSRF validation for duplicate cookie values on PATCH
This commit is contained in:
@@ -364,6 +364,7 @@ if "app.services.routing_pipeline" not in sys.modules:
|
||||
from fastapi import HTTPException
|
||||
|
||||
from app.api.auth import AuthContext, require_admin
|
||||
from app.api import auth as auth_dependency_module
|
||||
from app.api import routes_auth as auth_routes_module
|
||||
from app.api import routes_documents as documents_routes_module
|
||||
from app.core import config as config_module
|
||||
@@ -420,6 +421,63 @@ class AuthDependencyTests(unittest.TestCase):
|
||||
resolved = require_admin(context=auth_context)
|
||||
self.assertEqual(resolved.role, UserRole.ADMIN)
|
||||
|
||||
def test_csrf_validation_accepts_matching_token_among_duplicate_cookie_values(self) -> None:
|
||||
"""PATCH CSRF validation accepts header token matching any duplicate csrf cookie value."""
|
||||
|
||||
request = SimpleNamespace(
|
||||
method="PATCH",
|
||||
headers={"cookie": "dcm_session=session-token; dcm_csrf=stale-token; dcm_csrf=fresh-token"},
|
||||
)
|
||||
resolved_session = SimpleNamespace(
|
||||
id=uuid.uuid4(),
|
||||
expires_at=datetime.now(UTC),
|
||||
user=SimpleNamespace(
|
||||
id=uuid.uuid4(),
|
||||
username="admin",
|
||||
role=UserRole.ADMIN,
|
||||
),
|
||||
)
|
||||
with patch.object(auth_dependency_module, "resolve_auth_session", return_value=resolved_session):
|
||||
context = auth_dependency_module.get_request_auth_context(
|
||||
request=request,
|
||||
credentials=None,
|
||||
csrf_header="fresh-token",
|
||||
csrf_cookie="stale-token",
|
||||
session_cookie="session-token",
|
||||
session=SimpleNamespace(),
|
||||
)
|
||||
self.assertEqual(context.username, "admin")
|
||||
self.assertEqual(context.role, UserRole.ADMIN)
|
||||
|
||||
def test_csrf_validation_rejects_when_header_does_not_match_any_cookie_value(self) -> None:
|
||||
"""PATCH CSRF validation rejects requests when header token matches no csrf cookie values."""
|
||||
|
||||
request = SimpleNamespace(
|
||||
method="PATCH",
|
||||
headers={"cookie": "dcm_session=session-token; dcm_csrf=stale-token; dcm_csrf=fresh-token"},
|
||||
)
|
||||
resolved_session = SimpleNamespace(
|
||||
id=uuid.uuid4(),
|
||||
expires_at=datetime.now(UTC),
|
||||
user=SimpleNamespace(
|
||||
id=uuid.uuid4(),
|
||||
username="admin",
|
||||
role=UserRole.ADMIN,
|
||||
),
|
||||
)
|
||||
with patch.object(auth_dependency_module, "resolve_auth_session", return_value=resolved_session):
|
||||
with self.assertRaises(HTTPException) as raised:
|
||||
auth_dependency_module.get_request_auth_context(
|
||||
request=request,
|
||||
credentials=None,
|
||||
csrf_header="unknown-token",
|
||||
csrf_cookie="stale-token",
|
||||
session_cookie="session-token",
|
||||
session=SimpleNamespace(),
|
||||
)
|
||||
self.assertEqual(raised.exception.status_code, 403)
|
||||
self.assertEqual(raised.exception.detail, "Invalid CSRF token")
|
||||
|
||||
|
||||
class DocumentCatalogVisibilityTests(unittest.TestCase):
|
||||
"""Verifies predefined tag and path discovery visibility by caller role."""
|
||||
|
||||
Reference in New Issue
Block a user