PT-2026-63304 · Go · Gitea.Dev
CVE-2026-58438
·
Published
2026-07-21
·
Updated
2026-07-21
CVSS v4.0
2.3
Low
| Vector | AV:N/AC:H/AT:P/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:L/SA:N |
Details
RemoveDependency in routers/web/repo/issue dependency.go takes a removeDependencyID form parameter identifying the other issue by its global numeric ID, and fetches it with issues model.GetIssueByID(ctx, depID) - no repository or permission check at all. It then calls issues model.RemoveIssueDependency(ctx, ctx.Doer, issue, dep, depType) (models/issues/dependency.go), which deletes the dependency join row and then writes a comment referencing the removal, attributed to the calling user, onto the dependency record.The sibling function in the very same file,
AddDependency, does this correctly when the two issues are in different repos (which ALLOW CROSS REPOSITORY DEPENDENCIES, on by default, permits):go
if issue.RepoID != dep.RepoID {
if !setting.Service.AllowCrossRepositoryDependencies { ... }
depRepoPerm, err := access model.GetDoerRepoPermission(ctx, dep.Repo, ctx.Doer)
if !depRepoPerm.CanReadIssuesOrPulls(dep.IsPull) {
return // you can't see this dependency
}
}RemoveDependency has no equivalent block at all - it goes straight from resolving dep by ID to deleting the link, regardless of which repo dep lives in or whether the caller can see it. I confirmed this same code is present in the current latest release, v1.26.4.PoC
Prerequisites: an account with write access to issues on some repo
ownerA/repoA, and the global numeric issue ID of an issue in a private repo repoB that is (or was) legitimately dependency-linked to one of the attacker's issues in repoA (cross-repo dependencies are commonly used between related public/private repos, and ALLOW CROSS REPOSITORY DEPENDENCIES defaults to enabled).bash
curl -s -b "gitea session=$ATTACKER SESSION COOKIE" -X POST
--data-urlencode "removeDependencyID=<repoB issue global id>"
--data-urlencode "dependencyType=blockedBy"
"https://TARGET HOST/ownerA/repoA/issues/N/dependency/delete"
# Expected: the dependency link is deleted and a "removed dependency" comment
# authored by the attacker is added to the repoB issue, even though the
# attacker has no read access to repoB.Impact
This is a cross-repository IDOR / broken access control issue. An attacker can tamper with issue-tracking state (dependency relationships) and inject an attacker-authored comment into a private repository they cannot otherwise read or write to, crossing a trust boundary the "add" path explicitly enforces. Impact is bounded - it requires an existing dependency link and discloses no repository content - but it is a genuine unauthorized-write primitive across a private-repo boundary.
Fix
Add the same cross-repo permission check used in
AddDependency (access model.GetDoerRepoPermission(ctx, dep.Repo, ctx.Doer).CanReadIssuesOrPulls(dep.IsPull)) to RemoveDependency before allowing the deletion to proceed when issue.RepoID != dep.RepoID.If possible, please apply for a CVE number when publishing. I would greatly appreciate it.
Fix
Missing Authorization
Found an issue in the description? Have something to add? Feel free to write us 👾
Weakness Enumeration
Related Identifiers
Affected Products
Gitea.Dev