Aug 4, 2026·5 min read·38 visits
GitPython versions prior to 3.1.57 are vulnerable to argument injection, enabling arbitrary file overwrite via IndexFile.checkout() and arbitrary file read via TagReference.create() because keyword arguments are forwarded unsanitized to the underlying git binary.
An argument injection vulnerability in GitPython allows remote or local attackers to execute arbitrary file reads or arbitrary file overwrites via unsafe command option forwarding. This occurs because the wrapper methods `IndexFile.checkout()` and `TagReference.create()` fail to validate parameters before passing them to system-level git invocations.
GitPython is a Python library used to interact with Git repositories by abstracting command-line operations into Pythonic APIs. The library provides high-level classes such as IndexFile and TagReference to manage git indexes and reference tags respectively. When these classes perform actions, they serialize programmatic options and dispatch execution to the local system's git binary.
The attack surface exists within any API wrapper that passes developer-controlled keyword arguments directly to the underlying shell command execution. By default, GitPython features an opt-in parameter validation system called Git.check_unsafe_options(). However, because this verification is not enforced globally at the execution dispatcher layer, any API wrapper that omits the validation remains completely unguarded.
Two critical wrapper methods are vulnerable to this design pattern. The IndexFile.checkout() method forwards arguments to git checkout-index, which accepts the --prefix option to redirect file creation outside the workspace. The TagReference.create() method forwards arguments to git tag, which accepts the -F or --file options to read local system files and return them in-band. This leads to arbitrary file write (integrity and availability compromise) and arbitrary file read (confidentiality compromise).
The root cause of this vulnerability lies in improper validation of command-line arguments, classified under CWE-88 (Argument Injection) and CWE-20 (Improper Input Validation). GitPython relies on individual, opt-in implementations of Git.check_unsafe_options() inside wrapper functions to maintain security. If a wrapper accepts **kwargs and passes them directly to the git command execution without sanitization, an attacker who controls the arguments can inject system flags.
In IndexFile.checkout(), the python method forwards arguments to git checkout-index. The --prefix=<path> command-line option specifies a prefix directory to prepend to checked-out paths. Because there is no check preventing the use of --prefix, an attacker can specify absolute directories or directory traversal sequences. This allows the application to write files from the repository to arbitrary paths on the local filesystem, overwriting existing files if run with appropriate permissions.
In TagReference.create(), the method maps to the git tag command. The command-line utility supports the -F <file> and --file=<file> options to import a tag message from a target file. Because GitPython does not filter these options, an attacker can specify a sensitive system file as the source. GitPython subsequently reads this file, associates it with the created tag object, and exposes its raw contents when the application accesses the TagReference.tag.message property.
Before the patch, git/index/base.py contained the following unsafe forwarding mechanism:
def checkout(self, paths=None, force=False, fprogress=lambda *args: None, **kwargs):
# Unsanitized kwargs are passed directly to checkout_index
proc = self.repo.git.checkout_index(*args, **kwargs)Because there is no invocation of Git.check_unsafe_options() or defined blocked option list, any keys in kwargs are converted directly into command-line arguments. For example, passing prefix='/tmp/' evaluates to --prefix=/tmp/ in the shell execution.
Similarly, git/refs/tag.py contained the following definition:
def create(cls, repo, path, reference="HEAD", logmsg=None, force=False, **kwargs):
# Forwarded directly to the underlying git wrapper without verification
# kwargs can contain 'F' or 'file' keysTo address this, the patch introduces class-level variables specifying banned options and updates the method signatures to accept allow_unsafe_options: bool = False. Inside the methods, an explicit check validates the passed arguments against the blocklist unless allow_unsafe_options is set to True:
# Inside IndexFile
unsafe_git_checkout_index_options = ["--prefix"]
# Validation check added to checkout()
if not allow_unsafe_options:
Git.check_unsafe_options(
options=Git._option_candidates([], kwargs),
unsafe_options=self.unsafe_git_checkout_index_options,
)Exploitation requires the attacker to be able to supply or influence the keyword arguments (**kwargs) passed to either of the vulnerable methods. This scenario typically occurs in web applications, automated CI/CD pipelines, or repository management dashboards that accept custom execution configurations from users.
To perform an arbitrary file overwrite, an attacker invokes index.checkout() with a custom prefix argument pointing to a system target. The following proof-of-concept demonstrates how the exploit redirects repository files to write directly into /tmp/target_dir/ instead of the local repository workspace:
from git import Repo
import os
repo = Repo("/path/to/repo")
# Executes 'git checkout-index -a -f --prefix=/tmp/target_dir/'
repo.index.checkout(prefix="/tmp/target_dir/", a=True, f=True)To perform an arbitrary file read, the attacker targets TagReference.create() by specifying the -F parameter with the target file path. When GitPython spawns the process, it reads the target file and records its content as the tag message. The application can then read the sensitive file content in-band:
from git import Repo
from git.refs.tag import TagReference
repo = Repo("/path/to/repo")
# Executes 'git tag -a -f -F /etc/passwd leak_tag'
tag_ref = TagReference.create(repo, "leak_tag", force=True, a=True, F="/etc/passwd")
leaked_data = tag_ref.tag.message
print(leaked_data)The impact of these two primitives is high. The arbitrary file overwrite primitive enables attackers to compromise system integrity. If the application runs with root privileges or within a user directory, an attacker can overwrite critical files such as .bashrc, .ssh/authorized_keys, or configuration files, potentially escalating privileges or achieving remote code execution.
The arbitrary file read primitive directly breaks confidentiality. In automated environments, this allows attackers to leak configuration properties, local environment variables, system user information (such as /etc/passwd), or application source code. The data is returned directly to the calling context, which may output it to web interfaces or log collectors.
Because the underlying vulnerability pattern (opt-in security checks) relies on manual application on a per-wrapper basis, additional undocumented wrappers may still expose similar argument injection vectors. The CVSS score of 8.1 reflects high integrity and availability impact, though configuration-dependent confidentiality impacts are also severe.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
gitpython gitpython-developers | < 3.1.57 | 3.1.57 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-88 (Argument Injection), CWE-20 (Improper Input Validation) |
| Attack Vector | Network |
| CVSS Score | 8.1 |
| Impact | High (Arbitrary File Overwrite / Arbitrary File Read) |
| Exploit Status | Proof-of-Concept Available |
| KEV Status | Not Listed |
The application constructs an OS command using keyword arguments but does not neutralize or validate elements that can modify the command's actions, leading to argument injection.
A high-severity type-confusion vulnerability exists in NearForm fast-jwt prior to version 6.3.0. The vulnerability allows attackers to bypass crucial claim validation steps (such as expiration, issuer, audience, and subject validations) by presenting a validly signed JSON Web Token structured as a JSON array instead of a JSON object. This occurs because the library's decoder fails to reject JSON arrays during type evaluation.
NearForm fast-jwt prior to version 6.3.0 is vulnerable to an input validation flaw where configuring verifier properties (such as clockTolerance, clockTimestamp, and cacheTTL) with non-finite values like Infinity or NaN allows attackers to bypass temporal claim validations, including expiration (exp) and activation (nbf) boundaries. This validation bypass can result in unauthorized session persistence and cache poisoning.
A critical cryptographic vulnerability in fast-jwt versions 6.2.x prior to 6.3.0 allows unauthenticated remote attackers to execute an asymmetric-to-symmetric algorithm confusion attack due to incomplete validation of leading non-whitespace prefixes.
CVE-2026-107720 is a critical signature verification bypass vulnerability in NearForm's fast-jwt Node.js library. Under specific configurations where the token verifier is initialized with a falsy cryptographic key (such as an empty string or null) and a non-empty algorithms allowlist, the library erroneously skips signature validation. This allows unauthenticated remote attackers to submit fabricated, unsigned JSON Web Tokens and bypass the authorization boundary of the application entirely.
Ruby Mechanize prior to version 2.14.1 contains an information disclosure vulnerability. When executing cross-origin HTTP redirects, global headers configured on the Mechanize agent (such as Authorization or Session Cookies) are dynamically re-applied to the subsequent request, bypassing the internal header-stripping logic. An attacker who controls a redirection endpoint can capture sensitive bearer tokens or cookies.
An origin trust boundary failure in the Ruby mechanize library (prior to v2.14.1) allows unauthenticated remote web servers to harvest sensitive global request headers, such as Authorization Bearer tokens and cookies, by utilizing HTML-level meta-refresh redirection tags. Standard HTTP-level redirect boundaries were not applied to document-level redirects, creating a vector for cross-origin credential leakage during automated crawls.