CVE-2026-58443: Gitea Public-Only Token Writes Private Repos and Triggers Actions, CVSS 9.6 PoC Public
Self-hosted Gitea disclosed a critical token-scope bypass, CVE-2026-58443 (CVSS 9.6 Critical). Callers holding only a public-only token can push public commits into a private head repo via the PR update endpoint and trigger private Actions workflows. Fixed in v1.27.0. Full PoC is public.
CVE-2026-58443 (GHSA-xxjv-752h-3vp2) is a critical authorization bypass in the self-hosted Gitea code platform. CVSS 3.1 score 9.6. Callers holding only a public-only scoped token can push commits from a public base repo into a private head repo via the PR update endpoint, then trigger Actions workflows on that private repo—fully bypassing token-scope isolation. Fixed in Gitea v1.27.0. Full PoC is public.
Incident Overview
Gitea is a widely used lightweight self-hosted Git service. Many teams use it as an internal code host and CI trigger. Root cause: PR update API `POST /api/v1/repos/{owner}/{repo}/pulls/{index}/update`. Gitea's public-only check applies only to the route/base repo. `UpdatePullRequest()` runs ordinary RBAC via `IsUserAllowedToUpdate()` against the private head repo and does not re-apply token-scope limits. When the head repo has Actions enabled, the injected push event is treated as a legitimate push and fires the matching `ActionRun` and `ActionRunJob`. Attackers use public-scope credentials against private CI/CD.
Technical Details
Official snippet (`routers/api/v1/api.go:1358-1394`) shows PR routes nested under `checkTokenPublicOnly()`, but the inner `/update` endpoint does not re-check the head repo:
`m.Group("/pulls", func() {
...
m.Group("/{index}", func() {
...
m.Post("/update", reqToken(), repo.UpdatePullRequest)
...
})
}, mustAllowPulls, reqRepoReader(unit.TypeCode), ...
}, repoAssignment(), checkTokenPublicOnly())`
Prerequisites:
- Attacker holds a valid `public-only + write:repository` token
- Token owner has ordinary write access to the target private head branch
- A PR exists such that the public base repo can be merged/rebased onto the private head repo
Classified as CWE-863 Incorrect Authorization. Impacts Integrity and Availability. Scope marked Changed (C). PoC confirms: direct writes to the private repo return 404; the PR update endpoint successfully merges public commits onto the private branch and triggers Actions workflows.
Impact Assessment
All Gitea v1.26.4 and earlier are affected. CVSS vector `AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:H`. On self-hosted deployments, attackers can inject code into private repos and private build pipelines without direct write access to the private repo. EPSS is high. Researchers published a full PoC. No in-the-wild exploitation confirmed. Given the public PoC, and multiple similar Gitea scope-boundary failures found in the last 6 months (reverse-proxy auth bypass, webhook SSRF), mass-exploitation barrier is low.
Remediation
- Patch now: upgrade self-hosted instances to Gitea v1.27.0 or later (fix PR #38406). After upgrade, re-enable Actions and verify expected triggers.
- Token audit: before upgrade, enumerate tokens that combine public-only and write:repository scopes. Revoke unnecessary combinations. Reissue least-privilege tokens.
- Actions audit: inspect recent private-repo `ActionRun` records. Flag runs where `event=push`, `trigger_user_id` does not match the committer, or origin is PR-update.
- Harden RBAC and MFA: enforce MFA for all users. Recheck least privilege on private repos for `Push`, `Push to protected branch`, and `Run workflows`. Treat Token only public as a mandatory security baseline.
- Constrain the endpoint: at the reverse proxy, restrict `/api/v1/repos/*/pulls/*/update` by trusted IP, UA, and rate limits. WAF logs must capture all calls for forensics.
References
- GitHub Advisory GHSA-xxjv-752h-3vp2
- OSV advisory
- Gitea v1.27.0 Release Notes (fix PR #38406)
- SecurityOnline analysis