block / block/buzz

buzz-relay has zero HTTP request/body timeouts — unauthenticated clients can pin connections indefinitely by withholding request bodies

Open
#4,424 1 comment 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.