mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
Spec Documentation (18 files, 680KB): - Expanded all 15 existing spec files with deep detail from source code - Created 3 new specs: DM-SYSTEM, THEME-SYSTEM, RECONNECTION - Created E2E-BEST-PRACTICES spec - Audited all specs against source: fixed 50 errors Unit Tests (143 new): - Go: dm_queries_test (21), dm_handler_test (17), dm_handlers_test (18), ringbuffer_test (22) - TS: dm-store (16), disposable (14), themes security (17), ws reconnection (8), dispatcher DM (2) E2E Tests (22 mocked + 6 native specs): - New: dm-system, theme-persistence, reconnection (mocked + native) - Fixed 12 fake assertions, 18 hardcoded timeouts, 5 stale selectors - Persistent fixture: login once per run instead of per test - ensureLoggedIn with exponential backoff for rate limiting Security Fixes: - DM auth bypass: added IsDMParticipant to handleGetPins, handleSetPinned, handleSearch - LiveKit InsecureVerifier replaced with PinnedVerifier (TOFU from shared cert store) - IDOR leak: handleChatEdit/Delete now return opaque error codes - CSS injection: added deny-list for dangerous CSS functions in themes - BANNED error now triggers logout instead of infinite reconnect - CredFree leak fixed: Windows credential memory freed before parsing - Login lockout off-by-one: limit=9 so 10th failure triggers lockout Stability Fixes: - Rate limiter StartCleanup goroutine now started (prevents memory leak) - Voice mute/deafen rate limiting added (2/sec, matching camera/screenshare) - DM typing no longer echoes back to sender - Accept loop spin protection (5 consecutive error limit) - voice_config protocol drift resolved (3 missing fields added) - Login rate limit set to 60/min (spec updated, 10-failure lockout is real protection) - Hardcoded roleNameToId replaced with dynamic lookup from ready payload Co-Authored-By: claude-flow <ruv@ruv.net>
51 lines
1.5 KiB
Go
51 lines
1.5 KiB
Go
package ws
|
|
|
|
import (
|
|
"log/slog"
|
|
"time"
|
|
)
|
|
|
|
// Voice rate limit settings.
|
|
const (
|
|
voiceMuteRateLimit = 2
|
|
voiceMuteWindow = time.Second
|
|
voiceDeafenRateLimit = 2
|
|
voiceDeafenWindow = time.Second
|
|
voiceCameraRateLimit = 2
|
|
voiceCameraWindow = time.Second
|
|
voiceScreenshareRateLimit = 2
|
|
voiceScreenshareWindow = time.Second
|
|
)
|
|
|
|
// voiceQualities maps accepted voice quality presets to their target bitrate
|
|
// in bits/s. This is the single source of truth — voice_join.go validates
|
|
// against these keys, qualityBitrate looks up the value.
|
|
var voiceQualities = map[string]int{
|
|
"low": 32000,
|
|
"medium": 64000,
|
|
"high": 128000,
|
|
}
|
|
|
|
// qualityBitrate returns the target audio bitrate in bits/s based on a quality preset.
|
|
func qualityBitrate(quality string) int {
|
|
if bitrate, ok := voiceQualities[quality]; ok {
|
|
return bitrate
|
|
}
|
|
return voiceQualities["medium"]
|
|
}
|
|
|
|
// broadcastVoiceStateUpdate fetches the current voice state for the client
|
|
// and broadcasts it to all members of the voice channel they are in.
|
|
func (h *Hub) broadcastVoiceStateUpdate(c *Client) {
|
|
state, err := h.db.GetVoiceState(c.userID)
|
|
if err != nil {
|
|
slog.Error("ws broadcastVoiceStateUpdate GetVoiceState", "err", err, "user_id", c.userID)
|
|
c.sendMsg(buildErrorMsg(ErrCodeInternal, "failed to broadcast voice state update"))
|
|
return
|
|
}
|
|
if state == nil {
|
|
return // user not in a voice channel — nothing to broadcast
|
|
}
|
|
h.BroadcastToAll(buildVoiceState(*state))
|
|
}
|