Jul 21, 2026·5 min read·59 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-115895 is a high-severity code injection vulnerability (CWE-94) in the happy-dom npm package prior to version 21.0.4. The vulnerability exists within the ECMAScriptModuleCompiler component, which transforms ECMAScript module syntax into executable Node.js functions when JavaScript evaluation is enabled. Due to an incomplete fix for CVE-2026-33943, the compiler failed to validate local named import aliases against legal JavaScript identifier rules. Attackers supplying crafted module imports—specifically utilizing destructuring default initializers alongside JSON module attributes—can inject arbitrary JavaScript expressions directly into the host Node.js runtime context.
An improper input validation and authorization bypass vulnerability exists in the LangGraph storage layer affecting langgraph, langgraph-checkpoint-postgres, and langgraph-checkpoint-sqlite. Because relational store implementations serialize namespace tuples into dot-delimited strings without validating labels on read, search, delete, and batch operations, an attacker who controls a namespace label can inject periods (.) to traverse namespace boundaries and access, modify, or delete data belonging to other namespaces.
CVE-2026-115886 (GHSA-2wqp-q3c6-ccj5) is a Server-Side Request Forgery (SSRF) and Unsafe Reflection vulnerability in MLflow versions 3.12.0rc0 prior to 3.17.0. Unauthenticated REST API endpoints accept serialized third-party scorer JSON payloads that are dynamically deserialized and reflectively instantiated without sanitizing constructor keyword arguments. When optional dependencies such as trulens and trulens-providers-litellm are installed, attackers can inject egress-steering arguments into the scorer payload, forcing the MLflow server to dispatch outbound HTTP requests to arbitrary internal destinations or cloud metadata services.
CVE-2026-113872 (GHSA-q7qp-rjg7-q26m) is a high-severity denial-of-service vulnerability affecting the multer multipart/form-data upload middleware for Node.js and Express in versions 2.0.0 through 2.4.0. When an endpoint is configured to accept multiple files using an asynchronous fileFilter callback, a crafted, truncated multipart HTTP request can trigger an unhandled error event on the underlying busboy stream parser after request teardown has already completed. Because all error listeners are stripped during teardown, Node.js treats the resulting error event as an uncaught exception and immediately terminates the server process.
CVE-2026-115897 (also tracked as GHSA-m5mm-p8h7-m29v and aliased as CVE-2026-115754) is a Cross-Site Scripting (XSS) vulnerability in the Yahoo serialize-javascript npm package prior to version 7.1.3. The flaw exists in the regular expression used by escapeFunctionBody() to neutralize HTML script closing tags within serialized JavaScript functions. Because function bodies are sanitized in isolation before structural formatting is applied, a function whose string representation terminates directly with the sequence '</script' bypasses the positive lookahead check. When combined with serialization formatting options that introduce whitespace delimiters and a subsequent property that supplies a closing bracket, an attacker can prematurely terminate an enclosing HTML <script> block and inject arbitrary HTML or JavaScript into the rendered document.
CVE-2026-109841 is a Subresource Integrity (SRI) verification bypass vulnerability in undici, the official HTTP/1.1 and HTTP/2 client library for Node.js that powers the global fetch() implementation. The flaw stems from improper handling of case sensitivity (CWE-178) when validating cryptographic hash algorithm tokens within SRI metadata. Because undici compared incoming algorithm identifiers against a strictly lowercase set without prior ASCII-lowercase normalization, valid uppercase or mixed-case tokens such as SHA256- or Sha384- were treated as unrecognized algorithms and discarded. Under SRI forward-compatibility rules, discarding all provided tokens results in an empty integrity list, causing undici to fail open and accept tampered HTTP response bodies without cryptographic verification.