🛡️ CVE-2026-58424 — gitea

🟠 CVSS 8.9 — High ✅ No Known Exploit CWE-285 NVD
8.9
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

Gitea: Permanent Fork PR Workflow Approval Gate Bypass

| Field | Value |

|-------|-------|

| Identifier (researcher-assigned) | GITEA-2026-004 |

| Product | Gitea (self-hosted Git service) |

| Component | Gitea Actions — fork pull request approval gate |

| Affected versions | All Gitea releases v1.20.0 and later, including the latest main (1.27.0+dev-289-gb7e95cc48c). The buggy logic was introduced in commit edf98a2dc3 — *"Require approval to run actions for fork pull request (#22803)"*, 2023-02-24 — and has shipped unchanged since. |

| Fixed in | not yet (this disclosure) |

| Authentication required | Yes — one unprivileged Gitea account capable of forking the target repository (the default ability for every authenticated user) |

| User interaction required | Exactly once — a repository administrator must approve a single benign fork PR's workflow run from the attacker. After that, *no further interaction is ever required* for any future fork PR from the same attacker on the same repository. |

| Discovered by | Prakhar Porwal — [email protected] |

| Live-verified on | Gitea main at commit b7e95cc48cc0e0d6fe24c89bb83da5b84a74490f, 2026-05-24 |

1. Executive summary

Gitea Actions enforces an approval gate on workflow runs triggered by fork pull requests, so that an untrusted contributor cannot execute arbitrary workflow YAML on the maintainer's runner infrastructure without explicit consent. The gate is implemented by ifNeedApproval() in services/actions/notifier_helper.go. Its final clause skips the gate whenever the triggering user has any previously-approved run in the same repository:

```go

// services/actions/notifier_helper.go:423-433

if count, err := db.Count[actions_model.ActionRun](ctx, actions_model.FindRunOptions{

RepoID: repo.ID,

TriggerUserID: user.ID,

Approved: true,

}); err != nil {

return false, fmt.Errorf("CountRuns: %w", err)

} else if count > 0 {

log.Trace("do not need approval because user %d has been approved before", user.ID)

return false, nil

}

```

The check is scoped to (repo_id, trigger_user_id) only. It does not consider the pull request, the head commit, the workflow file contents, or any time window. The single approval click on a contributor's *first* fork PR is therefore interpreted by Gitea as *"this user is permanently trusted to execute any workflow YAML on this repository's CI infrastructure forever"* — for every future PR, on any branch, against any commit, regardless of what the workflow does.

This is a structural deviation from the documented intent — the in-source comment on the bypassing path reads *"if it's the first time user … triggered actions"*, implying a per-action-trigger check that the code does not actually perform. It is also a deviation from the equivalent behavior on the platform Gitea Actions is modeled after (GitHub Actions), where the first-contributor gate persists until a PR is merged, not merely approved-to-run.

I have live-reproduced the bypass end-to-end against a current main build. With zero further interaction from the maintainer after the one-time approval, an attacker's second PR's workflow:

  • Was created with need_approval = 0 and approved_by = 0 in the action_run table (i.e. nobody ever approved it, and yet it was not gated).
  • Was dispatched to the runner immediately.
  • Executed arbitrary shell on the runner, with outbound network access, a populated GITHUB_TOKEN, and access to the cloned source.

Full receipts are in §3.

2. Affected code

Primary

services/actions/notifier_helper.go:401-438 — the ifNeedApproval function:

```go

func ifNeedApproval(ctx context.Context, run *actions_model.ActionRun,

repo *repo_model.Repository, user *user_model.User) (bool, error) {

// 1. don't need approval if it's not a fork PR

// 2. don't need approval if the event is pull_request_target since the

// workflow will run in the context of base branch

if !run.IsForkPullRequest ||

run.TriggerEvent == actions_module.GithubEventPullRequestTarget {

return false, nil

}

// always need approval if the user is restricted

if user.IsRestricted {

return true, nil

}

// don't need approval if the user can write

if perm, err := access_model.GetDoerRepoPermission(ctx, repo, user); err != nil {

return false, fmt.Errorf("GetDoerRepoPermission: %w", err)

} else if perm.CanWrite(unit_model.TypeActions) {

return false, nil

}

// ===== VULNERABLE BLOCK ==================================================

// don't need approval if the user has been approved before

if count, err := db.Count[actions_model.ActionRun](ctx, actions_model.FindRunOptions{

RepoID: repo.ID,

TriggerUserID: user.ID,

Approved: true,

}); err != nil {

return false, fmt.Errorf("CountRuns:

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs low-level privileges on the target. A user must be tricked into taking some action. The scope is changed, meaning a successful attack can affect components beyond the vulnerable one. Rated impact: confidentiality low, integrity high, availability high.

Weakness class

CVE-2026-58424 is classified as CWE-285: Improper Authorization. A request is carried out without confirming that the caller is permitted to perform it on that specific resource.

Affected software

CVE-2026-58424 is recorded against 2 packages.

  • code.gitea.io/gitea
  • unknown

Timeline and source

Published on 21 July 2026 and last revised on 22 July 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.

References

github.com (Web)
nvd.nist.gov (Advisory)
github.com (Web)
github.com (Web)
github.com (Web)
blog.gitea.com (Web)
github.com (Package)
github.com (Web)

Details

Severity HIGH
CVSS Score 8.9
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:H/A:H
CWE CWE-285
Public Exploit ✅ No
Source NVD
Published 2026-07-21
Updated 2026-08-12
Modified 2026-07-22
Fix URL N/A

Affected Packages

Software From version Fixed in
code.gitea.io/gitea
unknown

Similar Threats

Site Security Check

Is gitea part of your stack?

CVE-2026-58424 is rated CVSS 8.9 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.