Applies fixes for 20 adversarially-verified findings from a whole-codebase
security review (server side). All Go build-tag variants build, `go vet` is
clean, and the suite passes (the sole failing test, ws TestEmitEvents, is a
pre-existing nil-harness failure unrelated to these changes).
High severity:
- auth: close TOCTOU in TOTP verify rate-limit by recording each attempt
atomically up-front (was Check-then-Allow), restoring the per-user
brute-force cap.
- plugin: enforce the CPU/time budget on every WASM guest call via a
WithTimeout context (WithCloseOnContextDone interrupts runaways); the
configured budget was previously parsed but never applied.
- api/waf: inspect request bodies for chunked (ContentLength==-1) requests
so the SQLi/XSS/RCE body rules can no longer be bypassed.
- ws: rate-limit voice_join/voice_leave and voice_e2ee announce/offer, which
fan out to every participant and could force mass disconnects.
Medium severity:
- api: run bcrypt on the unknown-user login path (no || short-circuit) to
remove the timing-based username-enumeration oracle.
- ws: verify LiveKit webhooks via the SDK receiver so the signature is bound
to the body hash (kills forgery/replay).
- authz: require READ_MESSAGES for reactions and for plugin-command
broadcasts; route the latter through RequireChannelAccess.
- api: cache the client-update signature fetch and rate-limit the endpoint.
- service: propagate DeleteOtherSessions failure from ChangePassword instead
of silently reporting success.
- api: trust the rightmost non-proxy X-Forwarded-For entry, not the
client-controllable leftmost one.
- plugin: route auto-registered commands through the conflict-checked
RegisterCommand; pin the DNS-validated IP for host_http dials
(DNS-rebinding TOCTOU).
- api: mark access-controlled downloads private/no-cache + Vary: Origin.
Low severity:
- auth: fail closed when a fully-shaped TOTP ciphertext fails GCM auth
(was returning the ciphertext as plaintext).
- api: apply the livekit-proxy path allowlist to WebSocket upgrades too.
- service: verify attachment ownership before linking (IDOR).
- admin: bound the bootstrap setup invite (5 uses / 24h); re-verify the
update binary hash immediately before rename+spawn (TOCTOU).
- service: require BanMembers + role hierarchy for moderation ban/unban.
chore: stop tracking the stray Server/owncord-server.exe build artifact.
Test infra: add uploader_id to the hand-rolled ws test attachment schemas and
make MemStore.GetAttachmentByID a no-op lookup, matching production/DB behavior.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- I-1: Add key holder election in Hub (lowest userID per channel); reject
non-key-holder voice_e2ee_offer with NOT_KEY_HOLDER error
- I-2: Accept raw (unpadded) base64 in E2EE announce/offer handlers via
decodeBase64Loose fallback
- I-6: Copy E2EE public key value while h.mu.RLock is held in getClientE2EEPubKey
- I-7: Lower loginRateLimitPerMinute from 60 to 5
- C-1: TOCTOU fix — target channel check held under same lock as client lookup
- C-2: Include is_key_holder bool in voice_token payload so client knows
whether to initiate key distribution
- M-5/M-6: Add ErrCodeBadPayload/ErrCodeNotKeyHolder error constants
- Fix pre-existing api build errors: block_handler.go getUserFromContext,
router.go RequirePermission arg count
- Add user_blocks table to all test DB schemas (ws, api DM)
- Add voice_e2ee_test.go and constants_test.go covering all fixes
- Add validateAvatarURL helper enforcing https:// scheme, non-empty host, and 512-char max length
- Add rate limiting (10/min) to PATCH /api/v1/users/me profile update endpoint
- Guard avatar rendering in DmSidebar, DmProfileSidebar, and UserProfilePopup with isSafeUrl check to prevent unsafe URL injection in the UI
Upload endpoint now enforces 10 uploads/min per user via the existing
RateLimiter. Previously only body size was capped (100 MiB) with no
per-user throttle, allowing authenticated users to exhaust disk with
repeated uploads.
BUG-110: Login handler now tracks failures per-username alongside per-IP.
Distributed brute force from rotating IPs is blocked after 9 failures
for the same username within 15 minutes.
BUG-111: Password-change, TOTP enable/confirm/disable endpoints now have
per-user escalating lockout (3 failures / 15min window / 15min lock),
matching the existing delete-account pattern. Prevents password oracle
attacks via stolen session tokens.
Create Server/api/constants.go (26 constants) and Server/auth/constants.go
(6 constants) for rate limits, timeouts, size limits, and token generation.
Replace all inline magic numbers with descriptive names across 9 source files.
No behavior changes — same values, just named for contributor readability.