Vulnerability Database

374,975

Total vulnerabilities in the database

GitPython: Unsafe git option guard bypass via split_single_char_options=False short-option token smuggling enables command execution — GitPython

Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')

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

  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','--','<src>','<dst>'].
  4. 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

  • Published: Aug 7, 2026
  • Updated: Aug 8, 2026
  • GHSA: GHSA-wvpp-8hx9-p66j
  • Severity: High
  • Exploit:
  • CISA KEV:

CVSS v3:

  • Severity: High
  • Score: 8.8
  • AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

CWEs:

OWASP TOP 10:

Frequently Asked Questions

A security vulnerability is a weakness in software, hardware, or configuration that can be exploited to compromise confidentiality, integrity, or availability. Many vulnerabilities are tracked as CVEs (Common Vulnerabilities and Exposures), which provide a standardized identifier so teams can coordinate patching, mitigation, and risk assessment across tools and vendors.

CVSS (Common Vulnerability Scoring System) estimates technical severity, but it doesn't automatically equal business risk. Prioritize using context like internet exposure, affected asset criticality, known exploitation (proof-of-concept or in-the-wild), and whether compensating controls exist. A "Medium" CVSS on an exposed, production system can be more urgent than a "Critical" on an isolated, non-production host.

A vulnerability is the underlying weakness. An exploit is the method or code used to take advantage of it. A zero-day is a vulnerability that is unknown to the vendor or has no publicly available fix when attackers begin using it. In practice, risk increases sharply when exploitation becomes reliable or widespread.

Recurring findings usually come from incomplete Asset Discovery, inconsistent patch management, inherited images, and configuration drift. In modern environments, you also need to watch the software supply chain: dependencies, containers, build pipelines, and third-party services can reintroduce the same weakness even after you patch a single host. Unknown or unmanaged assets (often called Shadow IT) are a common reason the same issues resurface.

Use a simple, repeatable triage model: focus first on externally exposed assets, high-value systems (identity, VPN, email, production), vulnerabilities with known exploits, and issues that enable remote code execution or privilege escalation. Then enforce patch SLAs and track progress using consistent metrics so remediation is steady, not reactive.

SynScan combines attack surface monitoring and continuous security auditing to keep your inventory current, flag high-impact vulnerabilities early, and help you turn raw findings into a practical remediation plan.