From 35b20719ee7714ad18d0f81af6798ac0a46e2b9b Mon Sep 17 00:00:00 2001 From: Radoslav Dimitrov Date: Tue, 12 May 2026 16:59:59 -0400 Subject: [PATCH] fix(validators/oci): fail closed on upstream rate limiting (#1281) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary When the OCI validator's anonymous manifest fetch returns HTTP 429, return a retryable error instead of `nil`. This aligns OCI with the fail-closed behavior of the npm / PyPI / NuGet / MCPB validators, and ensures the `io.modelcontextprotocol.server.name` label-match always runs before a publish is accepted. ## Test plan - [x] `go test ./internal/validators/registries/ -run TestValidateOCI` passes locally - [x] `gofmt` + `golangci-lint` clean on the changed file - [ ] CI green 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) --- internal/validators/registries/oci.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/internal/validators/registries/oci.go b/internal/validators/registries/oci.go index d4ab56c..f303267 100644 --- a/internal/validators/registries/oci.go +++ b/internal/validators/registries/oci.go @@ -4,7 +4,6 @@ import ( "context" "errors" "fmt" - "log" "net/http" "strings" "time" @@ -106,10 +105,13 @@ func ValidateOCI(ctx context.Context, pkg model.Package, serverName string) erro if errors.As(err, &transportErr) { switch transportErr.StatusCode { case http.StatusTooManyRequests: - // Rate limited - skip validation to avoid blocking publishers - // This is intentional: we prioritize UX over strict validation during high traffic - log.Printf("Skipping OCI validation for %s due to rate limiting", pkg.Identifier) - return nil + // Fail closed: a 429 means we could not verify the + // `io.modelcontextprotocol.server.name` label on the image, which is + // the only ownership proof we apply to OCI packages. Returning nil + // here would let a publisher bind their server record to an arbitrary + // public image they do not control, which is the bug this branch + // used to have. Surface the rate-limit as a retryable error instead. + return fmt.Errorf("OCI registry is currently rate-limiting validations for '%s'; please retry shortly", pkg.Identifier) case http.StatusNotFound: return fmt.Errorf("OCI image '%s' does not exist in the registry", pkg.Identifier) case http.StatusUnauthorized, http.StatusForbidden: