Skip to main content

Gitea CVE-2026-56443

| EUVDEUVD-2026-58142 CRITICAL
Incorrect Authorization (CWE-863)
2026-07-21 https://github.com/go-gitea/gitea GHSA-7p4h-3gxq-x3h3
Critical
Disputed · 9.6 Vendor: https://github.com/go-gitea/gitea
Share

Severity by source

Sources disagree (Medium–Critical)
Vendor (https://github.com/go-gitea/gitea) PRIMARY
9.6 CRITICAL
AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:H
vuln.today AI
6.5 MEDIUM

Authenticated attacker mints a public-only PAT (PR:L) over the network with low complexity; impact is unauthorized read of Limited-visibility content (C:H), while writes are blocked (I:N) and there is no availability impact (A:N).

3.1 AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N
SUSE
MEDIUM
qualitative

vuln.today treats the vendor’s rating as authoritative. A higher third-party CVSS (e.g. CISA-ADP) is shown for transparency but does not drive the headline severity.

CVSS VectorVendor: https://github.com/go-gitea/gitea

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Changed
Confidentiality
None
Integrity
High
Availability
High

Lifecycle Timeline

6
Analysis Updated
Aug 14, 2026 - 20:34 vuln.today
v2 (cvss_changed)
Re-analysis Queued
Aug 14, 2026 - 20:22 vuln.today
cvss_changed
Severity Changed
Aug 14, 2026 - 20:22 NVD
MEDIUM CRITICAL
CVSS changed
Aug 14, 2026 - 20:22 NVD
4.3 (MEDIUM) 9.6 (CRITICAL)
Source Code Evidence Fetched
Jul 21, 2026 - 21:08 vuln.today
Analysis Generated
Jul 21, 2026 - 21:08 vuln.today

DescriptionCVE.org

Summary

After PR #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:LineFunctionAffected surface
routers/api/v1/api.go:292checkTokenPublicOnly Package caseAPI v1 packages
routers/api/packages/api.go:76reqPackageAccess middlewareAll 24 native package registries (/api/packages/<type>/...)
services/context/api.goTokenCanAccessRepo helperAll API v1 Repository-category endpoints - content, issues, PRs, releases, labels, milestones, etc.
services/context/permission.go:32CheckTokenScopes (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".

  1. Upload a generic package as limuser:

PUT /api/packages/limuser/generic/secretpkg/1.0.0/secret.txt with body secret-content-internal-only201.

  1. Create user attacker.
  2. Mint PAT for attacker with scopes=["read:package","read:user","read:repository","public-only"].

Result on gitea/gitea:1.26.2 - nine PROBEs:

PROBE A  (download package via attacker PAT)
    HTTP=200  Body: secret-content-internal-only

PROBE C  (read README of limuser's PUBLIC repo)
    HTTP=200  Body: {"name":"README.md", ...}

PROBE F  (sanity - User category, same PAT, same owner)
    HTTP=403  Body: {"message":"token scope is limited to public users"}

PROBE G  (Repository category, same PAT, same owner)
    HTTP=200  Body: {"name":"README.md", ...}                    ← bypass

PROBE H  (list limuser's repos, User category)
    HTTP=403  Body: {"message":"token scope is limited to public users"}

PROBE M  (git HTTPS smart protocol - info/refs)
    HTTP=200  Body: 001e
# service=git-upload-pack ... HEAD ...   ← full clone enabled

PROBE N  (write attempt: POST contents/hacked.txt)
    HTTP=403  Body: {"message":"user should have a permission to write to the target branch"}
                                                                    Integrity:N confirmed

PROBE O  (Limited ORG - same bypass class)
    Org category    : HTTP=403  {"message":"token scope is limited to public orgs"}
    Repo category   : HTTP=200  README content                  ← bypass

Anonymous baseline (no auth) on every above endpoint: HTTP=401/404

Gitea's own server error string in PROBE F / H / O - *"token scope is limited to public users"* / *"public orgs"* - is the explicit declaration of intent. Repository / Package category violates that intent on the same Limited owner.

Why this is not a duplicate of CVE-2026-25714

CVE-2026-25714 / PR #37118 (the May 2026 unification pass for public-only token filtering, merged 2026-05-18, backported to 1.26.2 via PR #37773) realigned checkTokenPublicOnly's Org / User / ActivityPub cases on !Visibility.IsPublic() and introduced the TokenCanAccessRepo helper for the Repository / Issue / Notification cases.

PROBE F on 1.26.2 returns 403 "token scope is limited to public users" for the User category - i.e. PR #37118's unification IS in effect on the version under test. The Repository / Package leak occurs *after* that fix; the Limited gap is the next residual issue on the same hygiene effort (the Package case was not touched, and TokenCanAccessRepo reduces to !repo.IsPrivate without consulting owner visibility), not the same bug.

Suggested fix (4 spots, 1-line shape each)

go
// routers/api/v1/api.go:292  (checkTokenPublicOnly Package case)
- if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
+ if ctx.Package != nil && !ctx.Package.Owner.Visibility.IsPublic() {

// routers/api/packages/api.go:76  (reqPackageAccess middleware)
- if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
+ if ctx.Package != nil && !ctx.Package.Owner.Visibility.IsPublic() {

// services/context/api.go  (TokenCanAccessRepo helper)
- return repo == nil || !ctx.PublicOnly || !repo.IsPrivate
+ return repo == nil || !ctx.PublicOnly ||
+     (!repo.IsPrivate && repo.Owner != nil && repo.Owner.Visibility.IsPublic())

// services/context/permission.go:32  (CheckTokenScopes)
- if publicOnly && repo != nil && repo.IsPrivate {
+ if publicOnly && repo != nil &&
+     (repo.IsPrivate || (repo.Owner != nil && !repo.Owner.Visibility.IsPublic())) {

This aligns the Repository / Package categories with the User / Org / ActivityPub siblings already shipped in PR #37118.

Reporter

JebeenLee

AnalysisAI

Authorization bypass in Gitea before 1.27.0 lets a public-only Personal Access Token read internal content owned by Limited-visibility users and organizations, exceeding the reach the scope is meant to grant. The Repository and Package scope categories only test repo.IsPrivate / Owner.Visibility.IsPrivate(), which return false for VisibleTypeLimited, so an authenticated attacker with a public-only PAT can fetch READMEs, repo content, packages from all 24 registries, and even perform full git-upload-pack clones of instance-internal projects. A vendor-verified end-to-end Docker PoC exists (publicly available exploit code exists); it is not in CISA KEV and EPSS is low at 0.17% (6th percentile). This is the residual gap left after CVE-2026-25714 / PR #37118 fixed the sibling User/Org/ActivityPub cases.

Technical ContextAI

Gitea supports three owner visibility levels - Public, Limited (visible only to signed-in instance users), and Private. Personal Access Tokens can carry a 'public-only' modifier that is supposed to restrict a token to what an anonymous user could see. The central gatekeeper checkTokenPublicOnly (routers/api/v1/api.go) and its helpers TokenCanAccessRepo (services/context/api.go), CheckTokenScopes (services/context/permission.go:32), and the reqPackageAccess middleware (routers/api/packages/api.go:76) enforce this. The root cause is CWE-863 (Incorrect Authorization): the Org/User/ActivityPub branches correctly reject non-public owners with !Visibility.IsPublic(), but the Repository and Package branches use IsPrivate(), where VisibleType.IsPrivate() returns true only for VisibleTypePrivate and falls through for VisibleTypeLimited. Because 'not private' is not the same as 'public', a public-only token gains strictly more reach than anonymous access on Limited owners. Affected package identifier is pkg:go/code.gitea.io/gitea.

RemediationAI

Vendor-released patch: 1.27.0 - upgrade Gitea to 1.27.0 or later (release https://github.com/go-gitea/gitea/releases/tag/v1.27.0, notes https://blog.gitea.com/gitea-1.27.0-is-released/), which realigns the Repository and Package scope checks to use !Visibility.IsPublic() consistent with the User/Org/ActivityPub cases. If immediate upgrade is not possible, the most effective compensating control is to avoid relying on Limited visibility as a security boundary: convert genuinely sensitive owners/repositories/packages to Private (which IsPrivate() correctly blocks), accepting that they will no longer be visible to legitimate signed-in users. As a broader lock-down you can disable creation of public-only tokens or audit and revoke existing PATs, and restrict who can register accounts on the instance (since exploitation needs an authenticated account), at the cost of reduced automation/self-service. There is no reliable per-endpoint firewall workaround because the leak spans API v1 repository endpoints, all 24 package registries, web /raw, /media, /attachments, and LFS/git-upload-pack routes.

More in Docker

View all
CVE-2024-55964 CRITICAL POC
9.8 Mar 26

An issue was discovered in Appsmith before 1.52. Rated critical severity (CVSS 9.8), this vulnerability is remotely expl

CVE-2019-5736 HIGH POC
8.6 Feb 11

runc through version 1.0-rc6 (used in Docker before 18.09.2) contains a container escape vulnerability that allows attac

CVE-2023-32077 HIGH POC
7.5 Aug 24

Netmaker makes networks with WireGuard. Rated high severity (CVSS 7.5), this vulnerability is remotely exploitable, no a

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2023-5815 HIGH POC
8.1 Nov 22

The News & Blog Designer Pack - WordPress Blog Plugin - (Blog Post Grid, Blog Post Slider, Blog Post Carousel, Blog Post

CVE-2026-66384 MEDIUM POC
5.3 Aug 12

Path traversal in JFrog Artifactory (CWE-22) enables an authenticated low-privilege user to write data outside the inten

CVE-2014-9357 CRITICAL
10.0 Dec 16

Docker 1.3.2 allows remote attackers to execute arbitrary code with root privileges via a crafted (1) image or (2) build

CVE-2026-52806 CRITICAL POC
9.9 Jun 23

Remote code execution in Gogs through 0.14.2 allows authenticated users (and unauthenticated attackers on default-config

CVE-2026-56274 HIGH POC
8.7 Jun 23

Remote code execution in Flowise before 3.1.2 allows any authenticated user (or API caller with chatflow view/update per

CVE-2026-34156 CRITICAL POC
9.9 Mar 30

Remote code execution in NocoBase Workflow Script Node (npm @nocobase/plugin-workflow-javascript) allows authenticated l

CVE-2019-15752 HIGH POC
7.8 Aug 28

Docker Desktop Community Edition before 2.1.0.1 allows local users to gain privileges by placing a Trojan horse docker-c

CVE-2025-34221 CRITICAL POC
10.0 Sep 29

Vasion Print (formerly PrinterLogic) Virtual Appliance Host prior to version 25.2.169 and Application prior to version 2

Vendor StatusVendor

SUSE

Severity: Moderate
Product Status
SUSE Linux Enterprise Server 16.1 Affected
SUSE Linux Enterprise Server for SAP applications 16.1 Affected
SUSE Linux Enterprise Module for Package Hub 15 SP5 Affected
SUSE Linux Enterprise Module for Package Hub 15 SP6 Affected
openSUSE Leap 15.5 Affected

Share

CVE-2026-56443 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy