fix(gateway): bound per-connection send queue to prevent memory DoS #98

Merged
icub3d merged 2 commits from feature/81-bounded-ws-queue into main 2026-07-09 03:04:20 +00:00
Owner

Summary

Fixes #81. Each WebSocket connection used an unbounded mpsc channel for outbound messages. A client that connects, authenticates, then stops reading grows its queue without bound as server activity continues — one idle socket per attacker can exhaust memory.

Fix

  • Bounded channel sized by new config gateway.send_queue_capacity (default 256; validated > 0).
  • broadcast_to_channel / send_to_user / broadcast_all now try_send and collect connections whose queue is full or closed, then drop those senders in a short write-locked pass.
  • The registry owns the sole sender, so dropping it closes the connection's receiver; the connection task then exits and unregisters itself. A slow consumer is shed, not buffered.
  • The connection task now sends Hello/Ready straight to the socket instead of through its own sender clone (which would otherwise keep the receiver alive after a shed).

Tests

  • full_queue_drops_the_slow_connection: capacity-1 queue, second send with no reader → connection dropped.
  • Config validation rejects send_queue_capacity = 0.

Full server suite green (116 tests); clippy -D warnings clean.

Closes #81

## Summary Fixes #81. Each WebSocket connection used an unbounded `mpsc` channel for outbound messages. A client that connects, authenticates, then stops reading grows its queue without bound as server activity continues — one idle socket per attacker can exhaust memory. ## Fix - Bounded channel sized by new config `gateway.send_queue_capacity` (default 256; validated `> 0`). - `broadcast_to_channel` / `send_to_user` / `broadcast_all` now `try_send` and collect connections whose queue is full or closed, then drop those senders in a short write-locked pass. - The registry owns the **sole** sender, so dropping it closes the connection's receiver; the connection task then exits and unregisters itself. A slow consumer is *shed*, not buffered. - The connection task now sends Hello/Ready straight to the socket instead of through its own sender clone (which would otherwise keep the receiver alive after a shed). ## Tests - `full_queue_drops_the_slow_connection`: capacity-1 queue, second send with no reader → connection dropped. - Config validation rejects `send_queue_capacity = 0`. Full server suite green (116 tests); `clippy -D warnings` clean. Closes #81
fix(gateway): bound per-connection send queue to prevent memory DoS
Some checks failed
Security Audit / Gitleaks (Secrets) (pull_request) Successful in 10s
Security Audit / Trivy (SCA & IaC) (pull_request) Failing after 17s
Security Audit / PNPM Audit (JS SCA) (pull_request) Failing after 23s
CI / Client — test & lint (pull_request) Successful in 45s
Security Audit / Semgrep (SAST) (pull_request) Failing after 1m14s
CI / Server — test & lint (pull_request) Successful in 2m54s
Security Audit / Cargo Audit (Rust SCA) (pull_request) Failing after 3m48s
37e8100265
Each WebSocket connection used an unbounded mpsc channel for outbound
messages. A client that connects, authenticates, then stops reading would
grow its queue without bound as server activity continued — one idle
socket per attacker could exhaust server memory.

- Use a bounded channel (config gateway.send_queue_capacity, default 256).
- Broadcast/send methods now try_send and collect connections whose queue
  is full or closed, then drop those senders. The registry owns the sole
  sender, so dropping it closes the connection's receiver and the task ends
  and unregisters itself — a slow consumer is shed rather than buffered.
- The connection task sends Hello/Ready straight to the socket so it no
  longer holds its own sender clone (which would keep the receiver alive
  after a shed).

Tests: a full queue drops the slow connection; validation rejects a
zero send_queue_capacity.

Closes #81

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Merge branch 'main' into feature/81-bounded-ws-queue
Some checks failed
CI / Client — test & lint (pull_request) Successful in 1m4s
Security Audit / Trivy (SCA & IaC) (pull_request) Failing after 22s
Security Audit / PNPM Audit (JS SCA) (pull_request) Failing after 31s
Security Audit / Gitleaks (Secrets) (pull_request) Successful in 8s
Security Audit / Semgrep (SAST) (pull_request) Failing after 1m1s
CI / Server — test & lint (pull_request) Successful in 7m20s
Security Audit / Cargo Audit (Rust SCA) (pull_request) Failing after 9m26s
6b74c26f35
# Conflicts:
#	server/src/gateway/connection.rs
#	server/src/gateway/registry.rs
icub3d merged commit 9d951adea2 into main 2026-07-09 03:04:20 +00:00
icub3d deleted branch feature/81-bounded-ws-queue 2026-07-09 03:04:21 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
icub3d/decentcom!98
No description provided.