Milestone v0.32.0. From #46 item 6, reproduced by the phase-1 investigation.internal/adapters/llm/client.go runs io.ReadAll(resp.Body) before the status check, so a misbehaving gateway or proxy that streams a huge body is bounded only by the client's overall HTTP timeout; this client serves the summarizer, chat and the reconciliation adjudicator. (The error TEXT is already bounded to 512 bytes since v0.31.0; the read is not.)
Fix: wrap the body in io.LimitReader (about 8 MiB for a success body, and read at most maxErrorBody when the status is not 200). Red test first:TestClient_ABodyLargerThanTheCapIsAnErrorNotAnAllocation (a streaming server returning more than the cap gives an error and the client never holds more than the cap). Acceptance: red first, then green; task check and golangci-lint run ./... exit 0.
**Milestone v0.32.0. From #46 item 6, reproduced by the phase-1 investigation.** `internal/adapters/llm/client.go` runs `io.ReadAll(resp.Body)` before the status check, so a misbehaving gateway or proxy that streams a huge body is bounded only by the client's overall HTTP timeout; this client serves the summarizer, chat and the reconciliation adjudicator. (The error TEXT is already bounded to 512 bytes since v0.31.0; the read is not.)
**Fix:** wrap the body in `io.LimitReader` (about 8 MiB for a success body, and read at most `maxErrorBody` when the status is not 200). **Red test first:** `TestClient_ABodyLargerThanTheCapIsAnErrorNotAnAllocation` (a streaming server returning more than the cap gives an error and the client never holds more than the cap). **Acceptance:** red first, then green; `task check` and `golangci-lint run ./...` exit 0.
---
_Created via git-mcp on behalf of @mathias_
mathias
added this to the v0.32.0 - audio upload follow-ups milestone 2026-10-03 18:48:25 +00:00
Shipped in v0.32.0 (PR #58, merge 85f4cc3; running in production since 2026-10-04, pod 1/1, 0 restarts, now image 7b7da65). The LLM client reads a success body up to 8 MiB (defaultMaxResponseBytes) and errors with "LLM response exceeds N bytes" beyond it; a failing response is read only up to 513 bytes.
Evidence: red tests first (bodycap_test.go), then pinned at the boundary by the adversarial review: body of cap-1 and exactly cap accepted, cap+1 refused, the production default (client built with no option) at exactly 8 MiB passes and 8 MiB + 1 is refused, a non-positive override keeps the default. Mutations that went red: >→>=, a 1 TiB default, 4 MiB and 16 MiB defaults, n > 0→n > -1. Reviewer also executed a gzip bomb (411 KB compressed, ~200 MiB decompressed): rejected in 0.2 ms, the cap applies after decompression, and the 429-then-200 retry still works.
Not observed live: there is no way to make the production gateway stream an oversized body, so the behaviour rests on the tests and the review, not on a live trigger. Closing on that basis.
**Shipped in v0.32.0** (PR #58, merge 85f4cc3; running in production since 2026-10-04, pod 1/1, 0 restarts, now image 7b7da65). The LLM client reads a success body up to 8 MiB (`defaultMaxResponseBytes`) and errors with "LLM response exceeds N bytes" beyond it; a failing response is read only up to 513 bytes.
Evidence: red tests first (`bodycap_test.go`), then pinned at the boundary by the adversarial review: body of cap-1 and exactly cap accepted, cap+1 refused, the production default (client built with no option) at exactly 8 MiB passes and 8 MiB + 1 is refused, a non-positive override keeps the default. Mutations that went red: `>`→`>=`, a 1 TiB default, 4 MiB and 16 MiB defaults, `n > 0`→`n > -1`. Reviewer also executed a gzip bomb (411 KB compressed, ~200 MiB decompressed): rejected in 0.2 ms, the cap applies after decompression, and the 429-then-200 retry still works.
Not observed live: there is no way to make the production gateway stream an oversized body, so the behaviour rests on the tests and the review, not on a live trigger. Closing on that basis.
---
_Created via git-mcp on behalf of @mathias_
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Milestone v0.32.0. From #46 item 6, reproduced by the phase-1 investigation.
internal/adapters/llm/client.gorunsio.ReadAll(resp.Body)before the status check, so a misbehaving gateway or proxy that streams a huge body is bounded only by the client's overall HTTP timeout; this client serves the summarizer, chat and the reconciliation adjudicator. (The error TEXT is already bounded to 512 bytes since v0.31.0; the read is not.)Fix: wrap the body in
io.LimitReader(about 8 MiB for a success body, and read at mostmaxErrorBodywhen the status is not 200). Red test first:TestClient_ABodyLargerThanTheCapIsAnErrorNotAnAllocation(a streaming server returning more than the cap gives an error and the client never holds more than the cap). Acceptance: red first, then green;task checkandgolangci-lint run ./...exit 0.Created via git-mcp on behalf of @mathias
Shipped in v0.32.0 (PR #58, merge 85f4cc3; running in production since 2026-10-04, pod 1/1, 0 restarts, now image
7b7da65). The LLM client reads a success body up to 8 MiB (defaultMaxResponseBytes) and errors with "LLM response exceeds N bytes" beyond it; a failing response is read only up to 513 bytes.Evidence: red tests first (
bodycap_test.go), then pinned at the boundary by the adversarial review: body of cap-1 and exactly cap accepted, cap+1 refused, the production default (client built with no option) at exactly 8 MiB passes and 8 MiB + 1 is refused, a non-positive override keeps the default. Mutations that went red:>→>=, a 1 TiB default, 4 MiB and 16 MiB defaults,n > 0→n > -1. Reviewer also executed a gzip bomb (411 KB compressed, ~200 MiB decompressed): rejected in 0.2 ms, the cap applies after decompression, and the 429-then-200 retry still works.Not observed live: there is no way to make the production gateway stream an oversized body, so the behaviour rests on the tests and the review, not on a live trigger. Closing on that basis.
Created via git-mcp on behalf of @mathias