CVE-2026-58416: Gitea Fork-PR Actions Task Can Read Vulnerability
MODERATECVE-2026-58416 is a MODERATE (CVSS 6.3) fork-pr actions task can read vulnerability (CWE-863: Incorrect Authorization) affecting Gitea libraries in the Go ecosystem. The issue involves fork-pr actions task can read a third private repository via the collaborative-owner branch (missing fork-pr guard). Remediation is available by upgrading affected packages to version 1.27.0.
CVE-2026-58416 at a Glance
Security Impact
CVSS: 6.3
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N).
How to Fix CVE-2026-58416
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 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 gitea.dev
How to Check if You Are Affected
To determine whether your application is affected by CVE-2026-58416, inspect your project dependencies for Gitea:
inspect dependencies for gitea.dev
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-58416 Affected Gitea Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
gitea.dev
|
Go |
< 1.27.0
|
1.27.0
|
References
Frequently Asked Questions
What is CVE-2026-58416?
Which Gitea versions are affected?
What version fixes CVE-2026-58416?
Is gitea.dev affected?
What is the CVSS score of CVE-2026-58416?
What CWE is associated with CVE-2026-58416?
How can I check Go dependencies for CVE-2026-58416?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-fj8v-hjwv-qm88",
"modified" : "2026-07-21T20:40:03Z",
"published" : "2026-07-21T20:40:03Z",
"aliases" : [ "CVE-2026-58416" ],
"summary" : "Gitea: Fork-PR Actions task can read a third private repository via the collaborative-owner branch (missing fork-PR guard)",
"details" : "### Summary\n\n`GetActionsUserRepoPermission` (`models/perm/access/repo_permission.go`) decides whether an Actions\ntask token may access a target repo. Its cross-repo branches each enforce a fork-PR discriminator —\n**except the collaborative-owner branch**, which is missing the `!task.IsForkPullRequest` guard that\nits sibling has. As a result, when a private repo **B** lists owner **A** as a collaborative owner, an\n**attacker-controlled fork pull-request** workflow whose base repo is owned by A is granted code-read\non B — i.e. the fork's YAML can clone a third private repository it has no rights to.\n\n### Details\n\n```go\n// models/perm/access/repo_permission.go (v1.26.2), in GetActionsUserRepoPermission\nif checkSameOwnerCrossRepoAccess(ctx, taskRepo, repo, task.IsForkPullRequest) { // passes isForkPR -> denies forks\n return maxPerm, nil\n}\n...\nif taskRepo.IsPrivate { // <-- NO IsForkPullRequest check here\n actionsUnit := repo.MustGetUnit(ctx, unit.TypeActions)\n if actionsUnit.ActionsConfig().IsCollaborativeOwner(taskRepo.OwnerID) {\n return maxPerm, nil // grants code-read to target repo B\n }\n}\n```\n\nThe sibling same-owner path correctly denies fork PRs:\n\n```go\nfunc checkSameOwnerCrossRepoAccess(ctx, taskRepo, targetRepo, isForkPR bool) bool {\n if isForkPR {\n return false // Fork PRs are never allowed cross-repo access to other private repositories.\n }\n ...\n}\n```\n\n`taskRepo` = the repo whose workflow is running (the PR's base repo A); `repo` = the target being\ncloned (B). `IsCollaborativeOwner(taskRepo.OwnerID)` asks \"does target B's Actions config trust A's\nowner for cross-repo read?\" When B trusts ownerA, the branch returns `maxPerm` (code-read) **even when\n`task.IsForkPullRequest` is true** — i.e. when the executing YAML is the fork's, not A's.\n\nEvery sibling enforces the fork-PR discriminator; except for this branch:\n`checkSameOwnerCrossRepoAccess` denies forks; `ComputeTaskTokenPermissions`\n(`models/actions/token_permissions.go`) only clamps the token *ceiling* to read-only for fork/cross-repo\n(its own comment notes the access *decision* is in `GetActionsUserRepoPermission`, so it does not\nneutralize the gap — it just makes the leak read-only); secrets (`models/secret/secret.go`) and the\napproval gate (`services/actions/notifier_helper.go`) both correctly key on `IsForkPullRequest`.\n\n**Reachability** — the runner clones target repo B over git-HTTP with the task token:\n`routers/web/repo/githttp.go` → `GetDoerRepoPermission(ctx, repoB, ActionsUser)` →\n`GetActionsUserRepoPermission(ctx, repoB, actionsUser, taskID)` with `IsForkPullRequest == true` →\ncollaborative-owner branch returns code-read → `p.CanAccess(Read, code)` passes → private clone of B\nsucceeds. (`CheckRepoScopedToken` in githttp is a no-op for the Actions token.)\n\n### PoC\n\nSetup: private base repo A (`usera/repoA`), private third repo\nB (`userb/repoB`) with a planted `SECRET.txt`, B's Actions config trusting `usera` as a collaborative\nowner, and a genuine running fork-PR task token (`token_hash` computed with Gitea's own `HashToken`)\npresented as HTTP Basic. Requesting `GET /userb/repoB.git/info/refs?service=git-upload-pack`:\n\n| Condition (same fork-PR token) | HTTP | Meaning |\n|---|---|---|\n| anonymous (no token) | 401 | auth required |\n| token, A **public**, B trusts A | 404 | branch gated on `taskRepo.IsPrivate` ⇒ A public skips it |\n| token, A private, B has **no** collab-owner config | 404 | no trust ⇒ denied |\n| **token, A private, B trusts A (collab-owner)** | **200** | **`git clone` of private B succeeds** |\n| config removed / restored | 404 / 200 | deterministic |\n\nIn the 200 case, `git clone` of private repo B succeeded and yielded its `SECRET.txt` — the full source\nof a third private repo the fork-PR author has no rights to.\n\n### Impact\n\nRead-only confidentiality breach: discloses the full source of a *third* private repository (B) to an\nuntrusted external fork-PR author. Read-only, not write/RCE.\n\nPreconditions (honest):\n1. B is deliberately configured with a collaborative owner — but that is exactly the feature's intended\n use, so realistic for any deployment using it.\n2. The fork PR's base repo A is itself private (the branch is gated on `taskRepo.IsPrivate`). Forking a\n private A already requires read on A, so this is a normal internal-contributor situation, not a\n weakening — the escalation is \"read A (granted) → read a *different* private repo B (never granted).\"\n3. The fork-PR workflow must actually run — most realistically via an attacker who had one earlier PR\n approved (the \"approved before\" path in `ifNeedApproval`), after which fork PRs auto-run.\n\n### Suggested remediation\n\nAdd the same fork-PR guard the sibling path has (one line):\n\n```go\nif taskRepo.IsPrivate && !task.IsForkPullRequest {\n actionsUnit := repo.MustGetUnit(ctx, unit.TypeActions)\n if actionsUnit.ActionsConfig().IsCollaborativeOwner(taskRepo.OwnerID) {\n return maxPerm, nil\n }\n}\n```\n\nThis flips `Vuln_ForkPR_LeaksThirdPrivateRepo` to PASS, keeps `Control_NonFork_Allowed` PASS\n(legitimate collaborative-owner sharing still works), and leaves the existing\n`TestGetActionsUserRepoPermission` suite all green.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:N/A:N"
} ],
"affected" : [ {
"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-fj8v-hjwv-qm88"
}, {
"type" : "WEB",
"url" : "https://github.com/go-gitea/gitea/pull/38214"
}, {
"type" : "WEB",
"url" : "https://github.com/go-gitea/gitea/commit/1d43b736b5a16c5f80cfdcd9a9448a9c983ddaa0"
}, {
"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-280", "CWE-863" ],
"severity" : "MODERATE",
"github_reviewed" : true,
"github_reviewed_at" : "2026-07-21T20:40:03Z",
"nvd_published_at" : null
}
}