# Security review Review date: 2026-08-02 ## Scope Next.js portal and APIs, Discord bot, PostgreSQL persistence, Keycloak admin authentication, and the Velocity admission plugin. ## Automated checks - Semgrep `auto`: 0 findings - `npm audit`: 0 known vulnerabilities after dependency overrides - TypeScript, ESLint, unit tests, Next.js production build: passing - Velocity Java tests and shaded plugin build: passing ## Implemented controls - Discord login and session tokens use cryptographically secure randomness and are stored only as SHA-256 hashes. - Login links expire after ten minutes, are single use, and are rate limited per Discord user with a PostgreSQL advisory lock. - Session cookies are `httpOnly`, `sameSite=lax`, path-scoped, and secure in production. - Admin access uses Keycloak OIDC and a required role. - User mutations verify ownership server-side. - Mojang lookup is server-side and targets a fixed host, avoiding client-forged validation and SSRF. - Velocity credentials are high-entropy bearer tokens stored only as hashes. - Velocity admission and confirmed-connection requests have a 45-second clock window and database-unique request IDs for cross-instance replay prevention. - Velocity and its API fail closed. - Registered players require an enabled effective group; explicit assignments replace rather than combine with the protected, disabled-by-default `everyone` fallback. - Confirmed VPN, proxy, and Tor game connections are denied unless that same effective group has an explicit exception; `everyone` and new groups default to no exception. - Group, VPN-policy, identity, and membership mutations re-check the Keycloak administrator role server-side; registry assignments, policy changes, group edits, creation, deletion, and message settings commit atomically with their audit events. - Group identity creation/rename and membership assignment/deletion use compatible PostgreSQL advisory and row locks to prevent duplicate names, stale audit records, or membership/deletion races. The protected default name and deletion restriction are enforced server-side. - Every bearer-authenticated Velocity login uses cached IP intelligence before identity resolution, preventing account-creation races from bypassing network policy; malformed provider proxy signals classify as unknown. - Event filters accept only event types already present in the ledger, and event detail routes remain role-protected. - The administrator-only map defaults to bundled Natural Earth boundaries. OpenStreetMap tile requests begin only after an explicit operator opt-in; marker coordinates are not transmitted as data, but the requested tiles disclose the viewed geographic extent along with the administrator's IP and portal origin. - Grouped-map popup labels and links are created with DOM `textContent` and server-rendered React escaping rather than interpolated HTML. - ORM-parameterized queries are used throughout. - CSP, clickjacking, MIME-sniffing, referrer, and browser-permission headers are configured. - Forwarded IP headers are ignored unless `TRUST_PROXY=true` is explicitly configured. - Private and reserved addresses are not sent to ProxyCheck.io; lookup results are cached to reduce disclosure and API usage. - Portal and game login events include approximate network location and VPN/proxy classification when available. The administrator-only location list also exposes enriched network company, ASN, connection type, and the provider's proxy signal. - Structured Pino logging redacts credential fields, and secrets are excluded from logs and repository configuration. ## Outstanding production requirements - Monitor ProxyCheck.io usage, detection quality, and false positives. User account additions fail closed when classification is unavailable; hosting-provider blocking remains optional. - Define and automate retention for exact IP addresses, cached provider responses, approximate location, and audit events. - Add monitoring and alerts for repeated login denials, plugin authentication failures, and Discord API failures. - Use HTTPS for the public application and Velocity API URL. Protect the Velocity configuration file because it contains the one-time-displayed API token. - Restrict database credentials so normal application roles cannot update or delete historical event rows outside approved application paths. - Validate migrations in staging before production. Local migration application was unavailable during development because the Docker daemon was not running.