Severity by source
AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:N
Network vector via git-HTTP, high complexity for three-condition prerequisite, PR:L for authenticated contributor; scope changes to repo B; integrity set to None per the description's explicit read-only characterization, diverging from the vendor's I:L.
Primary rating from Vendor (https://github.com/go-gitea/gitea).
CVSS VectorVendor: https://github.com/go-gitea/gitea
Lifecycle Timeline
6DescriptionCVE.org
Summary
GetActionsUserRepoPermission (models/perm/access/repo_permission.go) decides whether an Actions task token may access a target repo. Its cross-repo branches each enforce a fork-PR discriminator - except the collaborative-owner branch, which is missing the !task.IsForkPullRequest guard that its sibling has. As a result, when a private repo B lists owner A as a collaborative owner, an attacker-controlled fork pull-request workflow whose base repo is owned by A is granted code-read on B - i.e. the fork's YAML can clone a third private repository it has no rights to.
Details
// models/perm/access/repo_permission.go (v1.26.2), in GetActionsUserRepoPermission
if checkSameOwnerCrossRepoAccess(ctx, taskRepo, repo, task.IsForkPullRequest) { // passes isForkPR -> denies forks
return maxPerm, nil
}
...
if taskRepo.IsPrivate { // <-- NO IsForkPullRequest check here
actionsUnit := repo.MustGetUnit(ctx, unit.TypeActions)
if actionsUnit.ActionsConfig().IsCollaborativeOwner(taskRepo.OwnerID) {
return maxPerm, nil // grants code-read to target repo B
}
}The sibling same-owner path correctly denies fork PRs:
func checkSameOwnerCrossRepoAccess(ctx, taskRepo, targetRepo, isForkPR bool) bool {
if isForkPR {
return false // Fork PRs are never allowed cross-repo access to other private repositories.
}
...
}taskRepo = the repo whose workflow is running (the PR's base repo A); repo = the target being cloned (B). IsCollaborativeOwner(taskRepo.OwnerID) asks "does target B's Actions config trust A's owner for cross-repo read?" When B trusts ownerA, the branch returns maxPerm (code-read) even when task.IsForkPullRequest is true - i.e. when the executing YAML is the fork's, not A's.
Every sibling enforces the fork-PR discriminator; except for this branch: checkSameOwnerCrossRepoAccess denies forks; ComputeTaskTokenPermissions (models/actions/token_permissions.go) only clamps the token *ceiling* to read-only for fork/cross-repo (its own comment notes the access *decision* is in GetActionsUserRepoPermission, so it does not neutralize the gap - it just makes the leak read-only); secrets (models/secret/secret.go) and the approval gate (services/actions/notifier_helper.go) both correctly key on IsForkPullRequest.
Reachability - the runner clones target repo B over git-HTTP with the task token: routers/web/repo/githttp.go → GetDoerRepoPermission(ctx, repoB, ActionsUser) → GetActionsUserRepoPermission(ctx, repoB, actionsUser, taskID) with IsForkPullRequest == true → collaborative-owner branch returns code-read → p.CanAccess(Read, code) passes → private clone of B succeeds. (CheckRepoScopedToken in githttp is a no-op for the Actions token.)
PoC
Setup: private base repo A (usera/repoA), private third repo B (userb/repoB) with a planted SECRET.txt, B's Actions config trusting usera as a collaborative owner, and a genuine running fork-PR task token (token_hash computed with Gitea's own HashToken) presented as HTTP Basic. Requesting GET /userb/repoB.git/info/refs?service=git-upload-pack:
| Condition (same fork-PR token) | HTTP | Meaning |
|---|---|---|
| anonymous (no token) | 401 | auth required |
| token, A public, B trusts A | 404 | branch gated on taskRepo.IsPrivate ⇒ A public skips it |
| token, A private, B has no collab-owner config | 404 | no trust ⇒ denied |
| token, A private, B trusts A (collab-owner) | 200 | git clone of private B succeeds |
| config removed / restored | 404 / 200 | deterministic |
In the 200 case, git clone of private repo B succeeded and yielded its SECRET.txt - the full source of a third private repo the fork-PR author has no rights to.
Impact
Read-only confidentiality breach: discloses the full source of a *third* private repository (B) to an untrusted external fork-PR author. Read-only, not write/RCE.
Preconditions (honest):
- B is deliberately configured with a collaborative owner - but that is exactly the feature's intended
use, so realistic for any deployment using it.
- The fork PR's base repo A is itself private (the branch is gated on
taskRepo.IsPrivate). Forking a
private A already requires read on A, so this is a normal internal-contributor situation, not a weakening - the escalation is "read A (granted) → read a *different* private repo B (never granted)."
- The fork-PR workflow must actually run - most realistically via an attacker who had one earlier PR
approved (the "approved before" path in ifNeedApproval), after which fork PRs auto-run.
Suggested remediation
Add the same fork-PR guard the sibling path has (one line):
if taskRepo.IsPrivate && !task.IsForkPullRequest {
actionsUnit := repo.MustGetUnit(ctx, unit.TypeActions)
if actionsUnit.ActionsConfig().IsCollaborativeOwner(taskRepo.OwnerID) {
return maxPerm, nil
}
}This flips Vuln_ForkPR_LeaksThirdPrivateRepo to PASS, keeps Control_NonFork_Allowed PASS (legitimate collaborative-owner sharing still works), and leaves the existing TestGetActionsUserRepoPermission suite all green.
AnalysisAI
Gitea's Actions permission engine grants fork-PR task tokens unauthorized code-read access to third private repositories via a missing fork-PR discriminator in the collaborative-owner branch of GetActionsUserRepoPermission. All Gitea instances running versions up to and including 1.26.4 are affected when the collaborative-owner Actions feature is configured, as confirmed by vendor advisory GHSA-fj8v-hjwv-qm88 and EUVD-2026-58151. An attacker who has had at least one fork PR previously approved can exploit this to clone the full source of a private repository they were never authorized to access; impact is strictly read-only, with no write or code-execution capability. A functional PoC was included in the security advisory; this CVE is not in the CISA KEV catalog and no EPSS score was provided in the available data.
Technical ContextAI
The vulnerability resides in models/perm/access/repo_permission.go within the GetActionsUserRepoPermission function, which is Gitea's sole access-decision point for Actions task tokens when they request repository access over git-HTTP. The function contains multiple cross-repo access branches; all siblings correctly pass the IsForkPullRequest flag to deny fork-PR tokens - the checkSameOwnerCrossRepoAccess path explicitly returns false when isForkPR is true - but the collaborative-owner branch (actionsUnit.ActionsConfig().IsCollaborativeOwner(taskRepo.OwnerID)) was implemented without this guard, returning maxPerm (code-read) unconditionally. The attack surface is the git-HTTP clone path in routers/web/repo/githttp.go, which resolves permissions via this function and passes the result to p.CanAccess(Read, code). Notably, ComputeTaskTokenPermissions in models/actions/token_permissions.go only clamps the token ceiling to read-only for fork/cross-repo scenarios; its own code comments acknowledge the access decision belongs to GetActionsUserRepoPermission, so it does not neutralize the gap. The root cause is CWE-280 (Improper Handling of Insufficient Permissions or Privileges) - specifically an inconsistently applied security discriminator across sibling authorization branches. The affected package is gitea.dev (Go module, CPE: pkg:go/gitea.dev), versions less than 1.27.0.
RemediationAI
Upgrade to Gitea v1.27.0, which contains the one-line fix introduced in PR #38214 (commit 1d43b736b5a16c5f80cfdcd9a9448a9c983ddaa0) that adds && !task.IsForkPullRequest to the collaborative-owner branch condition in GetActionsUserRepoPermission. The fix version is confirmed by the release tag at https://github.com/go-gitea/gitea/releases/tag/v1.27.0 and the vendor release blog at https://blog.gitea.com/gitea-1.27.0-is-released/. If immediate upgrade is not feasible, the most targeted compensating control is to remove all collaborative-owner entries from repository Actions configurations - navigate to each repository's Actions settings and clear any trusted collaborative-owner entries; this disables the vulnerable code path entirely but also disables legitimate cross-repo access for non-fork workflows, which is a meaningful functional trade-off for teams relying on that feature. A secondary mitigation is to configure the Actions approval gate to require manual approval for all external and first-time contributors on every fork PR, which prevents attacker-controlled fork workflows from auto-running; however, this does not close the vulnerability for attackers who already have a previously-approved PR and is not a substitute for patching. The patch PR and commit are independently verifiable at https://github.com/go-gitea/gitea/pull/38214.
Remote code execution in Gitea (self-hosted Git service) via a code-injection flaw (CWE-94) allows attackers to run arbi
Broken access control in Gitea's Composer package registry (versions up to and including 1.26.1) lets remote attackers r
Gitea before 1.16.7 does not escape git fetch remote. Rated high severity (CVSS 7.5), this vulnerability is remotely exp
The git hook feature in Gitea 1.1.0 through 1.12.5 might allow for authenticated remote code execution in customer envir
Improper Neutralization of Input During Web Page Generation (XSS or 'Cross-site Scripting') vulnerability in Gitea Gitea
Reverse-proxy authentication bypass in the official Gitea Docker image (versions up to and including 1.26.2) allows any
Container escape in Gitea act_runner (Docker backend, through act 0.262.0) lets an authenticated user with workflow-exec
models/repo_mirror.go in Gitea before 1.7.6 and 1.8.x before 1.8-RC3 mishandles mirror repo URL settings, leading to rem
Server-side request forgery and internal repository exfiltration in Gitea before 1.27.0 lets a low-privileged authentica
Authorization bypass in Gitea versions 1.22.3 through 1.26.1 allows holders of `public-only` access tokens or OAuth gran
An issue was discovered in Gitea through 1.11.5. Rated high severity (CVSS 7.5), this vulnerability is remotely exploita
Missing Authorization in GitHub repository go-gitea/gitea prior to 1.16.4. Rated high severity (CVSS 7.1), this vulnerab
Same technique Information Disclosure
View allVendor 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 |
| openSUSE Leap 15.6 | Affected |
Share
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-58151
GHSA-fj8v-hjwv-qm88