CVE-2026-58441

MEDIUMPre-NVD 6.36.3
EchelonGraph scoreLOW confidence

This medium-severity CVE scores 6.3 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
6.3EG
EchelonGraph verdictMonitorLow exploitation likelihood right now — keep watching.
  • Lower severity and no public exploit yet
CISA-KEV: Not listedEPSS PROB: CVSS: 6.3Exploit: None knownExposed: 0

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

Gitea: SSRF in restore-repo via unsanitized pull_request.yml Head.CloneURL

Summary

Gitea's restore-repo CLI command restores a repository from a dump directory/archive. When parsing pull_request.yml from that dump, the Head.CloneURL field is used to add a git remote and fetch from it with no validation, because the safety check that's supposed to guard it (CheckAndEnsureSafePR) is called with an empty commonCloneBaseURL, which silently disables it. This lets a malicious dump make the Gitea server execute git fetch against an attacker-chosen URL (SSRF), or disclose a local git repository via file://. This is a different root cause from the recently fixed path-traversal issue in the same command (#38215), which patched DownloadURL/PatchURL but not Head.CloneURL.

Details

services/migrations/restore.go's GetPullRequests() unmarshals pull_request.yml directly into base.PullRequest structs with no validation of Head.CloneURL:

err = yaml.Unmarshal(bs, &pulls)
...
for _, pr := range pulls {
    if pr.PatchURL != "" {
        pr.PatchURL = "file://" + util.FilePathJoinAbs(r.baseDir, pr.PatchURL)
    }
    CheckAndEnsureSafePR(pr, "", r)   // <-- empty baseURL
}

CheckAndEnsureSafePR (services/migrations/common.go) is supposed to reject Head.CloneURL/PatchURL values that don't share a common base URL:

func hasBaseURL(toCheck, baseURL string) bool {
    if len(baseURL) > 0 && baseURL[len(baseURL)-1] != '/' {
        baseURL += "/"
    }
    return strings.HasPrefix(toCheck, baseURL)
}

func CheckAndEnsureSafePR(pr *base.PullRequest, commonCloneBaseURL string, g base.Downloader) bool { valid := true if pr.PatchURL != "" && !hasBaseURL(pr.PatchURL, commonCloneBaseURL) { pr.PatchURL = "" valid = false } if pr.Head.CloneURL != "" && !hasBaseURL(pr.Head.CloneURL, commonCloneBaseURL) { pr.Head.CloneURL = "" valid = false } return valid }

strings.HasPrefix(anything, "") is always true in Go. Because restore.go is the only caller that passes "" as commonCloneBaseURL, this check is a complete no-op on the restore-repo path — Head.CloneURL survives unchanged regardless of its value. Every other downloader (github.go, gitlab.go, gitea_downloader.go, codebase.go, codecommit.go, onedev.go) passes a real base URL, so they are not affected.

services/migrations/gitea_uploader.go then uses the unvalidated value directly:

err := g.gitRepo.AddRemote(remote, pr.Head.CloneURL, true)
// ... later: fetch from that remote

resulting in the server executing git fetch against an attacker-controlled URL sourced from the dump file.

RCE via git's ext:: transport helper was tested and ruled out — a normal git install rejects it by default (fatal: transport 'ext' not allowed), independent of Gitea's own configuration. This report is scoped to SSRF and local git-repository disclosure.

Confirmed present, byte-for-byte identical, in v1.26.4 (latest stable tag), release/v1.27, and main, by direct checkout and diff.

PoC

  • Create a dump directory following the normal restore-repo layout
(repo.yml, etc.), and add a pull_request.yml containing at least one entry with:
- number: 1
     head:
       cloneURL: "http://:/ssrf-proof"
       ref: "main"
  • Run gitea restore-repo against that dump directory for any repo
owner.
  • Observe on the target host/listener: an actual git HTTP
discovery request arrives, e.g. GET /ssrf-proof/info/refs?service=git-upload-pack, driven entirely by the value from the dump file.

Verified the core mechanism (steps 2–3, i.e. the unvalidated Head.CloneURL surviving CheckAndEnsureSafePR("") and then being used in a real git remote add + git fetch) with a minimal, standalone Go program built from the verbatim, unmodified hasBaseURL / CheckAndEnsureSafePR function bodies (attached: gitea_ssrf_poc.go), run end-to-end against a local HTTP listener. The listener's access log confirms the request actually arrives.

Impact

An attacker who can get an administrator to run gitea restore-repo against a malicious dump (the same threat model already accepted for the just-fixed path-traversal issue in this command, #38215) can make the Gitea server issue a git fetch against an arbitrary attacker-chosen URL. This allows:
  • SSRF against internal-only services or cloud metadata endpoints
reachable from the Gitea host.
  • Disclosure of local git repositories reachable via file:// paths
readable by the Gitea process.

No public disclosure planned. Happy to provide further detail on request.

CVSS v3
6.3
EG Score
6.3(low)
EG Risk
33(Track)
EG Risk 33/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
Severity63% × 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-58441(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
code.gitea.io/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-27 09:11 UTCEG score recompute
  2. 2026-07-23 03:21 UTCEG score recompute
  3. 2026-07-21 21:20 UTCEG score recompute

Frequently asked(4)

What is CVE-2026-58441?
CVE-2026-58441 is a medium vulnerability published on July 21, 2026. Gitea: SSRF in restore-repo via unsanitized pull_request.yml Head.CloneURL Summary Gitea's restore-repo CLI command restores a repository from a dump directory/archive. When parsing pull_request.yml from that dump, the Head.CloneURL field is used to add a git remote and fetch from it with no…
When was CVE-2026-58441 disclosed?
CVE-2026-58441 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-58441?
CVE-2026-58441 has a CVSS v4.0 base score of 6.3 (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-58441?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-58441, 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-58441

Explore →

Is Your Infrastructure Affected by CVE-2026-58441?

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