The threats Sportz defends against today, the mitigations in place, and the honest gaps.
Security documented here is what exists. Sportz has no authentication, so this page covers the layers that are built (input validation, rate limiting, headers, secrets, transport) and is explicit about what isn’t.
Every request body and query is parsed with a Zod schema before any logic runs. Invalid input returns 400 with structured issue details and never reaches the database. This also closes the door on a class of injection by rejecting malformed shapes early. See the validation schemas in Backend.
All queries go through Drizzle ORM, which uses parameterized queries: user input is never string-concatenated into SQL. The one place raw SQL appears (TRUNCATE in the test reset helper) takes no user input.
Sliding window: 50 req/10s on HTTP; 5 connections/2s on WS upgrades.
A denied request gets 429 (rate limit) or 403 (other). If Arcjet itself errors, the HTTP path fails closed (503). Arcjet is mocked in tests so the suite is fast and offline (see Testing).
Production talks to Neon Cloud over verified TLS (rejectUnauthorized: true). The three-way SSL strategy (Cloud verified / Local self-signed / plain-Postgres off) is in Backend and was the subject of a real bug (Issues).
Follows from no auth: no roles or protected resources.
CSRF
➖ N/A today
No cookie-based auth / state-changing browser forms with ambient credentials, so the classic CSRF vector doesn’t apply. Becomes relevant the moment auth + cookies are added.
XSS
🟡 Partial
React escapes rendered content by default; no dangerouslySetInnerHTML in use. A formal CSP (Content-Security-Policy) header is not yet configured, a recommended hardening step.
Secrets rotation
❌ Manual
No automated rotation.
The biggest security caveat: there is no auth. Anyone who can reach the API can create matches and commentary. That is acceptable for a demo/portfolio platform but would be the first thing to add before any real multi-user deployment.