GHSA-wvpp-8hx9-p66j
ADVISORY - githubSummary
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
Common Weakness Enumeration (CWE)
Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')
Sign in to Docker Scout
See which of your images are affected by this CVE and how to fix them by signing into Docker Scout.
Sign in