security: remediate all P0-P2 audit findings (12 tasks)
P0 fixes: - WebSocket origin checking (reject untrusted origins) - WS session token moved from URL query param to first message frame - WebAuthn login cookie now uses Secure flag via shared SetSessionCookie - PostgreSQL sslmode configurable via POSTGRES_SSLMODE env (default: require) - Fixed DatabaseDSN to use real password instead of masked placeholder P1 fixes: - Per-IP rate limiting middleware (auth: 5 req/s, invites: 2 req/s) - Security headers on all responses (CSP, X-Frame-Options, nosniff, etc.) - WS broadcasts scoped to server members (prevents cross-server data leak) - Webhook tokens stored as SHA-256 hashes (not plaintext) P2 fixes: - CSRF protection via Origin header validation on state-changing requests - MANAGE_CHANNELS permission enforced on channel update/delete - Upload validation: 25MB limit, extension allowlist, server-side MIME check - File serve: path traversal protection + Content-Disposition: attachment Files: 23 changed, +499/-100. Builds clean (go build, go vet, npm build). Zero CVEs (govulncheck, npm audit).
This commit is contained in:
+18
-3
@@ -70,7 +70,7 @@ export const useWebSocketStore = create<WebSocketState>((set, get) => ({
|
||||
return;
|
||||
}
|
||||
|
||||
const socket = new WebSocket(`${getWsHost()}/ws?token=${encodeURIComponent(token)}`);
|
||||
const socket = new WebSocket(`${getWsHost()}/ws`);
|
||||
|
||||
let reconnectDelay = 1000;
|
||||
const maxReconnectDelay = 30000;
|
||||
@@ -89,8 +89,9 @@ export const useWebSocketStore = create<WebSocketState>((set, get) => ({
|
||||
};
|
||||
|
||||
socket.onopen = () => {
|
||||
set({ connected: true });
|
||||
reconnectDelay = 1000;
|
||||
// Send auth token as the first message frame instead of a URL param.
|
||||
// This keeps the token out of server logs, browser history, and Referer headers.
|
||||
socket.send(JSON.stringify({ token }));
|
||||
};
|
||||
|
||||
socket.onmessage = (event) => {
|
||||
@@ -101,6 +102,20 @@ export const useWebSocketStore = create<WebSocketState>((set, get) => ({
|
||||
return;
|
||||
}
|
||||
|
||||
// Server sends {"type":"ready"} after successful auth.
|
||||
if (data.type === 'ready') {
|
||||
set({ connected: true });
|
||||
reconnectDelay = 1000;
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type === 'error') {
|
||||
// Auth failed or server sent an error. Close and don't reconnect.
|
||||
console.error('ws error:', data);
|
||||
socket.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.type === 'ping') {
|
||||
get().send({ type: 'pong', payload: {} });
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user