Sep 8, 2026·6 min read·37 visits
GitPython failed to block the `--separate-git-dir` parameter during cloning operations, allowing attackers to write arbitrary git repository directories and potentially execute arbitrary code.
GitPython prior to version 3.1.59 contains a path traversal vulnerability via parameter injection. The clone denylist did not restrict the `--separate-git-dir` option, allowing attackers to write repository metadata to arbitrary system paths.
GitPython is a Python library used to interact with Git repositories by acting as a process wrapper for the system's git command-line binary. This architecture requires strict input validation because raw command parameters passed to Git can modify execution behavior or perform file operations outside the intended working directory. To protect applications, GitPython implements parameter denylists to block dangerous options from being executed.
The vulnerability identified as CVE-2026-78677 resides in the library's cloning filters. Specifically, the library failed to block the dangerous --separate-git-dir option inside the unsafe_git_clone_options denylist. Because of this omission, applications that accept remote inputs or user-controlled configuration parameters and pass them to clone functions are susceptible to path traversal.
The vulnerability affects all versions of GitPython prior to version 3.1.59. If successfully exploited, attackers can write repository metadata to arbitrary locations on the local filesystem. This capability can lead to unauthorized directory creation, configuration manipulation, and potential arbitrary code execution.
The root cause of CVE-2026-78677 is the missing entry for the --separate-git-dir option inside GitPython's clone-specific safety filter. When invoking cloning operations via Repo.clone() or Repo.clone_from(), the library checks the user-provided arguments against a hardcoded list of unsafe parameters. If a parameter matches the denylist, an UnsafeOptionError is thrown, unless allow_unsafe_options is explicitly set to True.
Because the option --separate-git-dir was omitted from unsafe_git_clone_options, GitPython allowed this option to bypass the safety check. The command execution engine built the git shell command containing the unchecked parameter. Interestingly, the library already blocked this parameter in other contexts, such as unsafe_git_init_options for repository initialization, illustrating an inconsistency in the filter implementation.
The --separate-git-dir parameter instructs the git binary to store the repository's database, objects, refs, and configuration at a path separate from the actual working directory. Instead of creating a standard .git folder inside the clone target, git writes a plaintext .git file referencing the external path. This behavior allows attackers to write repository files to any directory where the executing process has write permissions.
The vulnerability is located in git/repo/base.py, where unsafe_git_clone_options is defined. In vulnerable versions, this array only contained -c, --template, and --bundle-uri. The commit b68afff45af0f49e79a3e2d2162018986b37ad5d resolved this issue by appending the --separate-git-dir option to the denylist.
Below is the diff showing the addition of the validation filter:
# git/repo/base.py
class Repo:
unsafe_git_clone_options = [
"-c",
# Can install hooks that execute during clone:
"--template",
+ # Redirects the repository metadata to a caller-controlled path:
+ "--separate-git-dir",
# Fetches from an additional caller-controlled URI:
"--bundle-uri",
]To support submodule initialization, which utilizes --separate-git-dir internally, the developers had to adjust git/objects/submodule/base.py. The update checks user-provided options against the denylist before programmatically injecting the required argument:
# git/objects/submodule/base.py
if cls._need_gitfile_submodules(repo.git):
+ if not allow_unsafe_options:
+ Git.check_unsafe_options(Git._option_candidates([], kwargs), repo.unsafe_git_clone_options)
+ multi_options = kwargs.get("multi_options")
+ if multi_options:
+ Git.check_unsafe_options(
+ shlex.split(" ".join(cast("Sequence[str]", multi_options))),
+ repo.unsafe_git_clone_options,
+ )
+ allow_unsafe_options = True
kwargs["separate_git_dir"] = module_abspathExploitation of CVE-2026-78677 requires a target application to expose parameters of Repo.clone() or Repo.clone_from() to user input. This input can be provided via API calls, config files, or custom command structures where user-controlled dictionaries are unpacked as keyword arguments. If the application passes these arguments directly to GitPython, the validation bypass can be achieved.
To execute the attack, the adversary provides the parameter separate_git_dir mapped to a target filesystem path, such as a web directory or a system configuration path. The git process executes and creates the metadata structure at the specified target directory. The primary payload is written to the remote repository, which is cloned during the process.
Once the repository metadata is placed in the designated path, the attacker can exploit downstream triggers. By placing specific configurations or git hooks inside the target directory, the attacker can force code execution. When the local application or system runs subsequent git commands on that repository, the malicious hooks are invoked automatically.
The impact of CVE-2026-78677 is evaluated as High, with a CVSS v3.1 base score of 7.5 and a CVSS v4.0 score of 8.7. The primary hazard is path traversal leading to arbitrary directory and file write operations on the local host. Because the execution is driven by git, the file creation inherits the permissions of the application process running the GitPython library.
In multi-tenant environments, such as continuous integration (CI) platforms, source code hosting services, or automated analysis pipelines, this vulnerability allows sandbox escape. By writing files outside the designated workspace, attackers can overwrite configuration files, replace source code, or pollute web assets. This capability directly leads to system compromise.
Furthermore, because git metadata directories contain executable hooks (such as post-checkout or pre-commit), directing the metadata to a known directory can result in arbitrary code execution. When the host environment runs subsequent commands that interact with the corrupted repository directory, the configured hooks run local scripts automatically under the context of the executing user.
The primary remediation strategy is upgrading GitPython to version 3.1.59 or later. This release adds --separate-git-dir to the global cloning denylist, effectively blocking unauthorized directory redirection. If upgrading is not immediately possible, organizations should sanitize all user-supplied inputs to filter out keys resembling separate_git_dir or separate-git-dir.
A critical review of the patch in git/objects/submodule/base.py reveals a potential parsing discrepancy. The validation of multi_options relies on joining list elements with spaces and then parsing them with shlex.split. If an attacker can inject custom quotes or spaces into the list elements, the behavior of shlex.split might deviate from the underlying shell interpreter.
To ensure complete protection, developers should avoid forwarding untrusted, raw keyword arguments or list configurations directly to GitPython's process wrappers. Implementing a strict schema validation layer on the application side provides a defense-in-depth barrier against parameter injection variants.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
GitPython gitpython-developers | < 3.1.59 | 3.1.59 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network |
| CVSS Score | 7.5 (v3.1) / 8.7 (v4.0) |
| EPSS Score | 0.0043 (0.43%) |
| Impact | Path Traversal, Remote Code Execution (RCE) |
| Exploit Status | poc |
| KEV Status | Not Listed |
The product uses external input to construct a pathname that should be within a restricted directory, but it does not properly neutralize elements within the pathname that can cause the pathname to resolve to a location outside of the restricted directory.
A local race condition and mutual exclusion bypass vulnerability exists in filelock versions 4.1.0 through 4.2.0. On Linux filesystems with rapid inode reuse, a local attacker can manipulate modification timestamps and lock files to cause waiting processes to falsely evict active soft lock markers.
vLLM prior to version 0.31.0 contains an uncontrolled resource consumption vulnerability (CWE-400) in its HTTP API request handling logic. Unauthenticated remote attackers can transmit per-request keyword arguments (`mm_processor_kwargs` and `media_io_kwargs`) that are passed directly into internal multimodal preprocessing pipelines, causing CPU/GPU memory exhaustion and crashing the engine process.
An insufficient verification of data authenticity vulnerability in SiYuan prior to version 3.7.4 allows an attacker to overwrite arbitrary installed marketplace packages (plugins, widgets, themes, and templates). By providing a mismatched package name and package archive repository URL during a Bazaar installation request, arbitrary file write into trusted plugin directories can be achieved, enabling persistence and execution across application restarts.
In obsidian-tc versions 1.0.1 through 1.31.3, memory lifecycle tools fail to validate note ownership prior to rematerializing Markdown files and execute non-atomic SQLite database mutations. An authorized caller can overwrite arbitrary note bodies on disk and cause persistent desynchronization between the database memory graph and physical vault files.
A high-severity authorization bypass vulnerability in obsidian-tc allows clients to bypass Human-In-The-Loop (HITL) confirmation prompts over Streamable HTTP deployments. Due to incomplete validation of returned prompt responses, an attacker echoing a signed requestState token can force execution of destructive vault operations even if a human user declines or cancels the prompt.
A Denial of Service (DoS) vulnerability in pypdf versions prior to 6.21.0 allows remote attackers to cause high CPU and memory resource exhaustion by supplying PDF files containing oversized or deeply nested arrays, dictionaries, or content streams.