[CRITICAL] Unauthenticated GDPR data-deletion endpoint
Summary
DELETE /api/v1/users/{user_id}/gdpr deletes a user's database rows (research projects, agent tasks) and purges their memory-system data, but the route declares no authentication or ownership dependency at all — any unauthenticated caller can delete any user's data by guessing/enumerating a UUID.
Evidence
src/api/routes/users.py:30-34
@router.delete("/{user_id}/gdpr")
async def delete_user_gdpr(
user_id: UUID,
db: AsyncSession = Depends(get_session),
memory_system: MultiTierMemorySystem | None = Depends(get_memory_system),
) -> dict[str, Any]:
The global AuthMiddleware only initializes request.state and performs no token validation itself (per its own docstring); real authentication only happens where a route separately adds Depends(get_current_user) or similar. This route has neither.
tests/test_pii_privacy.py:181-238 (test_gdpr_delete_purges_user_database_rows_and_memory) calls the endpoint with no Authorization header at all and asserts the deletion succeeds — the only test covering this endpoint demonstrates the vulnerability while treating it as the expected happy path. No test anywhere asserts a 401/403 on this route.
Failure scenario: an unauthenticated request to DELETE /api/v1/users/{any-uuid}/gdpr succeeds and permanently deletes that user's research projects, agent tasks, and memory-tier data.
Impact
Any caller who can reach the API can destroy an arbitrary user's account data with no credentials. This is a full, unauthenticated data-destruction primitive.
Remediation
Add an authentication dependency to the route (e.g. Depends(get_current_user)), and verify the authenticated caller is either the target user or holds an admin/GDPR-operator role before proceeding. Add a regression test asserting 401/403 for an unauthenticated or non-owning caller.
Acceptance
uv run pytest tests/test_pii_privacy.py -v
[CRITICAL] Unauthenticated GDPR data-deletion endpoint
Summary
DELETE /api/v1/users/{user_id}/gdprdeletes a user's database rows (research projects, agent tasks) and purges their memory-system data, but the route declares no authentication or ownership dependency at all — any unauthenticated caller can delete any user's data by guessing/enumerating a UUID.Evidence
src/api/routes/users.py:30-34The global
AuthMiddlewareonly initializesrequest.stateand performs no token validation itself (per its own docstring); real authentication only happens where a route separately addsDepends(get_current_user)or similar. This route has neither.tests/test_pii_privacy.py:181-238(test_gdpr_delete_purges_user_database_rows_and_memory) calls the endpoint with noAuthorizationheader at all and asserts the deletion succeeds — the only test covering this endpoint demonstrates the vulnerability while treating it as the expected happy path. No test anywhere asserts a 401/403 on this route.Failure scenario: an unauthenticated request to
DELETE /api/v1/users/{any-uuid}/gdprsucceeds and permanently deletes that user's research projects, agent tasks, and memory-tier data.Impact
Any caller who can reach the API can destroy an arbitrary user's account data with no credentials. This is a full, unauthenticated data-destruction primitive.
Remediation
Add an authentication dependency to the route (e.g.
Depends(get_current_user)), and verify the authenticated caller is either the target user or holds an admin/GDPR-operator role before proceeding. Add a regression test asserting 401/403 for an unauthenticated or non-owning caller.Acceptance