Skip to content

fix(cargo): harden README fetch, clarify status handling, strengthen tests#1330

Open
rdimitrov wants to merge 1 commit into
mainfrom
followup/cargo-hardening
Open

fix(cargo): harden README fetch, clarify status handling, strengthen tests#1330
rdimitrov wants to merge 1 commit into
mainfrom
followup/cargo-hardening

Conversation

@rdimitrov
Copy link
Copy Markdown
Member

Follow-up to #1207, addressing items from the cargo validator review. Scoped to cargo + shared helpers — no behavior change for existing npm/pypi/nuget/oci/mcpb publishers. Safe to merge and promote.

Changes

SSRF hardening of the two-call README retrieval

  • Pin the step-2 README URL to an allowed host (static.crates.io in prod; the base host under test) before fetching, so a metadata response can't steer the validator at an internal/attacker host.
  • CheckRedirect policy pins every redirect hop to the same allowlist (the initial URL being pinned isn't enough if an upstream 3xx is followed).
  • Cap the README body with io.LimitReader (5 MiB).

Not publisher-exploitable today (the URL comes from crates.io), so this is defense-in-depth — but it makes the "host is pinned" guarantee hold in code rather than rely on crates.io's behavior.

Clearer status handling (previously any non-5xx/non-200 collapsed to "not found")

  • 429 → reported as transient/retryable, at both the metadata and README steps.
  • 403 → disambiguated via the crate-version endpoint: genuinely-missing stays "not found"; exists-but-no-README gets an actionable "add a README with mcp-name and republish" message (mirrors the NuGet validator). This continues the 5xx-vs-403 diagnostic split @P4ST4S started in the feat: add cargo (crates.io) as a package registry type #1207 review — thanks!

Shared containsMCPNameToken helper
Boundary-anchored ownership-token match (prevents prefix confusion, e.g. a README declaring io.github.acme/widget-pro satisfying a claim for io.github.acme/widget), used here by the cargo validator only. Adopting it in PyPI/NuGet is a separate follow-up because it's a behavior change for those already-live registries.

Tests & docs
Hermetic positive ServerNameFormats; combined fixture gains a version-existence endpoint + cases for 403-missing vs 403-no-README, 429, and prefix-confusion rejection; new foreign-README-host (SSRF) test; internal test for the helper. Docs: list crates.io in the registry-requirements list + the Package model doc.

Testing

go build, go vet, full ./internal/validators/... suite (incl. live cargo positive), make check-schema, validate-examples all pass. No new golangci-lint findings.

🤖 Generated with Claude Code

…tests

Follow-up to #1207, addressing items from review of the cargo validator.
Scoped to cargo + shared helpers only — no behavior change for existing
npm/pypi/nuget/oci/mcpb publishers.

SSRF hardening of the two-call README retrieval:
- Pin the step-2 README URL to an allowed host (static.crates.io in prod; the
  base host under test) before fetching, so a metadata response cannot steer the
  validator at an internal/attacker host.
- CheckRedirect policy pins every redirect hop to the same allowlist.
- Cap the README body with io.LimitReader (5 MiB).

Clearer status handling (previously any non-5xx/non-200 collapsed to "not found"):
- 429 is reported as transient/retryable rather than "not found", at both steps.
- A 403 from static.crates.io is disambiguated via the crate-version endpoint:
  genuinely-missing stays "not found"; exists-but-no-README gets an actionable
  "add a README with mcp-name and republish" message (mirrors NuGet). This
  continues the 5xx-vs-403 diagnostic split @P4ST4S started in the #1207 review.

Add a shared, boundary-anchored containsMCPNameToken helper (prevents prefix
confusion, e.g. a README declaring io.github.acme/widget-pro satisfying a claim
for io.github.acme/widget) and use it from the cargo validator. Adopting it in
the PyPI/NuGet validators is a follow-up (it is a behavior change for those
already-live registries).

Tests: hermetic positive ServerNameFormats; combined fixture gains a
version-existence endpoint + cases for 403-missing vs 403-no-readme, 429, and
prefix-confusion rejection; new foreign-README-host (SSRF) test; internal test
for the helper. Docs: list crates.io in registry requirements + the Package doc.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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.

1 participant