Add security regression tests for all hardening fixes
- New: test_security_headers.py — verify all headers present, HSTS conditional on HTTPS - New: test_passkey_error_handling.py — generic vs user-facing error messages - New: test_poi_validation.py — field length and coordinate range constraints - Extend test_rate_limiter.py — client IP depth selection, in-memory fallback enforcement - Extend test_models.py — sqm range validation - Extend test_task_service.py — IDOR 404, ownership 200, traceback suppression in production
This commit is contained in:
parent
727dd537ef
commit
492921424e
6 changed files with 365 additions and 0 deletions
71
tests/unit/test_passkey_error_handling.py
Normal file
71
tests/unit/test_passkey_error_handling.py
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
"""Unit tests for passkey route error handling."""
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
import pytest
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
# We need to test through the FastAPI app or build a minimal test client
|
||||
from api.passkey_routes import passkey_router
|
||||
from fastapi import FastAPI
|
||||
|
||||
|
||||
def _build_app() -> FastAPI:
|
||||
app = FastAPI()
|
||||
app.include_router(passkey_router)
|
||||
return app
|
||||
|
||||
|
||||
class TestPasskeyErrorHandling:
|
||||
"""Tests that passkey routes return generic error messages for internal exceptions."""
|
||||
|
||||
@patch("api.passkey_routes.passkey_service")
|
||||
@patch("api.passkey_routes.UserRepository")
|
||||
def test_register_begin_internal_error_returns_generic_message(
|
||||
self, mock_user_repo: MagicMock, mock_service: MagicMock
|
||||
) -> None:
|
||||
mock_service.begin_registration.side_effect = RuntimeError("DB connection lost")
|
||||
client = TestClient(_build_app())
|
||||
resp = client.post("/api/passkey/register/begin", json={"email": "test@example.com"})
|
||||
assert resp.status_code == 400
|
||||
assert resp.json()["detail"] == "Registration failed. Please try again."
|
||||
assert "DB connection lost" not in resp.json()["detail"]
|
||||
|
||||
@patch("api.passkey_routes.passkey_service")
|
||||
@patch("api.passkey_routes.UserRepository")
|
||||
def test_register_begin_value_error_returns_user_message(
|
||||
self, mock_user_repo: MagicMock, mock_service: MagicMock
|
||||
) -> None:
|
||||
mock_service.begin_registration.side_effect = ValueError("Email already registered")
|
||||
client = TestClient(_build_app())
|
||||
resp = client.post("/api/passkey/register/begin", json={"email": "test@example.com"})
|
||||
assert resp.status_code == 400
|
||||
assert resp.json()["detail"] == "Email already registered"
|
||||
|
||||
@patch("api.passkey_routes.passkey_service")
|
||||
@patch("api.passkey_routes.UserRepository")
|
||||
def test_login_complete_internal_error_returns_generic_message(
|
||||
self, mock_user_repo: MagicMock, mock_service: MagicMock
|
||||
) -> None:
|
||||
mock_service.complete_authentication.side_effect = RuntimeError("Crypto failure")
|
||||
client = TestClient(_build_app())
|
||||
resp = client.post(
|
||||
"/api/passkey/login/complete",
|
||||
json={"session_id": "abc", "credential": {"id": "x"}},
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.json()["detail"] == "Login could not be completed."
|
||||
assert "Crypto failure" not in resp.json()["detail"]
|
||||
|
||||
@patch("api.passkey_routes.passkey_service")
|
||||
@patch("api.passkey_routes.UserRepository")
|
||||
def test_login_complete_value_error_returns_user_message(
|
||||
self, mock_user_repo: MagicMock, mock_service: MagicMock
|
||||
) -> None:
|
||||
mock_service.complete_authentication.side_effect = ValueError("Invalid credential")
|
||||
client = TestClient(_build_app())
|
||||
resp = client.post(
|
||||
"/api/passkey/login/complete",
|
||||
json={"session_id": "abc", "credential": {"id": "x"}},
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
assert resp.json()["detail"] == "Invalid credential"
|
||||
Loading…
Add table
Add a link
Reference in a new issue