CVE-2026-24791 is a high-severity incorrect authorization vulnerability in code.gitea.io/gitea (go), affecting versions >= 1.22.3, <= 1.26.1. It is fixed in 1.26.2.
Summary Many authenticated self routes under /api/v1/user/... do not enforce the public-only token restriction. As a result, a token or OAuth grant marked public-only, but otherwise carrying the route-required read/write scope category, can access or modify private account resources through self routes. The canonical private-user endpoint correctly rejects the same tokens, for example GET /api/v1/users/{privateUser} returns 403. The bypass exists because the generic /api/v1/user route group requires user scope and reqToken(), but does not enforce the token's public-only restriction for most self routes. This is a systemic token/OAuth scope-boundary bypass, not a single endpoint bug. This appears related to the previously fixed public-only token issue tracked as CVE-2025-68941 / GHSA-xfq3-qj7j-4565, which affected Gitea < 1.22.3. The behavior described here reproduces on tested main checkout 6a2706626904. A representative SSH-key self-route PoC also reproduces on tested releases through v1.26.1. In other words, this should be treated as an incomplete fix / residual gap in a different route family, not as a duplicate of the older advisory. Affected Code The generic /api/v1/user group is mounted with user scope and reqToken(): routers/api/v1/api.go:1008-1128 tokenRequiresScopes() sets ctx.PublicOnly when the token contains public-only, but the public-only restriction is enforced only by routes that also call checkTokenPublicOnly(): routers/api/v1/api.go:241-294 implements checkTokenPublicOnly(). routers/api/v1/api.go:299-341 sets ctx.PublicOnly from the token scope. Representative affected routes in that group: /api/v1/user: private self profile and settings. /api/v1/user/emails: read, add, and delete account email addresses. /api/v1/user/keys: list and add SSH public keys. /api/v1/user/applications/oauth2: list and create OAuth2 applications, including returned client secrets. /api/v1/user/actions/secrets/{secretname}: create or delete user-level Actions secrets. /api/v1/user/actions/variables: list, read, create, update, and delete user-level Actions variables. /api/v1/user/actions/runners/...: list, update, delete runners, and mint registration tokens. /api/v1/user/actions/runs and /api/v1/user/actions/jobs: list workflow metadata for private repositories. /api/v1/user/repos: create private repositories and list private repositories. /api/v1/user/subscriptions, /api/v1/user/times, /api/v1/user/stopwatches, /api/v1/user/teams, /api/v1/user/hooks: leak or modify private-account resources. Correct public-only enforcement for comparison: routers/api/v1/api.go:970-1008 applies context.UserAssignmentAPI() and checkTokenPublicOnly() to canonical /api/v1/users/{username} routes. routers/api/v1/user/user.go:122-125 rejects public-only access to private users on /api/v1/users/{username}. routers/api/v1/api.go:1091-1092 shows that /api/v1/user/repos requires the additional repository scope category, but still does not apply checkTokenPublicOnly(). Local PoCs The following dynamic PoCs were retested on checkout 6a2706626904 and all reproduced successfully. Each PoC writes a temporary integration test, runs it, and removes it afterward. Reproduced Impact Examples Using private fixture user user31, public-only tokens are rejected by GET /api/v1/users/user31, but tokens with the route-required scopes can still reach the self routes below. Confirmed with public-only,write:user: add SSH keys through /api/v1/user/keys; add account emails through /api/v1/user/emails; create OAuth2 applications and receive clientsecret through /api/v1/user/applications/oauth2; create/delete user-level Actions secrets; create/read/list/update/delete user-level Actions variables; mint user-level runner registration tokens; manage user-level runners; create user webhooks. Confirmed with public-only,read:user: read private self profile/settings and account email surfaces; list OAuth2 applications and user webhooks; list private repository workflow runs/jobs exposed through self Actions routes; list private subscriptions, tracked times, stopwatches, and team memberships. Confirmed with public-only plus the route-required repository category: create private repositories through POST /api/v1/user/repos with public-only,write:user,write:repository; list those private repositories through GET /api/v1/user/repos with public-only,read:user,read:repository, while the canonical private repository endpoint remains forbidden. Impact The public-only token flag is intended to limit a token or OAuth grant to public resources. These routes violate that boundary for private accounts. Practical abuse scenarios include: a third-party app or leaked token with the route-required write scope, but restricted to public resources, adding SSH credentials or OAuth applications to a private account; a public-resource-restricted token with the route-required write scope modifying Actions secrets/variables or registering/managing runners; a token limited to public resources creating and enumerating private repositories; a supposedly public-only integration learning private repository, workflow, team, timing, subscription, webhook, and email metadata. Suggested Fix Apply public-only enforcement consistently to self routes under /api/v1/user. At minimum: for self routes, treat ctx.Doer as the target user/resource owner when enforcing public-only; mechanically adding checkTokenPublicOnly() is not sufficient unless ctx.ContextUser is set to ctx.Doer or the check explicitly handles self routes; reject ctx.PublicOnly on credential, identity, OAuth application, repository creation, webhook, Actions, runner, and email-management self-route mutations; filter list routes so public-only tokens cannot return private repositories, private organization/team metadata, private workflow runs/jobs, private tracked time, private stopwatches, or hidden subscriptions; add regression coverage that compares each affected /api/v1/user/... route against the canonical private-user or private-repository endpoint. Non-public-only tokens should preserve current behavior. Attachment: apipubliconlyusersshkeybypassdynamicpoc.go Attachment: apipubliconlyuseroauthappbypassdynamicpoc.go Attachment: apipubliconlyuserreposprivaterepobypassdynamicpoc.go Attachment: apipubliconlyuseractionssecretvariablebypassdynamicpoc.go Attachment: apipubliconlyuserrunnerregistrationbypassdynamic_poc.go Version validation Validation date: 2026-05-13 The SSH-key write PoC was used as the representative dynamic test for the systemic /api/v1/user self-route public-only bypass. | Version | Commit | Result | |---|---:|---| | main | 6a2706626904 | reproduced dynamically | | v1.26.1 | afdbd9b7c5 | reproduced dynamically | | v1.25.5 | f913d90ab6 | reproduced dynamically | | v1.24.7 | 99053ce4fa | reproduced dynamically | | v1.23.8 | cccd54999a | reproduced dynamically | | v1.22.6 | 8eefa1f6de | reproduced dynamically with Go 1.22.12 test toolchain | The representative version-matrix PoC validates the same root cause across tested releases for the SSH-key self-route write surface. The additional lead/supporting PoCs above were retested on the main checkout listed in the Local PoCs section.
The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions. Typical impact: unauthorized data access or execution of privileged operations.
CVE-2026-24791 has a CVSS score of 8.1 (High). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment.
A fixed version is available (1.26.2). Upgrading removes the vulnerable code path.
go
code.gitea.io/gitea (>= 1.22.3, <= 1.26.1)code.gitea.io/gitea → 1.26.2 (go)Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter instead of chasing every advisory.
Kodem's runtime-powered SCA identifies whether CVE-2026-24791 is reachable in your applications. Explore open-source security for your team.
See if CVE-2026-24791 is reachable in your applications. Get a demo
Upgrade code.gitea.io/gitea to 1.26.2 or later to resolve this vulnerability.
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
CVE-2026-24791 is a high-severity incorrect authorization vulnerability in code.gitea.io/gitea (go), affecting versions >= 1.22.3, <= 1.26.1. It is fixed in 1.26.2. The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions.
CVE-2026-24791 has a CVSS score of 8.1 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
code.gitea.io/gitea (go) versions >= 1.22.3, <= 1.26.1 is affected.
Yes. CVE-2026-24791 is fixed in 1.26.2. Upgrade to this version or later.
Whether CVE-2026-24791 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
Upgrade code.gitea.io/gitea to 1.26.2 or later.