Summary
The body-size limit middleware documents a 413 on oversized bodies. For the declared-Content-Length path this is correct and short-circuits inner middleware. But on the chunked/streaming path (no Content-Length), receive_guarded raises _BodyTooLarge while the endpoint is reading the body — and FastAPI wraps await request.body() / request.form() in a broad except Exception, converting it to HTTPException(400, "There was an error parsing the body"). The exception never reaches the middleware's except _BodyTooLarge (src/iceberg/auth/body_limit.py:61-83); the client gets 400, not 413.
Reproduced empirically: a 2 MB chunked (no Content-Length) POST to a pydantic-body endpoint behind this middleware → 400 {"detail":"There was an error parsing the body"}; the declared-length path correctly gives 413.
Impact
The DoS goal still holds (buffering stops at cap + one chunk), so this is a contract/correctness defect, not a security hole: the module docstring (body_limit.py:12-14) and the 413 contract are wrong for the streaming path, and the 400 is actively misleading to clients (implies malformed body, not oversized).
Why the suite misses it
tests/test_body_limit.py:23 builds a bare Starlette app — the one framework path the middleware actually protects in production (FastAPI) is untested.
Suggested fix
Signal the condition out-of-band rather than relying on exception propagation through FastAPI — e.g. set scope["iceberg.body_too_large"] = True and rewrite the response in send_tracked, or intercept the parse failure. Add an integration test through the real create_app() stack (bonus: assert the 413 carries the security headers, which is the stated reason for the middleware's position).
Found in Fable review of PR #232 → HEAD.
Summary
The body-size limit middleware documents a 413 on oversized bodies. For the declared-Content-Length path this is correct and short-circuits inner middleware. But on the chunked/streaming path (no Content-Length),
receive_guardedraises_BodyTooLargewhile the endpoint is reading the body — and FastAPI wrapsawait request.body()/request.form()in a broadexcept Exception, converting it toHTTPException(400, "There was an error parsing the body"). The exception never reaches the middleware'sexcept _BodyTooLarge(src/iceberg/auth/body_limit.py:61-83); the client gets 400, not 413.Reproduced empirically: a 2 MB chunked (no Content-Length) POST to a pydantic-body endpoint behind this middleware →
400 {"detail":"There was an error parsing the body"}; the declared-length path correctly gives 413.Impact
The DoS goal still holds (buffering stops at cap + one chunk), so this is a contract/correctness defect, not a security hole: the module docstring (
body_limit.py:12-14) and the 413 contract are wrong for the streaming path, and the 400 is actively misleading to clients (implies malformed body, not oversized).Why the suite misses it
tests/test_body_limit.py:23builds a bare Starlette app — the one framework path the middleware actually protects in production (FastAPI) is untested.Suggested fix
Signal the condition out-of-band rather than relying on exception propagation through FastAPI — e.g. set
scope["iceberg.body_too_large"] = Trueand rewrite the response insend_tracked, or intercept the parse failure. Add an integration test through the realcreate_app()stack (bonus: assert the 413 carries the security headers, which is the stated reason for the middleware's position).Found in Fable review of PR #232 → HEAD.