GHSA-rwj8-pgh3-r573: GitPython: Environment-variable exfiltration via os.path.expandvars() on Repo.clone_from() URL
Summary
Repo.clone_from() passes the caller-supplied remote URL through Git.polish_url(), which on every non-Cygwin platform calls os.path.expandvars() on the URL before handing it to git clone. An attacker who controls the URL argument — the documented use case for clone_from() in "import repository from URL" features of CI servers, git-hosting mirrors, and dependency scanners — can embed $NAME / ${NAME} tokens that are expanded server-side to the values of the hosting process's environment variables. The resulting URL, now containing the secret, is transmitted over the network to the attacker-named host. This crosses the trust boundary between an untrusted remote URL and the server's process environment, disclosing secrets such as AWS_SECRET_ACCESS_KEY or GITHUB_TOKEN with no precondition beyond the ability to submit a clone URL.
Details
Affected versions: gitpython (PyPI) — all releases up to and including 3.1.50 (latest at time of reporting); confirmed present on the main branch.
Git.polish_url() unconditionally applies environment-variable expansion to its input on the non-Cygwin branch:
git/cmd.py (v3.1.50), lines 907–925:
@classmethod
def polish_url(cls, url: str, is_cygwin: Union[None, bool] = None) -> PathLike:
"""Remove any backslashes from URLs to be written in config files.
...
"""
if is_cygwin is None:
is_cygwin = cls.is_cygwin()
if is_cygwin:
url = cygpath(url)
else:
url = os.path.expandvars(url) # <-- line 921
if url.startswith("~"):
url = os.path.expanduser(url)
url = url.replace("\\\\", "\\").replace("\\", "/")
return url
Repo._clone() — reached from the public Repo.clone_from() (git/repo/base.py:1520) and Repo.clone() — runs the unsafe-protocol check on the raw URL and then passes the polished (post-expansion) URL to the git clone subprocess:
git/repo/base.py (v3.1.50), lines 1407–1418:
if not allow_unsafe_protocols:
Git.check_unsafe_protocols(url)
if not allow_unsafe_options:
Git.check_unsafe_options(options=list(kwargs.keys()), unsafe_opt
Details
Original advisory: https://github.com/advisories/GHSA-rwj8-pgh3-r573
Recent advisories for GHSA-rwj8-pgh3-r573
A cluster of recent advisories against the same product widens the attack surface — attackers routinely chain freshly published CVEs on one product, so review these together.
More from GitHub Security Advisories
- mediumGHSA-jr6p-8pjj-mfx6: Capsule has an incomplete fix of CVE-2026-22872: TenantResource RawItems and Generators s…2026-07-31
- mediumGHSA-68cj-mvg9-rgm2: Capsule: CapsuleConfiguration NodeMetadata regex fields lack webhook validation, allowing…2026-07-31
- mediumGHSA-ff84-5f28-78qj: re2: Out-of-bounds heap read in `exec`/`test`/`match` via attacker-influenced `lastIndex`…2026-07-31
- mediumGHSA-6hxr-mr5r-9836: re2: Global `String.prototype.match` with an empty-matchable pattern never advances → inf…2026-07-31
- mediumGHSA-x83g-979r-f5fh: Sylius Mollie Plugin has unauthenticated IDOR that leaks order token and customer PII2026-07-31