Skip to content

fix: limit HTTP response body reads to prevent OOM#2296

Merged
Subserial merged 2 commits into
google:mainfrom
evilgensec:fix/limit-http-response-body-reads
May 12, 2026
Merged

fix: limit HTTP response body reads to prevent OOM#2296
Subserial merged 2 commits into
google:mainfrom
evilgensec:fix/limit-http-response-body-reads

Conversation

@evilgensec

Copy link
Copy Markdown
Contributor

Problem

Three paths read HTTP response bodies from a registry with no size limit, allowing a malicious or compromised registry to exhaust client memory:

1. pkg/v1/remote/referrers.go

b, err = io.ReadAll(resp.Body)  // Referrers API — no size cap

The manifest fetch path caps at manifestLimit (100 MiB), but the Referrers fast path bypasses this entirely.

2. pkg/v1/remote/transport/error.goCheckError and retryError

b, err := io.ReadAll(resp.Body)  // fires on every non-2xx response

Triggered before any authentication succeeds (e.g., on the initial 401 challenge), so a malicious registry can exhaust memory without the client ever authenticating.

3. pkg/v1/remote/transport/bearer.gorefreshOauth and refreshBasic

return io.ReadAll(resp.Body)  // token endpoint responses

A registry whose WWW-Authenticate realm points to a large-response server (or a compromised token endpoint) can cause OOM during every token refresh.

Fix

  • Referrers: apply the existing manifestLimit (100 MiB) constant.
  • Error bodies and token responses: add maxErrorBodySize (64 KiB) — sufficient for any structured registry error JSON or token response.

All existing tests pass.

Three unbounded io.ReadAll calls on HTTP response bodies from untrusted
registries could allow a malicious or compromised registry to exhaust
the client's memory:

- pkg/v1/remote/referrers.go: Referrers API response body read with no
  size limit, unlike the manifest fetch path which caps at manifestLimit
  (100 MiB). Apply the same manifestLimit cap.

- pkg/v1/remote/transport/error.go: CheckError and retryError read
  error response bodies unboundedly. These fire on any non-2xx response,
  including the initial auth challenge (401). Apply a 64 KiB cap via
  maxErrorBodySize — sufficient for any structured registry error JSON.

- pkg/v1/remote/transport/bearer.go: refreshOauth and refreshBasic read
  token endpoint responses unboundedly. Apply the same 64 KiB cap.
@evilgensec evilgensec force-pushed the fix/limit-http-response-body-reads branch from 82616d6 to cbd9b4e Compare May 7, 2026 15:25
Comment thread pkg/v1/remote/transport/bearer.go Outdated
@codecov-commenter

codecov-commenter commented May 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 56.70%. Comparing base (fac4ed9) to head (5c0c5f6).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2296   +/-   ##
=======================================
  Coverage   56.70%   56.70%           
=======================================
  Files         165      165           
  Lines       11239    11239           
=======================================
  Hits         6373     6373           
  Misses       4106     4106           
  Partials      760      760           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Token endpoint success responses are not errors; introduce a separate
constant so the limit's purpose is clear at the call site.
@Subserial Subserial merged commit 341d8be into google:main May 12, 2026
17 checks passed
Subserial pushed a commit to Subserial/go-containerregistry that referenced this pull request May 15, 2026
* fix: limit HTTP response body reads to prevent OOM

Three unbounded io.ReadAll calls on HTTP response bodies from untrusted
registries could allow a malicious or compromised registry to exhaust
the client's memory:

- pkg/v1/remote/referrers.go: Referrers API response body read with no
  size limit, unlike the manifest fetch path which caps at manifestLimit
  (100 MiB). Apply the same manifestLimit cap.

- pkg/v1/remote/transport/error.go: CheckError and retryError read
  error response bodies unboundedly. These fire on any non-2xx response,
  including the initial auth challenge (401). Apply a 64 KiB cap via
  maxErrorBodySize — sufficient for any structured registry error JSON.

- pkg/v1/remote/transport/bearer.go: refreshOauth and refreshBasic read
  token endpoint responses unboundedly. Apply the same 64 KiB cap.

* transport: use dedicated maxTokenBodySize for bearer token reads

Token endpoint success responses are not errors; introduce a separate
constant so the limit's purpose is clear at the call site.

---------

Co-authored-by: evilgensec <sujaltuladhar1231@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants