You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a tracking epic for a coordinated hardening of payload/message size
limits across the Autobahn|Python transports (WebSocket + RawSocket) and their
use in Crossbar.io. It originates from a reported denial-of-service issue
(security advisory GHSA-hxp9-w8x3-p566, "permessage-deflate bypasses maxMessagePayloadSize after inflation", reported by Team Atlanta), but the
investigation surfaced a small cluster of related defects and design gaps that
are best fixed together so the size-limit model is finally coherent.
No development happens on this issue. Each child below is fixed on its own
issue + branch + PR, following our TDD red/green workflow (add the failing test
first, prove it red in CI, then fix, prove it green — all on the child's PR).
This epic is closed manually once all children are done.
The core problem: one knob, four possible meanings
There are two independent axes to a "size limit":
on-the-wire (compressed)
at-the-application (uncompressed)
per-frame
maxFramePayloadSize (correct)
(no knob — covered transitively)
per-message
(no knob — covered transitively)
maxMessagePayloadSize
Today maxMessagePayloadSize is documented as bounding the "re-assembled
payload" (uncompressed), enforced on send against the uncompressed message,
and enforced by RawSocket against the (uncompressed, no-compression) wire
message — but on the WebSocket receive path (Hybi/v13) it is enforced against
the compressed frame length, before inflation. A tiny compressed frame can
therefore inflate past the configured limit and reach the application. Hixie
(v0) already accounts uncompressed bytes correctly; Hybi is the outlier.
Agreed design (see child issues for detail)
D1 — semantic anchor.maxMessagePayloadSize = maximum size of a single
serialized WAMP message (transport payload), measured uncompressed — i.e.
before compression on send, after decompression on receive. This is already
true on send, on RawSocket, and on Hixie receive; we bring Hybi receive in
line. Default stays 0 (unlimited), so no existing deployment changes.
D2 — two knobs, correctly defined.maxMessagePayloadSize (per-message,
uncompressed) + maxFramePayloadSize (per-frame, wire). The other two matrix
cells are covered transitively (enforcing the uncompressed per-message cap during inflation also bounds a single-frame bomb; bounding uncompressed
transitively bounds accepted wire bytes). We deliberately do not add more
knobs — the existing two, defined correctly, are sufficient and less confusing.
Enforcement site. Backend-agnostic, at the inflation site in onFrameData, so it works identically in message mode and streaming
(frame-based) mode (which differ only by which onMessage* callbacks a
consumer overrides), and independently of the compression backend
(deflate / snappy / bzip2 / brotli).
Summary
This is a tracking epic for a coordinated hardening of payload/message size
limits across the Autobahn|Python transports (WebSocket + RawSocket) and their
use in Crossbar.io. It originates from a reported denial-of-service issue
(security advisory GHSA-hxp9-w8x3-p566, "permessage-deflate bypasses
maxMessagePayloadSizeafter inflation", reported by Team Atlanta), but theinvestigation surfaced a small cluster of related defects and design gaps that
are best fixed together so the size-limit model is finally coherent.
No development happens on this issue. Each child below is fixed on its own
issue + branch + PR, following our TDD red/green workflow (add the failing test
first, prove it red in CI, then fix, prove it green — all on the child's PR).
This epic is closed manually once all children are done.
The core problem: one knob, four possible meanings
There are two independent axes to a "size limit":
maxFramePayloadSize(correct)maxMessagePayloadSizeToday
maxMessagePayloadSizeis documented as bounding the "re-assembledpayload" (uncompressed), enforced on send against the uncompressed message,
and enforced by RawSocket against the (uncompressed, no-compression) wire
message — but on the WebSocket receive path (Hybi/v13) it is enforced against
the compressed frame length, before inflation. A tiny compressed frame can
therefore inflate past the configured limit and reach the application. Hixie
(v0) already accounts uncompressed bytes correctly; Hybi is the outlier.
Agreed design (see child issues for detail)
maxMessagePayloadSize= maximum size of a singleserialized WAMP message (transport payload), measured uncompressed — i.e.
before compression on send, after decompression on receive. This is already
true on send, on RawSocket, and on Hixie receive; we bring Hybi receive in
line. Default stays
0(unlimited), so no existing deployment changes.maxMessagePayloadSize(per-message,uncompressed) +
maxFramePayloadSize(per-frame, wire). The other two matrixcells are covered transitively (enforcing the uncompressed per-message cap
during inflation also bounds a single-frame bomb; bounding uncompressed
transitively bounds accepted wire bytes). We deliberately do not add more
knobs — the existing two, defined correctly, are sufficient and less confusing.
onFrameData, so it works identically in message mode and streaming(frame-based) mode (which differ only by which
onMessage*callbacks aconsumer overrides), and independently of the compression backend
(deflate / snappy / bzip2 / brotli).
Children (dependency-ordered)
max_message_sizecap silently truncates and corrupts messages (missingunconsumed_taildrain) #1908maxMessagePayloadSizeis enforced against compressed size, bypassed after permessage-deflate inflation #1909setProtocolOptions), unlike the Twisted backend #1911Cross-references
boundary mistake).
/ multi-codec size-limit coverage; Crossbar zip-bomb functest into CI).
This work is being completed with AI assistance (Claude Code); all commits are
human-reviewed per AI_POLICY.md.