CVE-2026-76222: GitPython Arbitrary Git Repository Creation Outside Vulnerability
HIGHCVE-2026-76222 is a HIGH (CVSS 8.2) arbitrary git repository creation outside vulnerability (CWE-73: External Control of File Name or Path) affecting GitPython libraries in the PyPI ecosystem. The issue involves arbitrary git repository creation outside the working tree via unvalidated .gitmodules submodule name in gitpython. Remediation is available by upgrading affected packages to version 3.1.58.
CVE-2026-76222 at a Glance
Security Impact
CVSS: 8.2
The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:L).
How to Fix CVE-2026-76222
To remediate this vulnerability, upgrade all affected GitPython dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
pip install GitPython>=3.1.58
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
pip show GitPython
How to Check if You Are Affected
To determine whether your Python environment is affected by CVE-2026-76222, inspect your installed packages using pip:
pip show GitPython
Analyze the terminal output to locate any version of GitPython in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 3.1.58), your application is affected.
Note: The pip show GitPython command checks currently installed packages in your active Python environment.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-73: External Control of File Name or Path, where the product allows user input to control or influence paths or file names that are used in filesystem operations. In GitPython, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
CVE-2026-76222 Affected GitPython Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
GitPython
|
PyPI |
< 3.1.58
|
3.1.58
|
References
Frequently Asked Questions
What is CVE-2026-76222?
Which GitPython versions are affected?
What version fixes CVE-2026-76222?
Is GitPython affected?
What is the CVSS score of CVE-2026-76222?
What CWE is associated with CVE-2026-76222?
How can I check PyPI dependencies for CVE-2026-76222?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-hmq2-w58f-27jc",
"modified" : "2026-09-08T20:55:34Z",
"published" : "2026-08-07T15:45:39Z",
"aliases" : [ "CVE-2026-76222" ],
"summary" : "GitPython: Arbitrary Git Repository Creation Outside the Working Tree via Unvalidated .gitmodules Submodule Name in GitPython",
"details" : "### Summary\nGitPython computes the on-disk location of a submodule's separate Git directory (`.git/modules/<name>`) from the submodule's `.gitmodules` section name with no validation. Because that name is fully attacker-controlled content of a cloned repository, a malicious repository can set a submodule name to a traversal string (e.g. `../../../../home/victim/.something`) and cause GitPython to create and initialize a full Git repository at an attacker-chosen filesystem path outside the intended clone directory. The only precondition is that a victim clones the malicious repository with GitPython and runs submodule initialization (`submodule_update(init=True)` / `sm.update(init=True)`), a very common and often automatic step. Core Git itself already blocks this exact attack class (CVE-2018-11235), but GitPython's independent reimplementation never adopted an equivalent check.\n\n### Details\n`src/GitPython/git/objects/submodule/util.py` `sm_name()` strips the `submodule \"` / `\"` wrapper from a `.gitmodules` `[submodule \"...\"]` header and returns the result unchecked. `Submodule.iter_items()` in `src/GitPython/git/objects/submodule/base.py` reads this via `sm_name(sms)` and assigns it to `sm._name`; unlike the submodule `path`, `name` is never used for a tree lookup, so it is never implicitly validated. `Submodule._module_abspath()` then builds `osp.join(parent_repo.git_dir, \"modules\", name)` - `os.path.join` does not normalize `../` sequences. `Submodule._clone_repo()` passes this value straight to `os.makedirs()` and to `git clone --separate-git-dir=<module_abspath>`, creating and populating a full Git repository (objects, refs, hooks, config) at the escaped path. Attack prerequisite: attacker controls a repository the victim clones and initializes submodules for.\n\n### PoC\n1. Environment: Docker image built `FROM python:3.11-slim`, with `git` installed via `apt-get install -y git` (Debian bookworm packaged version, described in the advisory as \"git 2.x\"; the host-side verification separately used system git `2.34.1`, but no exact version is pinned for the git binary inside this Docker image). GitPython is installed inside the container via `pip install /src/GitPython` from this repository's own source, which the advisory states resolved to the officially released `GitPython==3.1.57` and `gitdb==4.0.12`.\n2. Configuration / preconditions: None beyond what's described - the victim must clone the attacker's repository with GitPython and run submodule initialization (`repo.submodules` + `sm.update(init=True)`, equivalent to `git submodule update --init`).\n3. Commands run (quoted verbatim from the advisory's \"Confirmed test run\" section):\n```bash\n$ docker build -f GHSA/testing/Dockerfile -t ghsa-gitpython-poc .\n$ docker run --rm ghsa-gitpython-poc\n```\n(Per the Dockerfile, `docker run` executes `/work/run_all.sh`, which in turn runs `build_attacker_repo.sh`, then `poc_gitpython.py`, then `poc_control_realgit.sh`.)\n4. Full source of the PoC script (`GHSA/testing/poc_gitpython.py`), verbatim:\n```python\n\"\"\"GHSA-001 PoC: GitPython side.\n\nClones the attacker repo and runs the equivalent of\n`git submodule update --init` via GitPython, then checks whether a git\nrepository was created outside the clone directory.\n\"\"\"\nimport os\nimport shutil\n\nimport git\n\nCLONE_DIR = '/work/victim_clone/repo'\nESCAPE_TARGET = '/tmp/gitpython_poc_escaped_root'\n\n\ndef main():\n shutil.rmtree(os.path.dirname(CLONE_DIR), ignore_errors=True)\n shutil.rmtree(ESCAPE_TARGET, ignore_errors=True)\n os.makedirs(os.path.dirname(CLONE_DIR), exist_ok=True)\n\n print(f'GitPython version: {git.__version__}')\n repo = git.Repo.clone_from('/work/attacker_repo', CLONE_DIR)\n print('Cloned into:', repo.working_tree_dir)\n\n sms = list(repo.submodules)\n for sm in sms:\n print(' submodule name:', repr(sm.name))\n print(' submodule path:', repr(sm.path))\n\n print('escape_target exists before update:', os.path.exists(ESCAPE_TARGET))\n\n for sm in sms:\n try:\n sm.update(init=True)\n except Exception as e:\n print('sm.update raised:', repr(e))\n\n exists = os.path.exists(ESCAPE_TARGET)\n print('escape_target exists after update:', exists)\n if exists:\n print('escape_target contents:', os.listdir(ESCAPE_TARGET))\n\n print('POC_RESULT=VULNERABLE' if exists else 'POC_RESULT=SAFE')\n\n\nif __name__ == '__main__':\n main()\n```\n5. Exact captured terminal output (verbatim, from the original advisory's \"Confirmed test run (Docker, released package)\" section):\n```\n=== GitPython PoC (vulnerable path) ===\nGitPython version: 3.1.57\nCloned into: /work/victim_clone/repo\n submodule name: '../../../../../../tmp/gitpython_poc_escaped_root/modules_dir'\n submodule path: 'legit_dir'\nescape_target exists before update: False\nescape_target exists after update: True\nescape_target contents: ['modules_dir']\nPOC_RESULT=VULNERABLE\n\n=== Control: real git CLI on identical repo ===\nwarning: ignoring suspicious submodule name: ../../../../../../tmp/gitpython_poc_escaped_root/modules_dir\nwarning: ignoring suspicious submodule name: ../../../../../../tmp/gitpython_poc_escaped_root/modules_dir\nfatal: No url found for submodule path 'legit_dir' in .gitmodules\nCONTROL_RESULT=SAFE (real git correctly refused)\n```\n6. Payload: the attacker rewrites the `.gitmodules` section header from `[submodule \"legit_dir\"]` to `[submodule \"../../../../../../tmp/gitpython_poc_escaped_root/modules_dir\"]` (built by `build_attacker_repo.sh`, part of the harness in `GHSA/testing/`). The malicious part is the `../../../../../../` traversal sequence embedded in the submodule *name* (not the tree-validated `path`), which becomes the on-disk target for the submodule's separate git directory.\n7. Expected vs. observed: A safe implementation (as demonstrated by the real `git` CLI control run) rejects the submodule name with \"ignoring suspicious submodule name\" and refuses to create anything outside the repository. GitPython instead created the escape-target directory and a fully-initialized Git repository at `/tmp/gitpython_poc_escaped_root/modules_dir`, confirmed by `escape_target exists after update: True` and its listed contents.\n8. Security impact demonstrated: arbitrary filesystem directory and Git-repository creation at an attacker-chosen absolute path outside the victim's intended clone directory, populated with attacker-controlled content sourced from the submodule's own (also attacker-controlled) `url`.\n\n### Impact\nPath traversal (CWE-22) / external control of file path (CWE-73) leading to arbitrary directory and Git-repository creation outside the intended clone directory. Integrity impact is High (attacker chooses destination path and, via the submodule URL, much of the written content); Confidentiality impact is None (only creation was demonstrated); Availability impact is Low-Medium (disk-exhaustion potential). No authentication is required; the attacker only needs to control a repository the victim clones and initializes submodules for - a routine, often fully-automatic operation in CI pipelines, IDE integrations, and dependency-management tooling.",
"severity" : [ {
"type" : "CVSS_V3",
"score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:N/I:H/A:L"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "PyPI",
"name" : "GitPython"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "3.1.58"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 3.1.57"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-hmq2-w58f-27jc"
}, {
"type" : "ADVISORY",
"url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-76222"
}, {
"type" : "WEB",
"url" : "https://github.com/gitpython-developers/GitPython/pull/2202"
}, {
"type" : "WEB",
"url" : "https://github.com/gitpython-developers/GitPython/commit/4299c990e1ca21896f9485277caf7bb0ae5b404c"
}, {
"type" : "WEB",
"url" : "https://github.com/gitpython-developers/GitPython/commit/e4b8e7d026ca6abb4cf604f8e77093432ce23c06"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/gitpython-developers/GitPython"
}, {
"type" : "WEB",
"url" : "https://github.com/gitpython-developers/GitPython/releases/tag/3.1.58"
}, {
"type" : "WEB",
"url" : "https://github.com/pypa/advisory-database/tree/main/vulns/gitpython/PYSEC-2026-3784.yaml"
}, {
"type" : "WEB",
"url" : "https://www.vulncheck.com/advisories/gitpython-before-path-traversal-via-gitmodules-submodule-name"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-22", "CWE-73" ],
"severity" : "HIGH",
"github_reviewed" : true,
"github_reviewed_at" : "2026-08-07T15:45:39Z",
"nvd_published_at" : null
}
}