Jul 21, 2026·5 min read·4 visits
GitPython fails to validate abbreviated long options and clustered short options, allowing attackers to bypass the unsafe option filters and execute arbitrary shell commands.
GitPython prior to version 3.1.51 contains a command injection vulnerability due to flaws in its option validation routine. By exploiting Git's native argument parser behavior, specifically long-option abbreviation resolution and short-option clustering, attackers can bypass the check_unsafe_options blocklist to execute arbitrary OS commands.
GitPython is a Python library used to interact with Git repositories. Applications rely on its API to clone repositories, fetch commits, and manage git configurations. The library includes a defensive security component named check_unsafe_options designed to prevent remote command execution by validating command line options passed to git processes.
This security boundary is designed to block dangerous parameters such as --upload-pack and -u which can run arbitrary code on the underlying host. However, a major architectural limitation in how GitPython validates these parameters allows attackers to construct payloads that evade validation while resolving into executable payloads inside Git's native CLI wrapper.
The vulnerability is classified as CWE-78 (OS Command Injection) and CWE-184 (Incomplete Blacklist / Blocklist). It affects all deployments of GitPython prior to version 3.1.51 where untrusted inputs are passed to repository subcommand parameters.
The root cause of this vulnerability lies in the logical mismatch between GitPython's exact-match validation logic and Git's native command-line option parser implemented in parse-options.c.
First, Git permits long-option prefix abbreviations. If an option starting with a specific prefix is unique among all valid option names for a given subcommand, Git automatically resolves that prefix to the full option name. For example, Git automatically interprets --upload-p as --upload-pack because there are no other options starting with --upload-p for that command.
Second, Git natively supports short-option clustering and direct value attachment. This feature allows multiple single-dash flags to be combined in a single block (e.g., -fq instead of -f -q) and allows parameters to be directly attached to their target value without spaces or equal signs (e.g., -u/bin/sh).
GitPython's original check_unsafe_options implementation validated options by performing exact-match lookups against a static blocklist of dangerous strings. Because the blocklist only checked for literal strings like --upload-pack or -u, it was completely bypassed by options leveraging abbreviation (like --upload-p) or value attachment (like -utouch). When these bypassed arguments reached the Git process, the Git engine successfully expanded and executed them.
The original verification logic in GitPython parsed argument inputs without evaluating character boundaries or abbreviation paths.
# Vulnerable implementation in git/cmd.py (Simplified)
canonical_unsafe_options = {cls._canonicalize_option_name(option): option for option in unsafe_options}
for option in options:
candidate = cls._canonicalize_option_name(option)
# Exact-match validation failed to detect abbreviations or joined option strings
unsafe_option = canonical_unsafe_options.get(candidate)
if unsafe_option is not None:
raise UnsafeOptionError(f"{unsafe_option} is not allowed...")In the patched version (3.1.51), the developer added character-by-character analysis of short-option tokens and checked for long-option abbreviation paths:
# Patched implementation in git/cmd.py
canonical_unsafe_options = {cls._canonicalize_option_name(option): option for option in unsafe_options}
# Extract unsafe single-character options (e.g., 'u', 'c')
unsafe_short_options = {
canonical: option
for canonical, option in canonical_unsafe_options.items()
if option.startswith("-") and not option.startswith("--") and len(canonical) == 1
}
clusterable_short_options = frozenset("46flnqsv")
for option in options:
candidate = cls._canonicalize_option_name(option)
# Manual scanning of short-option characters and clustered values
option_token = option.split("=", 1)[0].split(None, 1)[0]
if option_token.startswith("-") and not option_token.startswith("--"):
for option_char in option_token[1:]:
unsafe_option = unsafe_short_options.get(option_char)
if unsafe_option is not None:
raise UnsafeOptionError(
f"{unsafe_option} is not allowed, use `allow_unsafe_options=True` to allow it."
)
# Terminate scan early if a non-clusterable option is hit
if option_char not in clusterable_short_options:
breakAn attacker can exploit this vulnerability by submitting repository action parameters that leverage abbreviations to subcommands handled by GitPython.
# Exploitation using prefix abbreviations on vulnerable versions (< 3.1.51)
import git
repo = git.Repo("/tmp/repo")
# By using 'upload_p', the exact match validation is bypassed
repo.git.fetch("origin", upload_p="touch /tmp/rce_poc")Furthermore, critical evaluation of the patch reveals a secondary bypass vulnerability (CWE-184). The hardcoded set of safely clusterable flags clusterable_short_options = frozenset("46flnqsv") is incomplete.
Several subcommands contain valueless boolean flags not declared in this set. For instance, in git fetch, -p (prune) and -k (keep) are valueless boolean flags. An attacker can construct a payload such as -pu/path/to/payload. Because the parser encounters p (which is not in clusterable_short_options), it breaks execution scanning early, bypassing the checks while Git translates -pu as a combination of -p and -u, invoking the command.
Successful exploitation of this vulnerability results in arbitrary shell command execution with the privileges of the running Python application. In standard web applications running background workers or ingestion services, this allows attackers to execute commands directly on internal servers.
An attacker could retrieve environment variables containing API tokens, read sensitive files, modify repository branches, or move laterally into the internal network. The estimated CVSS score of 8.8 reflects the severity of unauthenticated remote execution contexts where user inputs are automatically processed.
Organizations running pipelines, automation platforms, or platform-as-a-service providers that accept user-provided Git repositories or parameters are at high risk.
The direct remediation path is upgrading GitPython dependencies to version 3.1.51 or higher, which introduces advanced scanning and token validation logic to catch option abbreviations and clustered parameters.
If patching is not immediately viable, developers must implement strict validation on all options passed to repository commands. Avoid allowing users to pass arbitrary argument variables or keyword arguments directly to low-level wrappers.
Sanitize and validate arguments using structured whitelists, allowing only expected values (such as branch names and remote URLs) and rejecting any inputs containing dashes or special symbols.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
GitPython gitpython-developers | < 3.1.51 | 3.1.51 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-78, CWE-184 |
| Attack Vector | Network (Remote) |
| CVSS v3.1 Score | 8.8 |
| Exploit Status | poc |
| KEV Status | Not Listed |
The application fails to correctly neutralize or validate inputs that are subsequently passed as command options to an OS shell execution command, allowing for option injection bypasses.
CVE-2026-58426 is a critical security vulnerability in Gitea Actions where improper signature serialization allows an authenticated attacker to execute a canonicalization (boundary-shifting) attack. By rewriting query parameters while keeping the signature intact, the attacker can bypass access control checks to read private workflow artifacts or modify concurrent task upload states.
CVE-2026-59879 describes a critical integer overflow vulnerability in the Immutable.js library when handling indices near 32-bit boundaries. An attacker can leverage this flaw to cause a denial of service via CPU thread lockup or process crashes, as well as data corruption through silent size truncation.
A critical security bypass and algorithm downgrade vulnerability in the PostgreSQL JDBC Driver (pgjdbc) allows Man-in-the-Middle (MITM) attackers to silently bypass channel binding requirements. When configured with `channelBinding=require`, the driver fails to assert that the negotiated SCRAM mechanism utilizes channel binding, allowing downgrade to plain SCRAM-SHA-256 when encountering unsupported server certificate signature algorithms (such as Ed25519 or Ed448).
CVE-2026-56170 is a high-severity Remote Denial of Service (DoS) vulnerability in Microsoft's ASP.NET Core framework. The vulnerability spans three separate resource-management vectors within the ASP.NET Core ecosystem, including SignalR Stateful Reconnect allocations, JSON Patch Type Confusion leading to stack exhaustion, and Kestrel HTTP/2 synchronization issues. An unauthenticated remote attacker can exploit these issues to cause process-terminating exceptions, rendering applications unavailable.
The Loofah Ruby gem version 2.25.0 and 2.25.1 contains an incomplete validation vulnerability in its public string-level utility Loofah::HTML5::Scrub.allowed_uri?. This helper fails to detect 'javascript:' URIs that are split by HTML5 named whitespace character references such as 	 and 
. Applications manually invoking this utility to validate links are vulnerable to stored Cross-Site Scripting (XSS), as browsers parse and remove these entities during rendering.
CVE-2026-50525 is a high-severity Denial of Service (DoS) vulnerability in the Microsoft .NET XML Cryptography stack. The vulnerability resides in the `System.Security.Cryptography.Xml` library, specifically within the `EncryptedXml` processing engine. Unauthenticated remote attackers can exploit this flaw by sending specifically crafted XML documents containing nested or recursive structures, or utilizing resource-intensive transforms. Processing such payloads leads to infinite CPU loops, stack exhaustion, or memory starvation, resulting in application termination.