Spaces:
Running
Running
File size: 4,689 Bytes
f1fa34c | 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 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | """
Upload-validation and configuration-hardening regression tests.
The audit reproduced two crashes here: an empty file and a text file renamed to
.pdf both returned 500. Malformed input is a client error and must never take
a worker down or pollute error monitoring.
"""
from __future__ import annotations
import io
import pytest
from tests.conftest import auth_header, register_and_login
@pytest.mark.parametrize(
"name,payload,label",
[
("empty.pdf", b"", "empty file"),
("text.pdf", b"just plain text, definitely not a pdf", "renamed text file"),
("corrupt.pdf", b"%PDF-1.7\n<<GARBAGE", "valid header, corrupt body"),
("nul.pdf", b"\x00" * 512, "binary noise"),
],
)
def test_malformed_upload_returns_422_never_500(client, name, payload, label):
"""AUDIT H4: every malformed upload is a 422 with an actionable message."""
tokens = register_and_login(client, "alice")
res = client.post(
"/api/documents/upload",
headers=auth_header(tokens),
files={"file": (name, io.BytesIO(payload), "application/pdf")},
)
assert res.status_code == 422, f"{label} returned {res.status_code}"
assert res.json()["message"], "422 must explain what is wrong"
def test_truncated_pdf_is_rejected(client, make_pdf):
"""A structurally broken PDF passes the magic-byte check but must still 422."""
tokens = register_and_login(client, "alice")
valid = make_pdf()
res = client.post(
"/api/documents/upload",
headers=auth_header(tokens),
files={"file": ("trunc.pdf", io.BytesIO(valid[: len(valid) // 3]), "application/pdf")},
)
assert res.status_code == 422
def test_disallowed_content_type_is_rejected(client):
tokens = register_and_login(client, "alice")
res = client.post(
"/api/documents/upload",
headers=auth_header(tokens),
files={"file": ("payload.exe", io.BytesIO(b"MZ\x90\x00"), "application/x-msdownload")},
)
assert res.status_code == 422
def test_oversized_upload_returns_413(client, monkeypatch):
from app.config import get_settings
monkeypatch.setattr(get_settings(), "max_upload_mb", 1)
tokens = register_and_login(client, "alice")
res = client.post(
"/api/documents/upload",
headers=auth_header(tokens),
files={"file": ("big.pdf", io.BytesIO(b"%PDF-1.7" + b"\x00" * (2 * 1024 * 1024)), "application/pdf")},
)
assert res.status_code == 413
def test_valid_pdf_still_ingests(client, pdf_upload):
"""The guard must not break the happy path."""
tokens = register_and_login(client, "alice")
res = client.post("/api/documents/upload", headers=auth_header(tokens), files=pdf_upload())
assert res.status_code == 200
body = res.json()
assert body["chunks_added"] > 0
assert body["coverage"]["coverage_pct"] == 100.0
# βββ Configuration hardening (C2, H6) ββββββββββββββββββββββββββββββββββββββββ
def test_production_requires_explicit_cors_allowlist(monkeypatch):
"""AUDIT C2: wildcard CORS must be impossible in production."""
from api.settings import ApiSettings, ConfigurationError
monkeypatch.setenv("ENVIRONMENT", "production")
monkeypatch.setenv("JWT_SECRET", "x" * 48)
monkeypatch.setenv("CORS_ORIGINS", "")
with pytest.raises(ConfigurationError):
ApiSettings().validate_runtime()
monkeypatch.setenv("CORS_ORIGINS", "*")
with pytest.raises(ConfigurationError):
ApiSettings().cors_origins_list
monkeypatch.setenv("CORS_ORIGINS", "https://app.example.com")
assert ApiSettings().cors_origins_list == ["https://app.example.com"]
def test_production_requires_strong_jwt_secret(monkeypatch):
from api.settings import ApiSettings, ConfigurationError
monkeypatch.setenv("ENVIRONMENT", "production")
monkeypatch.setenv("CORS_ORIGINS", "https://app.example.com")
monkeypatch.setenv("JWT_SECRET", "")
with pytest.raises(ConfigurationError):
ApiSettings().validate_runtime()
monkeypatch.setenv("JWT_SECRET", "tooshort")
with pytest.raises(ConfigurationError):
ApiSettings().validate_runtime()
monkeypatch.setenv("JWT_SECRET", "x" * 48)
ApiSettings().validate_runtime() # must not raise
def test_docs_hidden_in_production(monkeypatch):
from api.settings import ApiSettings
monkeypatch.setenv("ENVIRONMENT", "production")
monkeypatch.setenv("JWT_SECRET", "x" * 48)
monkeypatch.setenv("CORS_ORIGINS", "https://app.example.com")
monkeypatch.setenv("EXPOSE_DOCS", "false")
assert ApiSettings().docs_url is None
|