feat(sp19): security hardening + rich health probe
Three pure-ASGI middlewares close completeness-review gaps §3.1.4 (no body/rate limits) and §3.1.25 (no security headers): - BodySizeLimitMiddleware — rejects oversized uploads (50 MB default, CYCLONE_MAX_BODY_BYTES override). 413 on over-cap Content-Length and on chunked reads that cross the cap. - RateLimitMiddleware — sliding-window per-IP limiter (300/min default, CYCLONE_RATE_LIMIT_PER_MIN override). 429 over the window. /api/health is exempt. - SecurityHeadersMiddleware — stamps X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, and a strict Content-Security-Policy on every response. Every 413/429 also writes a tamper-evident api.request_rejected event into the SP11 audit chain so an operator can correlate rejections with the SP18 JSON logs. GET /api/health is rewritten to return a subsystem snapshot: DB connectivity (SELECT 1), MFT scheduler state, backup scheduler state, live pubsub subscriber counts, last batch id + timestamp. Returns status='degraded' if any subsystem is unhappy; per-subsystem errors surfaced in the respective dict. Cyclone.pubsub.EventBus.stats() — new method for live subscriber counts. 13 new tests (test_security.py) + 1 updated (test_api.py health endpoint). All 883 backend tests pass.
This commit is contained in:
@@ -31,10 +31,18 @@ def client() -> TestClient:
|
||||
|
||||
|
||||
def test_health_endpoint(client: TestClient):
|
||||
"""SP19: health endpoint now returns a subsystem snapshot."""
|
||||
resp = client.get("/api/health")
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body == {"status": "ok", "version": __version__}
|
||||
# Old contract (status + version) is preserved.
|
||||
assert body["status"] == "ok"
|
||||
assert body["version"] == __version__
|
||||
# SP19 additions.
|
||||
assert "db" in body and body["db"].get("ok") is True
|
||||
assert "scheduler" in body
|
||||
assert "pubsub" in body
|
||||
assert "batch" in body
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
Reference in New Issue
Block a user