GitHub Advisoryadvisory · PyPI

GHSA-wvpp-8hx9-p66j GitPython: Unsafe git option guard bypass via split_single_char_options=False short-option token smuggling enables command execution

HIGH Published 2026-08-07 Modified 2026-09-08 Official page ↗
Aliases
CVE-2026-76220
Ecosystem
PyPI
Packages
GitPython
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Weaknesses
CWE-88
Reviewed
2026-08-07

Affected packages

• PyPI GitPython: < 3.1.58 → fixed in 3.1.58

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` argv token that git parses as `--upload-pack=`, 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 ;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 ;git-upload-pack` (cmd.py:1631). git clusters value-less short flags then parses `-u` = `--upload-pack=` → 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 ```python 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 1. Entry: app forwards user kwargs to `Repo.clone_from(url, path, kwargs)`: `{split_single_char_options: False, n: 'utouch /tmp/ACE;git-upload-pack'}`. 2. Check: `check_unsafe_options(_option_candidates([], kwargs), unsafe_git_clone_options)`. Guard: denylist includes `--upload-pack`/`-u`. Bypass proof: `_option_candidates` yields only `['-n']` (value token skipped because `split=False`); guard never sees `-u`. 3. Sink: `transform_kwarg` emits joined token (cmd.py:1631). argv (observed): `['git','clone','-v','-nutouch /tmp/ACE;git-upload-pack','--','','']`. 4. Impact: git clusters `-n` + `-u` → 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 ` 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

Related

Alias · CVE (1)

  • CVE-2026-76220 GitPython before 3.1.58 contains a command execution vulnerability in the check_unsafe_options guard that can be bypassed by combining a… vulnerability

Weakness · CWE (1)

  • CWE-88 Improper Neutralization of Argument Delimiters in a Command weakness

References