CVE-2026-58432
Gitea: draft release attachment disclosure via missing web authorization
Summary
### Summary Gitea's draft-release access control is enforced only on the API release endpoints (`/api/v1/repos/{owner}/{repo}/releases/{id}` and its `/assets/...` sub-routes) but not on the web-level UUID-based attachment endpoints (`/attachments/{uuid}`, `/{owner}/{repo}/attachments/{uuid}`, `/{owner}/{repo}/releases/attachments/{uuid}`). Anyone (including unauthenticated callers) who has, learns, or otherwise obtains the UUID of an attachment belonging to a draft release can download its full contents, despite the draft release itself being correctly hidden from listings and direct-by-ID API lookups. The `browser_download_url` field returned by the API (visible to anyone with write access to the repo) embeds the UUID. Forwarding this URL by email, log scrape, browser history, screenshot, or any side channel grants any recipient unauthenticated access to the attachment, indefinitely. This is the identical insider-leak threat model that Gitea fixed on the API surface in PR #36659 (CVE-2026-27660, Feb 2026) by adding `canAccessReleaseDraft` checks. The web mirror was missed. ### Details **Root cause:** the web-side handler `ServeAttachment` (`routers/web/repo/attachment.go:122-203`) checks only repo-level unit-read permission, never the `IsDraft` flag of the linked release: ```go // routers/web/repo/attachment.go:122-203, current implementation func ServeAttachment(ctx *context.Context, uuid string) { attach, err := repo_model.GetAttachmentByUUID(ctx, uuid) if err != nil { ... } // cross-repo guard (only fires when accessed via repo-scoped URL) if attach.CreatedUnix > repo_model.LegacyAttachmentMissingRepoIDCutoff && ctx.Repo.Repository != nil && ctx.Repo.Repository.ID != attach.RepoID { ctx.HTTPError(http.StatusNotFound) return } unitType, repoID, err := repo_service.GetAttachmentLinkedTypeAndRepoID(ctx, attach) if unitType == unit.TypeInvalid { if !(ctx.IsSigned && attach.UploaderID == ctx.Doer.ID) { ctx.HTTPError(http.StatusNotFound) return } } else { var perm access_model.Permission // ... resolves repo perm if !perm.CanRead(unitType) { // <-- ONLY check ctx.HTTPError(http.StatusNotFound) return } // NO release.IsDraft check // NO canAccessReleaseDraft equivalent } // ... serves the file } ``` The helper `GetAttachmentLinkedTypeAndRepoID` (`services/repository/repository.go:185-207`) returns `(unit.TypeReleases, rel.RepoID)` for release-linked attachments but discards the release object (including its `IsDraft` flag) before returning. **Mounted routes affected** (all reach `ServeAttachment` via `GetAttachment`): | File:line | Route | Auth gate | |---|---|---| | `routers/web/web.go:874` | `GET /attachments/{uuid}` (top-level) | `optionsCorsHandler() + webAuth.AllowBasic + webAuth.AllowOAuth2`, accepts anonymous | | `routers/web/web.go:1284` | `GET /{owner}/{repo}/attachments/{uuid}` (issue-context) | repo context, anonymous OK | | `routers/web/web.go:1473` | `GET /{owner}/{repo}/releases/attachments/{uuid}` (release-context) | `webAuth.AllowBasic + webAuth.AllowOAuth2`, anonymous OK | | `routers/web/web.go:1491` | `GET /{owner}/{repo}/attachments/{uuid}` (legacy compatibility) | `webAuth.AllowBasic + webAuth.AllowOAuth2`, anonymous OK | **Reference: existing fix on the API surface (PR #36659, commit `1eced4a7c0`, Feb 22 2026):** ```go // routers/api/v1/repo/release.go:24-37, added by PR #36659 func canAccessReleaseDraft(ctx *context.APIContext) bool { if !ctx.IsSigned || !ctx.Repo.Permission.CanWrite(unit.TypeReleases) { return false } // ... API-token scope check } ``` `canAccessReleaseDraft` is called from `GetRelease` (line 80), `ListReleases` (line 178), `GetReleaseAttachment` (`release_attachment.go:37`), and `ListReleaseAttachments` (line 148). Every API code path now gates draft visibility on **write access*
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.