Skip to content

fix(nuget): harden README fetch with body size limit + redirect host-pinning#1398

Open
SaurabhMaydeo wants to merge 1 commit into
modelcontextprotocol:mainfrom
SaurabhMaydeo:fix/nuget-readme-hardening
Open

fix(nuget): harden README fetch with body size limit + redirect host-pinning#1398
SaurabhMaydeo wants to merge 1 commit into
modelcontextprotocol:mainfrom
SaurabhMaydeo:fix/nuget-readme-hardening

Conversation

@SaurabhMaydeo

@SaurabhMaydeo SaurabhMaydeo commented Jun 26, 2026

Copy link
Copy Markdown

Follow-up to #1330, applying the same README-fetch hardening to the NuGet validator. Scoped to NuGet — no behavior change for existing publishers.

NuGet and cargo are the only validators that read a raw README body (npm/pypi decode JSON metadata directly), so this finishes the pair started in #1330.

Changes

  • Cap the README body with io.LimitReader (5 MiB, matching maxCargoReadmeBytes), so an oversized response can't exhaust validator memory.
  • Pin redirects on the client: each hop must stay on the originating request's host, and on the real NuGet base must keep https + the default port. This covers the service-index, README, and package-index fetches. The decision is a small pure function (nugetRedirectAllowed) with a table test, mirroring cargo's cargoURLAllowed / TestCargoURLAllowed.

Not publisher-exploitable today — the README URL comes from the trusted service index and is served same-host — so this is defense-in-depth, same framing as #1330.

NuGet has no separate README CDN host like cargo's static.crates.io: the README URL is built from the service-index ReadmeUriTemplate and served from the same host. So redirects are pinned to the request's own host rather than a static allowlist. If NuGet ever serves READMEs cross-host, this would need to become an allowlist like cargo's.

Test plan

  • go test ./internal/validators/registries/ (incl. existing live-API tests)
  • New unit tests: TestNuGetRedirectAllowed (cross-host, http-downgrade, non-default-port, metadata-IP, userinfo) + httptest tests for README truncation at the cap and cross-host redirect refusal
  • gofmt, go vet, and golangci-lint v2.11.4 (the version CI pins) all clean — 0 issues

Docker/Postgres integration tests run in CI.

…pinning

validateReadme read the package README via io.ReadAll with no size cap, and
the shared http.Client had no CheckRedirect. Bound the read with
io.LimitReader (5 MiB) and pin every redirect hop to the originating
request's host (and, for the real NuGet base, forbid scheme/port
downgrades), so an oversized or redirected upstream response can't exhaust
validator memory or be steered at an unexpected host.

NuGet serves the service index, README, and package index directly without
cross-host redirects, so this is behaviour-preserving for real packages.
Mirrors the cargo hardening from modelcontextprotocol#1330. Adds tests for README truncation
and cross-host redirect refusal.
@SaurabhMaydeo SaurabhMaydeo force-pushed the fix/nuget-readme-hardening branch from 807b2ee to d6151d9 Compare June 26, 2026 09:50
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.

1 participant