File size: 3,209 Bytes
d623240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
011e5b6
 
 
 
 
 
 
 
 
 
d623240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0248050
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
from __future__ import annotations

from fastapi.testclient import TestClient

from app.main import create_app
from tests.conftest import FakeModelManager, build_test_settings


def test_security_headers_and_request_id_are_present(client: TestClient) -> None:
    response = client.get("/api/v1/health/live")

    assert response.status_code == 200
    assert response.headers["X-Request-ID"]
    assert response.headers["X-Content-Type-Options"] == "nosniff"
    assert response.headers["X-Frame-Options"] == "DENY"
    assert response.headers["Cache-Control"] == "no-store"


def test_docs_endpoint_remains_renderable_under_csp(client: TestClient) -> None:
    response = client.get("/docs")

    assert response.status_code == 200
    assert "Swagger UI" in response.text
    assert "cdn.jsdelivr.net" in response.headers["Content-Security-Policy"]


def test_huggingface_space_docs_allow_embedding_in_app_tab(client: TestClient) -> None:
    response = client.get("/docs", headers={"Host": "demo-space.hf.space"})

    assert response.status_code == 200
    assert "X-Frame-Options" not in response.headers
    assert "frame-ancestors https://huggingface.co https://*.huggingface.co" in response.headers[
        "Content-Security-Policy"
    ]


def test_rate_limit_blocks_excess_requests(tmp_path) -> None:
    settings = build_test_settings(
        f"sqlite:///{tmp_path / 'rate_limit.db'}",
        rate_limit_requests=1,
        rate_limit_window_seconds=60,
    )
    app = create_app(settings=settings, model_manager=FakeModelManager())

    with TestClient(app) as client:
        first_response = client.post(
            "/api/v1/auth/register",
            json={
                "email": "limit@example.com",
                "full_name": "Limit User",
                "password": "StrongPass!123",
            },
        )
        second_response = client.post(
            "/api/v1/auth/register",
            json={
                "email": "limit2@example.com",
                "full_name": "Limit User Two",
                "password": "StrongPass!123",
            },
        )

    assert first_response.status_code == 201
    assert second_response.status_code == 429
    assert second_response.json()["error"]["code"] == "rate_limit_exceeded"


def test_trusted_host_middleware_rejects_invalid_hosts(client: TestClient) -> None:
    response = client.get("/api/v1/health/live", headers={"Host": "evil.example.com"})

    assert response.status_code == 400


def test_allowed_hosts_configuration_normalizes_full_urls(tmp_path) -> None:
    settings = build_test_settings(
        f"sqlite:///{tmp_path / 'trusted_hosts.db'}",
        allowed_hosts=[
            "localhost/docs",
            "127.0.0.1:8000/docs",
            "https://demo-space.hf.space/docs",
        ],
    )
    app = create_app(settings=settings, model_manager=FakeModelManager())

    with TestClient(app) as client:
        localhost_response = client.get("/api/v1/health/live", headers={"Host": "localhost:8000"})
        space_response = client.get("/api/v1/health/live", headers={"Host": "demo-space.hf.space"})

    assert localhost_response.status_code == 200
    assert space_response.status_code == 200