kin: BATON-SEC-003-backend_dev
This commit is contained in:
parent
8629f3e40b
commit
3a2ec11cc7
13 changed files with 593 additions and 125 deletions
|
|
@ -6,6 +6,9 @@ Acceptance criteria:
|
|||
5 requests pass (200), 6th returns 429; counter resets after the 10-minute window.
|
||||
2. Token comparison is timing-safe:
|
||||
secrets.compare_digest is used in middleware.py (no == / != for token comparison).
|
||||
|
||||
UUID notes: RegisterRequest.uuid requires a valid UUID v4 pattern.
|
||||
All UUID constants below satisfy this constraint.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -20,6 +23,7 @@ os.environ.setdefault("CHAT_ID", "-1001234567890")
|
|||
os.environ.setdefault("WEBHOOK_SECRET", "test-webhook-secret")
|
||||
os.environ.setdefault("WEBHOOK_URL", "https://example.com/api/webhook/telegram")
|
||||
os.environ.setdefault("FRONTEND_ORIGIN", "http://localhost:3000")
|
||||
os.environ.setdefault("ADMIN_TOKEN", "test-admin-token")
|
||||
|
||||
import pytest
|
||||
from tests.conftest import make_app_client
|
||||
|
|
@ -38,6 +42,24 @@ _SAMPLE_UPDATE = {
|
|||
},
|
||||
}
|
||||
|
||||
# Valid UUID v4 constants for rate-limit tests
|
||||
# Pattern: [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}
|
||||
_UUIDS_OK = [
|
||||
f"d0{i:06d}-0000-4000-8000-000000000001"
|
||||
for i in range(10)
|
||||
]
|
||||
_UUIDS_BLK = [
|
||||
f"d1{i:06d}-0000-4000-8000-000000000001"
|
||||
for i in range(10)
|
||||
]
|
||||
_UUIDS_EXP = [
|
||||
f"d2{i:06d}-0000-4000-8000-000000000001"
|
||||
for i in range(10)
|
||||
]
|
||||
_UUID_BLK_999 = "d1000999-0000-4000-8000-000000000001"
|
||||
_UUID_EXP_BLK = "d2000999-0000-4000-8000-000000000001"
|
||||
_UUID_EXP_AFTER = "d2001000-0000-4000-8000-000000000001"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Criterion 1 — Rate limiting: first 5 requests pass
|
||||
|
|
@ -51,7 +73,7 @@ async def test_register_rate_limit_allows_five_requests():
|
|||
for i in range(5):
|
||||
resp = await client.post(
|
||||
"/api/register",
|
||||
json={"uuid": f"rl-ok-{i:03d}", "name": f"User{i}"},
|
||||
json={"uuid": _UUIDS_OK[i], "name": f"User{i}"},
|
||||
)
|
||||
assert resp.status_code == 200, (
|
||||
f"Request {i + 1}/5 unexpectedly returned {resp.status_code}"
|
||||
|
|
@ -70,11 +92,11 @@ async def test_register_rate_limit_blocks_sixth_request():
|
|||
for i in range(5):
|
||||
await client.post(
|
||||
"/api/register",
|
||||
json={"uuid": f"rl-blk-{i:03d}", "name": f"User{i}"},
|
||||
json={"uuid": _UUIDS_BLK[i], "name": f"User{i}"},
|
||||
)
|
||||
resp = await client.post(
|
||||
"/api/register",
|
||||
json={"uuid": "rl-blk-999", "name": "Attacker"},
|
||||
json={"uuid": _UUID_BLK_999, "name": "Attacker"},
|
||||
)
|
||||
assert resp.status_code == 429
|
||||
|
||||
|
|
@ -94,13 +116,13 @@ async def test_register_rate_limit_resets_after_window_expires():
|
|||
for i in range(5):
|
||||
await client.post(
|
||||
"/api/register",
|
||||
json={"uuid": f"rl-exp-{i:03d}", "name": f"User{i}"},
|
||||
json={"uuid": _UUIDS_EXP[i], "name": f"User{i}"},
|
||||
)
|
||||
|
||||
# Verify the 6th is blocked before window expiry
|
||||
blocked = await client.post(
|
||||
"/api/register",
|
||||
json={"uuid": "rl-exp-blk", "name": "Attacker"},
|
||||
json={"uuid": _UUID_EXP_BLK, "name": "Attacker"},
|
||||
)
|
||||
assert blocked.status_code == 429, (
|
||||
"Expected 429 after exhausting rate limit, got " + str(blocked.status_code)
|
||||
|
|
@ -110,7 +132,7 @@ async def test_register_rate_limit_resets_after_window_expires():
|
|||
with patch("time.time", return_value=base_time + 601):
|
||||
resp_after = await client.post(
|
||||
"/api/register",
|
||||
json={"uuid": "rl-exp-after", "name": "Legit"},
|
||||
json={"uuid": _UUID_EXP_AFTER, "name": "Legit"},
|
||||
)
|
||||
|
||||
assert resp_after.status_code == 200, (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue