buzz-relay has zero HTTP request/body timeouts — unauthenticated clients can pin connections indefinitely by withholding request bodies
- Dominant language
- Rust
- Stars
- 32.7k
- Forks
- 4.3k
- Avg merge
- 1d 13h
- Merged PRs (30d)
- 253
Description
## Summary
`buzz-relay` has **no HTTP request or body-read timeouts anywhere**: nothing in `crates/buzz-relay/src/router.rs` or `main.rs` mounts a `TimeoutLayer` or read deadline, and `axum::serve` configures none (verified at `42e96a118` and on then-current main `318fbf896`; scope: rg over those files). `RequestBodyLimitLayer` bounds byte *count*, not elapsed *time*. Any client can open a connection, send headers, and drip or withhold the body indefinitely, pinning a connection/task per socket.
## Pre-auth reachable today, on multiple routes
Found by Wren while reviewing #4401 (whose probe middleware awaits 4 unauthenticated body bytes with no deadline). Survey during that review showed the seam is platform-wide, not new to #4401 — main already has *larger* unauthenticated deadline-free body reads:
- `accept_policy` (`invites.rs:199`) — no auth at all, buffers up to 1 MiB
- `claim_invite` — body buffered before `authenticate` runs
- `workflow_webhook` (`bridge.rs:1800`)
- and now the #4401 probe route (4 bytes, header-gated)
## Measured, not theoretical
Sami built the #4401 middleware standalone and opened 200 sockets sending the candidate headers while withholding the body:
```text
variant=A (PR as written) inflight_report: 200 answered_without_body=0
```
200 parked tasks, one per socket, indefinitely. A header-only predicate (variant B) removed the parking but was rejected as the wrong trade: the exact-body match is the best property of the #4401 shim, and dropping it to dodge a 4-byte park while `accept_policy` buffers 1 MiB deadline-free would be false comfort.
## Fix shape
A relay-wide timeout layer, not per-route patches:
1. `tower_http::timeout::TimeoutLayer` (or equivalent) on the router with a generous default (e.g. 30–60s), with the existing per-route git pack-ops timeouts (`PACK_OPS_TIMEOUT` 300s in `transport.rs`) taking precedence on long-lived routes.
2. Consider a hyper-level header-read timeout so pre-routing slowloris (headers never completing) is also bounded.
3. Regression test with a never-completing body against an unauthenticated route.
Websocket upgrade routes must be exempted from any body/request deadline.
## Credit
Mechanism identified by Wren; platform-wide survey by Eva; load measurement by Sami — all during the #4401 review (buzz-outside-pr-reviews, 2026-08-02). Related: #2723 (startup conformance probe has no deadline), #3651 (reader statements unbounded) — same missing-deadline family, different layers.
Refs: #4401.
Contributor guide
Assessment
This issue has not been assessed yet.