A AegiFlow
MEDIUMCVSS 4.3

CVE-2026-56443

Gitea: Token public-only scope bypassed on Limited-visibility owners (Repository + Package categories) — residual after CVE-2026-25714 / PR #37118

Published
2026-07-21
Modified
2026-07-21
Aliases
GHSA-7p4h-3gxq-x3h3
Sources
github-advisory

Summary

## Summary After [PR #37118](https://github.com/go-gitea/gitea/pull/37118) / **CVE-2026-25714** (`fix: Unify public-only token filtering in API queries and repo access checks`, merged 2026-05-18, backport `#37773` to 1.26.2 — the May 2026 unification pass for public-only token filtering, reporter Medoedus per the 1.26.2 release notes), the `public-only` PAT scope is still bypassable on **Repository** and **Package** scope categories when the owner's `Visibility = Limited` (instance-internal). The sibling `Org` / `User` / `ActivityPub` cases in the same `checkTokenPublicOnly` switch correctly reject Limited owners via `!Visibility.IsPublic()`. The Repository / Package cases use `repo.IsPrivate` or `Owner.Visibility.IsPrivate()`, both of which return `false` for `VisibleTypeLimited` — so a `public-only` PAT strictly exceeds anonymous reach on a Limited owner. Tested on `gitea/gitea:1.26.2`. The decisive marker is that PR #37118's unification IS applied in the version under test (User-category PROBE returns `403 "token scope is limited to public users"`). Despite that, the Repository-category PROBE on the same Limited owner with the same PAT returns `200` and serves content. ## Affected entry points (4 spots) | File:Line | Function | Affected surface | |---|---|---| | `routers/api/v1/api.go:292` | `checkTokenPublicOnly` Package case | API v1 packages | | `routers/api/packages/api.go:76` | `reqPackageAccess` middleware | All 24 native package registries (`/api/packages/ /...`) | | `services/context/api.go` | `TokenCanAccessRepo` helper | All API v1 Repository-category endpoints — content, issues, PRs, releases, labels, milestones, etc. | | `services/context/permission.go:32` | `CheckTokenScopes` (called via `CheckRepoScopedToken`) | Web download endpoints `/raw`, `/media`, `/attachments`. LFS routes (`services/lfs/server.go:470/472`, `services/lfs/locks.go:62/151/216/284`) also chain through this helper. | All four sinks check `repo.IsPrivate` or `Owner.Visibility.IsPrivate()` only. `VisibleTypeLimited` falls through. ```go // modules/structs/visible_type.go func (vt VisibleType) IsPrivate() bool { return vt == VisibleTypePrivate } // line 39-40 func (vt VisibleType) IsLimited() bool { return vt == VisibleTypeLimited } // line 33-34 ``` ## Same-file evidence (`routers/api/v1/api.go:246-299` after PR #37118) ```go case auth_model.AccessTokenScopeCategoryOrganization: orgPrivate := ... && !ctx.Org.Organization.Visibility.IsPublic() // !IsPublic ✓ case auth_model.AccessTokenScopeCategoryUser: if ... && !ctx.ContextUser.Visibility.IsPublic() { ... } // !IsPublic ✓ case auth_model.AccessTokenScopeCategoryActivityPub: if ... && !ctx.ContextUser.Visibility.IsPublic() { ... } // !IsPublic ✓ case auth_model.AccessTokenScopeCategoryPackage: if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() { // IsPrivate ONLY ✗ ctx.APIError(http.StatusForbidden, "token scope is limited to public packages") return } ``` `TokenCanAccessRepo` (`services/context/api.go`) reduces to `!repo.IsPrivate`: ```go // A public-only token cannot reach a private repo; any other token is unrestricted by this check. func (ctx *APIContext) TokenCanAccessRepo(repo *repo_model.Repository) bool { return repo == nil || !ctx.PublicOnly || !repo.IsPrivate } ``` `CheckTokenScopes` (`services/context/permission.go:32`): ```go if publicOnly && repo != nil && repo.IsPrivate { ctx.HTTPError(http.StatusForbidden) return } ``` ## PoC (Docker e2e VERIFIED on `gitea/gitea:1.26.2`, 2026-06-05) Full script in the report (`run-poc.sh`). Setup: 1. Create user `limuser`. `PATCH /api/v1/admin/users/limuser` with body `{"visibility":"limited", ...}` — response confirms `"visibility":"limited"`. 2. Upload a generic package as `limuser`: `PUT /api/packages/limuser/generic/secretpkg/1.0.0/secret.txt` with body `secret-content-internal-only` → `201`. 3. Create us

Affected packages

EcosystemPackageAffected versionsFixed versions
Gocode.gitea.io/gitea1.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.