GitHub Security Advisory: GHSA-95c3-6vvw-4mrq
Package: github.com/modelcontextprotocol/registry (GO)
Severity: LOW (CVSS 0.0)
CWE: CWE-918: Server-Side Request Forgery (SSRF)
Vulnerable versions: < 1.7.6
Patched in: 1.7.6

Summary: MCP Registry's GitHub OIDC tokens are replayable across registry deployments due to shared audience

Details:
# [SECURITY] registry_001 Vulnerability Report

While analyzing the code logic, an area that may lead to unintended behavior under specific conditions was discovered. 

## Overview
- Verified Version: `c5c4b9e8890dd5754bee889b2f1417f4fe3b5ce5`
- Vulnerability Type: Authentication bypass via cross-registry OIDC token replay
- Affected Location: `cmd/publisher/commands/login.go:67-105,130-135,199-224`; `cmd/publisher/auth/github-oidc.go:24-38,58-75,108-165`; `internal/api/handlers/v0/auth/github_oidc.go:75-135,229-277,280-296`
- Trigger Scenario: a workflow invokes `mcp-publisher login github-oidc --registry <other-registry>` (or equivalent publish flow) and the publisher still requests a GitHub Actions ID token with the shared audience `mcp-registry`; any other registry deployment running this code can replay that token to its own `/v0/auth/github-oidc` endpoint and mint a publish-capable registry JWT for the same GitHub owner namespace.

## Root Cause
The client-side and server-side GitHub OIDC flow is bound only to a global audience string, not to the specific registry instance being targeted. On the client side, the publisher always appends `audience=mcp-registry` when requesting the GitHub Actions ID token, regardless of the selected `--registry` URL. On the server side, the exchange endpoint validates only that same fixed audience and then derives publish permissions directly from `repository_owner`. As a result, a token legitimately obtained while interacting with one registry deployment remains acceptable to any other deployment that shares the same code and audience string.

## Source-to-Sink Chain
1. Source
   `cmd/publisher/commands/login.go:67-105,130-135,199-224` parses the user-controlled `--registry` flag into `flags.RegistryURL`, creates a `GitHubOIDCProvider`, and calls `authProvider.GetToken(ctx)` for the chosen authentication method.
2. Propagation
   `cmd/publisher/auth/github-oidc.go:24-38` obtains an OIDC token and immediately exchanges it against the selected registry URL.
   `cmd/publisher/auth/github-oidc.go:58-75` builds `exchangeURL := o.registryURL + "/v0/auth/github-oidc"` and posts the GitHub token to whichever registry instance was selected.
   `cmd/publisher/auth/github-oidc.go:108-165` constructs `fullURL := requestURL + "&audience=mcp-registry"` and therefore requests the same audience for every registry deployment.
3. Sink
   `internal/api/handlers/v0/auth/github_oidc.go:75-135` validates only the shared audience value passed into `ValidateToken`.
   `internal/api/handlers/v0/auth/github_oidc.go:254-277` calls `h.validator.ValidateToken(ctx, oidcToken, "mcp-registry")` and, on success, signs a new registry JWT.
   `internal/api/handlers/v0/auth/github_oidc.go:280-296` converts `claims.RepositoryOwner` into the publish permission pattern `io.github.<owner>/*`, which is then embedded into the new registry JWT.

## Exploitation Preconditions
1. The victim uses the GitHub Actions OIDC publishing path.
2. The victim wo