Add WebSocket fragment timeout and size limits#29
Conversation
Fragment accumulation in ws-frag-bufs previously had no TTL or size limit independent of max-request-size. A malicious client could send an initial fragment and never complete it, leaking memory indefinitely. - ConnState gains ws-frag-start (Map Int Int) to track when each connection began accumulating fragments - App.ws-frag-ttl (default 30s): sweep-idle closes connections where fragments have been accumulating longer than this threshold - App.ws-max-frag-size (default 1 MiB): separate configurable limit for accumulated fragment size, checked on first fragment and each continuation frame - Fragment start timestamps are cleaned up on completion, protocol error, size limit breach, and connection teardown
There was a problem hiding this comment.
Build & Tests
CI passes (macos-latest — the only platform in this repo's CI matrix). Tests pass, including the two new assertions for ws-frag-ttl and ws-max-frag-size defaults.
Findings
Thorough and well-executed security hardening. Detailed observations:
-
Cleanup coverage is complete.
ws-frag-startis removed at every exit point: protocol error (new message during fragmentation, line 1708), accumulated size limit breach (line 1737), message completion (line 1756), unfragmented message during fragmentation (line 1781), and connection teardown viaconn-cleanup(line 1494). I checked each one against the original fragment cleanup sites and all are covered. -
sweep-idleTTL enforcement (lines 2098–2106): Thelet→let-dochange is correct — thecondfor ping/pong and thewhenfor fragment TTL both execute sequentially for each WebSocket connection. The guard(> App.ws-frag-ttl 0)correctly allows disabling the feature. Ifqueue-closewas already called by the ping/pong logic for a dead client, calling it again for stale fragments is harmless (double-add toto-closearray,conn-cleanupremoves already-removed map entries as no-ops). -
First-fragment size check (line 1711): Correctly gates on
(> plen App.ws-max-frag-size)before storing the payload — no wasted allocation on oversized first fragments. -
Continuation frame size check (line 1732): Changed from
App.max-request-sizetoApp.ws-max-frag-size— correct and intentional. Both default to 1048576, so no behavioral change with default settings, but operators can now tune WebSocket fragment limits independently. -
ConnState initializer (line 2201): 19 fields, 19 initializers (18
{}+ 1[]). The additional{}forws-frag-startis in the right position. -
CHANGELOG updated — good.
-
Test coverage: The new tests verify the constant defaults, which is the right thing for compile-time checking. Behavioral tests for TTL enforcement and size limits are hard to write without a test harness that controls
System.time, so their absence is understandable.
No bugs found.
Verdict: merge
Solid security hardening that completes the fragment accumulation protection. Clean code, CHANGELOG updated, CI green. Ready to merge.
Summary
Fragment accumulation in
ws-frag-bufspreviously had no TTL and relied onApp.max-request-sizefor its only size check. A malicious client could send an initial fragment and never complete it, leaking memory indefinitely. This builds on the slowloris protection work in PR #27.Changes
ConnState.ws-frag-start— new(Map Int Int)field that records theSystem.timewhen each connection begins accumulating fragments. Cleaned up on completion, protocol error, size limit breach, and connection teardown.App.ws-frag-ttl(default 30 seconds) —sweep-idlenow checks whether any WebSocket connection has been accumulating fragments longer than this threshold and closes it if so. Setting to 0 disables the check.App.ws-max-frag-size(default 1 MiB) — separate configurable limit for accumulated fragment size, independent ofApp.max-request-size. Checked when the first fragment arrives and on each continuation frame. Allows operators to tune WebSocket message limits without affecting HTTP request sizes.New constants
App.ws-frag-ttlApp.ws-max-frag-sizeTests
Added tests verifying the default values of
App.ws-frag-ttlandApp.ws-max-frag-size. All existing tests continue to pass.Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.