CVE-2026-76220 — gitpython
Fix: gitpython-developers/GitPython#2204CVE-2026-76220 is a CWE-88 vulnerability in gitpython. A fix is available for gitpython — see the affected versions and patch details below.
GitPython before 3.1.58 Command Execution via split_single_char_options
Exploitation Status
No confirmed exploitation observed yet
- A successful exploit gives an attacker total control of the affected component, not partial access.
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-76220.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
Real-World Exposure
gitpythonReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
The check_unsafe_options guard can be bypassed on every guarded method (clone/clone_from, fetch/pull/push, ls_remote, iter_commits, blame, archive) by combining a single-character kwarg with split_single_char_options=False. The guard's candidate list omits the smuggled option, but transform_kwarg emits a JOINED -n<value> argv token that git parses as --upload-pack=<cmd>, yielding arbitrary command execution at the default allow_unsafe_options=False. This is an incomplete-fix bypass of commit e8d0fbf7 (the fix for GHSA-r9mr-m37c-5fr3), which only emits value-derived candidates when split_single_char_options is True.
Root Cause
_option_candidates derives value-token candidates only under if len(key)==1 and split_single_char_options: (cmd.py:1048, added by e8d0fbf7). With split_single_char_options=False, _option_candidates([], {"n":"utouch <cmd>;git-upload-pack"}) returns only ['-n'] (not on the denylist), so the guard passes. But transform_kwarg('n', value, split_single_char_options=False) emits the JOINED token -nutouch <cmd>;git-upload-pack (cmd.py:1631). git clusters value-less short flags then parses -u<cmd> = --upload-pack=<cmd> → command execution. The hardened guard WOULD block the joined token if it saw it — the flaw is it never receives it.
Impact
Arbitrary OS command execution as the host process (via --upload-pack) at default allow_unsafe_options=False, affecting all guarded methods that forward kwargs. Precondition: the app forwards a user-controlled kwargs dict containing split_single_char_options=False plus a single-char key (same user-dict-forwarding model GHSA-r9mr-m37c-5fr3 accepts).
Proof of Concept
from git import Repo
Repo.clone_from(src, dst,
n="utouch /tmp/ACE;git-upload-pack",
split_single_char_options=False) # /tmp/ACE created -> ACE
Attack Chain
- Entry: app forwards user kwargs to
Repo.clone_from(url, path, **kwargs):{split_single_char_options: False, n: 'utouch /tmp/ACE;git-upload-pack'}. - Check:
check_unsafe_options(_option_candidates([], kwargs), unsafe_git_clone_options). Guard: denylist includes--upload-pack/-u. Bypass proof:_option_candidatesyields only['-n'](value token skipped becausesplit=False); guard never sees-u. - Sink:
transform_kwargemits joined token (cmd.py:1631). argv (observed):['git','clone','-v','-nutouch /tmp/ACE;git-upload-pack','--','<src>','<dst>']. - Impact: git clusters
-n+-u<cmd>→ runs upload-pack command → ACE.
Bypass Evidence
Independently reproduced (gate harness, default allow_unsafe_options=False): the split=False payload created the marker VH05_GATE_ACE (ACE); the clone returned normally (guard bypassed). Control: n='--upload-pack=…' (split default True) → UnsafeOptionError: --upload-pack is not allowed. Fix-commit read: e8d0fbf7 extends candidates only under if len(key)==1 and split_single_char_options: — split=False skips value emission. Also confirmed the earlier clustering-parse fix (commit 56806080) does not cover this because the guard only ever receives ['-n'].
Affected Versions
GitPython <= 3.1.57 (code present verbatim on the latest release tag).
Suggested Fix
Make _option_candidates emit value-derived candidates regardless of split_single_char_options (i.e. also for the joined -n<value> form), OR run check_unsafe_options over the fully-transformed argv rather than the reconstructed name-only candidate list.
Reported by zx (Jace) — GitHub: @manus-use
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | gitpython | all versions | 3.1.58pip install --upgrade 'gitpython==3.1.58' |
Affected Products
gitpythongitpython_projectDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for gitpython, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update gitpython to 3.1.58 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-76220 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
GitPython's check_unsafe_options guard can be bypassed by combining a single-character keyword argument with split_single_char_options=False, letting an attacker who supplies a crafted kwargs dictionary to guarded methods such as clone_from emit a joined token parsed as `--upload-pack` and achieve arbitrary OS command…
Do not pass untrusted or attacker-influenced keyword arguments to GitPython's guarded methods such as clone_from, and do not set split_single_char_options=False on untrusted input. Upgrade to GitPython 3.1.58 or later, where the check_unsafe_options bypass is fixed.Source: Red Hat security advisory for CVE-2026-76220 (CC BY 4.0)
| Product | Fixed in | Advisory |
|---|---|---|
| Red Hat Satellite 6.19 for RHEL 9 | python3.12-gitpython-0:3.1.59-1.el9pc | RHSA-2026:63385 |
| Red Hat Ansible Automation Platform 2.5 | ansible-automation-platform-25/controller-rhel8:1789607021 | RHSA-2026:71210 |
| Red Hat Ansible Automation Platform 2.6 | ansible-automation-platform-26/controller-rhel9:1789673739 | RHSA-2026:71179 |
| Red Hat Ansible Automation Platform 2.7 | ansible-automation-platform-27/controller-rhel9:1789580684 | RHSA-2026:71177 |
| Red Hat Satellite 6.18 | satellite/iop-vmaas-rhel9:1789611858 | RHSA-2026:68764 |
| Red Hat Satellite 6.18 | satellite/iop-vulnerability-engine-rhel9:1789637082 | RHSA-2026:68771 |
| Red Hat Satellite 6.19 | satellite/iop-vulnerability-engine-rhel9:1789607605 | RHSA-2026:68776 |
| Red Hat Satellite 6.19 | satellite/iop-vmaas-rhel9:1789611998 | RHSA-2026:68780 |
Frequently Asked Questions
Is CVE-2026-76220 in your dependencies?
Find it across PyPI, including transitive dependencies.