CVE-2026-58507
Gitea: Private Repository Existence Disclosure via go-get Meta Endpoint
Summary
| Field | Value | |-------|-------| | **Affected File** | `routers/web/repo/githttp.go`, `services/context/repo.go` | | **Affected Functions** | `httpBase()`, `EarlyResponseForGoGetMeta()` | | **Affected Lines** | `githttp.go:63–66`, `services/context/repo.go:374–396` | | **Prerequisite** | None — fully unauthenticated | --- #### Description Gitea implements a special behavior for requests containing the `?go-get=1` query parameter. This parameter is sent by the Go toolchain (`go get`, `go install`) to discover VCS metadata for module imports. When Gitea detects this parameter in the HTTP request path for a repository, it bypasses the normal authentication and authorization stack and returns an HTTP 200 response containing ` ` and ` ` tags — regardless of whether: - The repository is private - The requesting user is authenticated - The requesting user has any permission on the repository The entry point is `routers/web/repo/githttp.go:63–66`: ```go func httpBase(ctx *context.Context, optGitService ...string) *serviceHandler { reponame := strings.TrimSuffix(ctx.PathParam("reponame"), ".git") if ctx.FormString("go-get") == "1" { context.EarlyResponseForGoGetMeta(ctx) return nil // ← returns before any auth or permission check } ... ``` The `EarlyResponseForGoGetMeta` function (`services/context/repo.go:379–396`) is called unconditionally, and the function's own docstring documents the intended behavior: ```go // EarlyResponseForGoGetMeta responses appropriate go-get meta with status 200 // if user does not have actual access to the requested repository, // or the owner or repository does not exist at all. // This is particular a workaround for "go get" command which does not respect // .netrc file. func EarlyResponseForGoGetMeta(ctx *Context) { username := ctx.PathParam("username") reponame := strings.TrimSuffix(ctx.PathParam("reponame"), ".git") ... ctx.PlainText(http.StatusOK, htmlMeta) // ← HTTP 200, no auth check } ``` The function also appears at `services/context/repo.go:444, 516, 571` — all repository-scoped route handlers that check `?go-get=1` and call `EarlyResponseForGoGetMeta` before performing any permission verification. The metadata returned includes: 1. The **full repository name** and owner — confirming the repository exists 2. The **HTTP clone URL** — a fully-formed URL pointing to the repository 3. The **source browsing URL templates** — which may reveal the default branch name This allows an unauthenticated attacker to: 1. **Confirm existence** of any private repository by name 2. **Enumerate** private repository names through brute-force without triggering authentication failures 3. **Harvest** clone URLs and default branch names of private repositories --- #### Proof of Concept **Step 1 — Identify a private repository** Any private repository works. For this demonstration, `admin/classified-internal` is set to private: --- **Step 2 — Confirm access is denied without authentication** Standard requests to a private repository correctly return 404 for unauthenticated users. --- **Step 3 — Bypass using go-get parameter** ```bash curl -s "http://localhost:3000/admin/classified-internal?go-get=1" ``` **Actual response (HTTP 200):** ```html go get --insecure localhost:3000/admin/classified-internal
Affected packages
| Ecosystem | Package | Affected versions | Fixed versions |
|---|---|---|---|
| Go | code.gitea.io/gitea | — | 1.27.0 |
Remediation: Upgrade to 1.27.0 or later.
References
Includes data from the GitHub Advisory Database, licensed under CC-BY 4.0.
CVE® is a registered trademark of The MITRE Corporation. CVE content reproduced under the CVE Terms of Use; copyright designation © MITRE.