Skip to content

[META] Transport payload-size limits: consistent, correct, cross-transport enforcement #1907

Description

@oberstet

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
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).

Children (dependency-ordered)

  1. [BUG] permessage-deflate max_message_size cap silently truncates and corrupts messages (missing unconsumed_tail drain) #1908
  2. [BUG] WebSocket maxMessagePayloadSize is enforced against compressed size, bypassed after permessage-deflate inflation #1909
  3. [BUG] Backend-agnostic bounded decompression for permessage-compress (snappy / bzip2 / brotli parity with deflate) #1910
  4. [BUG] asyncio RawSocket receive size limit is hardcoded to 16 MB (no setProtocolOptions), unlike the Twisted backend #1911
  5. [BUG] WebSocket size-limit config: fill the empty compression validator, clarify compressed-vs-uncompressed docs, fix the zip-bomb functest and put it in CI crossbar#2250

Cross-references

  • Security advisory: GHSA-hxp9-w8x3-p566 (public; developed in the open).
  • Related class: CVE-2016-10544 (ws / Node.js — same compressed-vs-decompressed
    boundary mistake).
  • Ties into the Track T testing epic (parametrized both-backend / both-transport
    / 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions