CVE-2026-56443: Gitea Token Public-only Scope Bypassed On Vulnerability
MODERATECVE-2026-56443 is a MODERATE (CVSS 4.3) token public-only scope bypassed on vulnerability (CWE-863: Incorrect Authorization) affecting Gitea libraries in the Go ecosystem. The issue involves token public-only scope bypassed on limited-visibility owners (repository + package categories) — residual after cve-2026-25714 / pr #37118. Remediation is available by upgrading affected packages to version 1.27.0.
CVE-2026-56443 at a Glance
Security Impact
CVSS: 4.3
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N).
How to Fix CVE-2026-56443
To remediate this vulnerability, upgrade all affected Gitea dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
Upgrade code.gitea.io/gitea to 1.27.0
Upgrade gitea.dev to 1.27.0
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
inspect dependencies for code.gitea.io/gitea
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-56443, inspect your project dependencies for Gitea:
inspect dependencies for code.gitea.io/gitea
Analyze the terminal output to locate any version of Gitea in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.27.0), your application is affected.
Note: Verify declared build manifests and deployed runtime container environments.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-863: Incorrect Authorization, where the product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. In Gitea, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-56443 Affected Gitea Versions
2 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
code.gitea.io/gitea
|
Go |
< 1.27.0
|
1.27.0
|
|
gitea.dev
|
Go |
< 1.27.0
|
1.27.0
|
References
Frequently Asked Questions
What is CVE-2026-56443?
Which Gitea versions are affected?
What version fixes CVE-2026-56443?
Is code.gitea.io/gitea affected?
Is gitea.dev affected?
What is the CVSS score of CVE-2026-56443?
What CWE is associated with CVE-2026-56443?
How can I check Go dependencies for CVE-2026-56443?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-7p4h-3gxq-x3h3",
"modified" : "2026-09-24T15:53:00Z",
"published" : "2026-07-21T20:17:19Z",
"aliases" : [ "CVE-2026-56443" ],
"summary" : "Gitea: Token public-only scope bypassed on Limited-visibility owners (Repository + Package categories) — residual after CVE-2026-25714 / PR #37118",
"details" : "## Summary\n\nAfter [PR #37118](https://github.com/go-gitea/gitea/pull/37118) / **CVE-2026-25714**\n(`fix: Unify public-only token filtering in API queries and repo access checks`,\nmerged 2026-05-18, backport `#37773` to 1.26.2 — the May 2026 unification pass\nfor public-only token filtering, reporter Medoedus per the 1.26.2 release notes),\nthe `public-only` PAT scope is still bypassable on **Repository** and **Package**\nscope categories when the owner's `Visibility = Limited` (instance-internal).\n\nThe sibling `Org` / `User` / `ActivityPub` cases in the same `checkTokenPublicOnly`\nswitch correctly reject Limited owners via `!Visibility.IsPublic()`. The\nRepository / Package cases use `repo.IsPrivate` or `Owner.Visibility.IsPrivate()`,\nboth of which return `false` for `VisibleTypeLimited` — so a `public-only` PAT\nstrictly exceeds anonymous reach on a Limited owner.\n\nTested on `gitea/gitea:1.26.2`. The decisive marker is that PR #37118's\nunification IS applied in the version under test (User-category PROBE returns\n`403 \"token scope is limited to public users\"`). Despite that, the\nRepository-category PROBE on the same Limited owner with the same PAT returns\n`200` and serves content.\n\n## Affected entry points (4 spots)\n\n| File:Line | Function | Affected surface |\n|---|---|---|\n| `routers/api/v1/api.go:292` | `checkTokenPublicOnly` Package case | API v1 packages |\n| `routers/api/packages/api.go:76` | `reqPackageAccess` middleware | All 24 native package registries (`/api/packages/<type>/...`) |\n| `services/context/api.go` | `TokenCanAccessRepo` helper | All API v1 Repository-category endpoints — content, issues, PRs, releases, labels, milestones, etc. |\n| `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. |\n\nAll four sinks check `repo.IsPrivate` or `Owner.Visibility.IsPrivate()` only.\n`VisibleTypeLimited` falls through.\n\n```go\n// modules/structs/visible_type.go\nfunc (vt VisibleType) IsPrivate() bool { return vt == VisibleTypePrivate } // line 39-40\nfunc (vt VisibleType) IsLimited() bool { return vt == VisibleTypeLimited } // line 33-34\n```\n\n## Same-file evidence (`routers/api/v1/api.go:246-299` after PR #37118)\n\n```go\ncase auth_model.AccessTokenScopeCategoryOrganization:\n orgPrivate := ... && !ctx.Org.Organization.Visibility.IsPublic() // !IsPublic ✓\ncase auth_model.AccessTokenScopeCategoryUser:\n if ... && !ctx.ContextUser.Visibility.IsPublic() { ... } // !IsPublic ✓\ncase auth_model.AccessTokenScopeCategoryActivityPub:\n if ... && !ctx.ContextUser.Visibility.IsPublic() { ... } // !IsPublic ✓\n\ncase auth_model.AccessTokenScopeCategoryPackage:\n if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() { // IsPrivate ONLY ✗\n ctx.APIError(http.StatusForbidden, \"token scope is limited to public packages\")\n return\n }\n```\n\n`TokenCanAccessRepo` (`services/context/api.go`) reduces to `!repo.IsPrivate`:\n\n```go\n// A public-only token cannot reach a private repo; any other token is unrestricted by this check.\nfunc (ctx *APIContext) TokenCanAccessRepo(repo *repo_model.Repository) bool {\n return repo == nil || !ctx.PublicOnly || !repo.IsPrivate\n}\n```\n\n`CheckTokenScopes` (`services/context/permission.go:32`):\n\n```go\nif publicOnly && repo != nil && repo.IsPrivate {\n ctx.HTTPError(http.StatusForbidden)\n return\n}\n```\n\n## PoC (Docker e2e VERIFIED on `gitea/gitea:1.26.2`, 2026-06-05)\n\nFull script in the report (`run-poc.sh`). Setup:\n\n1. Create user `limuser`. `PATCH /api/v1/admin/users/limuser` with body\n `{\"visibility\":\"limited\", ...}` — response confirms `\"visibility\":\"limited\"`.\n2. Upload a generic package as `limuser`:\n `PUT /api/packages/limuser/generic/secretpkg/1.0.0/secret.txt` with body\n `secret-content-internal-only` → `201`.\n3. Create user `attacker`.\n4. Mint PAT for `attacker` with `scopes=[\"read:package\",\"read:user\",\"read:repository\",\"public-only\"]`.\n\nResult on `gitea/gitea:1.26.2` — nine PROBEs:\n\n```\nPROBE A (download package via attacker PAT)\n HTTP=200 Body: secret-content-internal-only\n\nPROBE C (read README of limuser's PUBLIC repo)\n HTTP=200 Body: {\"name\":\"README.md\", ...}\n\nPROBE F (sanity — User category, same PAT, same owner)\n HTTP=403 Body: {\"message\":\"token scope is limited to public users\"}\n\nPROBE G (Repository category, same PAT, same owner)\n HTTP=200 Body: {\"name\":\"README.md\", ...} ← bypass\n\nPROBE H (list limuser's repos, User category)\n HTTP=403 Body: {\"message\":\"token scope is limited to public users\"}\n\nPROBE M (git HTTPS smart protocol — info/refs)\n HTTP=200 Body: 001e# service=git-upload-pack ... HEAD ... ← full clone enabled\n\nPROBE N (write attempt: POST contents/hacked.txt)\n HTTP=403 Body: {\"message\":\"user should have a permission to write to the target branch\"}\n Integrity:N confirmed\n\nPROBE O (Limited ORG — same bypass class)\n Org category : HTTP=403 {\"message\":\"token scope is limited to public orgs\"}\n Repo category : HTTP=200 README content ← bypass\n\nAnonymous baseline (no auth) on every above endpoint: HTTP=401/404\n```\n\nGitea's own server error string in PROBE F / H / O — *\"token scope is limited\nto public users\"* / *\"public orgs\"* — is the explicit declaration of intent.\nRepository / Package category violates that intent on the same Limited owner.\n\n## Why this is not a duplicate of CVE-2026-25714\n\nCVE-2026-25714 / PR #37118 (the May 2026 unification pass for public-only token\nfiltering, merged 2026-05-18, backported to 1.26.2 via PR #37773) realigned\n`checkTokenPublicOnly`'s Org / User / ActivityPub cases on `!Visibility.IsPublic()`\nand introduced the `TokenCanAccessRepo` helper for the Repository / Issue /\nNotification cases.\n\nPROBE F on `1.26.2` returns `403 \"token scope is limited to public users\"` for\nthe User category — i.e. PR #37118's unification IS in effect on the version\nunder test. The Repository / Package leak occurs *after* that fix; the Limited\ngap is the next residual issue on the same hygiene effort (the Package case was\nnot touched, and `TokenCanAccessRepo` reduces to `!repo.IsPrivate` without\nconsulting owner visibility), not the same bug.\n\n## Suggested fix (4 spots, 1-line shape each)\n\n```go\n// routers/api/v1/api.go:292 (checkTokenPublicOnly Package case)\n- if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {\n+ if ctx.Package != nil && !ctx.Package.Owner.Visibility.IsPublic() {\n\n// routers/api/packages/api.go:76 (reqPackageAccess middleware)\n- if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {\n+ if ctx.Package != nil && !ctx.Package.Owner.Visibility.IsPublic() {\n\n// services/context/api.go (TokenCanAccessRepo helper)\n- return repo == nil || !ctx.PublicOnly || !repo.IsPrivate\n+ return repo == nil || !ctx.PublicOnly ||\n+ (!repo.IsPrivate && repo.Owner != nil && repo.Owner.Visibility.IsPublic())\n\n// services/context/permission.go:32 (CheckTokenScopes)\n- if publicOnly && repo != nil && repo.IsPrivate {\n+ if publicOnly && repo != nil &&\n+ (repo.IsPrivate || (repo.Owner != nil && !repo.Owner.Visibility.IsPublic())) {\n```\n\nThis aligns the Repository / Package categories with the User / Org / ActivityPub\nsiblings already shipped in PR #37118.\n## Reporter\n\nJebeenLee",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "Go",
"name" : "code.gitea.io/gitea"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "1.27.0"
} ]
} ]
}, {
"package" : {
"ecosystem" : "Go",
"name" : "gitea.dev"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "1.27.0"
} ]
} ]
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/go-gitea/gitea/security/advisories/GHSA-7p4h-3gxq-x3h3"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-56443"
}, {
"type" : "WEB",
"url" : "https://github.com/go-gitea/gitea/commit/de4b8277e9cb576f2315fb03b5ab6478b42a1d31"
}, {
"type" : "WEB",
"url" : "https://blog.gitea.com/gitea-1.27.0-is-released"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/go-gitea/gitea"
}, {
"type" : "WEB",
"url" : "https://github.com/go-gitea/gitea/releases/tag/v1.27.0"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-863" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-07-21T20:17:19Z",
"nvd_published_at" : "2026-08-13T17:17:25Z"
}
}