GHSA-v396-v7q4-x2qj
ADVISORY - githubSummary
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_optionscorrectly lists--upload-pack,-u,--config, and-cas unsafe clone options.Repo._clone()splitsmulti_optionswithshlex.split(" ".join(multi_options))and then callsGit.check_unsafe_options(...)._canonicalize_option_name("-u/path/to/helper")returns a string beginning withu..., not the canonical short optionu, so it does not match the blocked-uentry.- 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:
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/GitPythonversion3.1.50returned no vulnerabilities. - The repository's public advisories include related unsafe Git option issues, including
GHSA-x2qx-6953-8485/CVE-2026-42284andGHSA-rpm5-65cw-6hj4/CVE-2026-42215. Their public affected ranges are marked as fixed before 3.1.50. GHSA-x2qx-6953-8485describes validatingmulti_optionsbeforeshlex.split(...). GitPython 3.1.50 now validates after splitting, but the joined short option-u<value>still bypasses because the validator canonicalizes it tou<value>rather thanu.GHSA-rpm5-65cw-6hj4describes unsafe underscored kwargs such asupload_pack=.... The current PoC usesmulti_options=["-u<helper>"]against 3.1.50 and does not depend on underscored kwargs.- GitHub issue search for
upload-pack unsafe optionsfound 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 unsafefound PR #2130, which fixed splitting ofmulti_optionsbefore checking. The current issue remains after that split because-u<value>is treated as option nameu<value>, not blocked short optionu. - GitHub issue searches for
u<upload-pack> unsafeand-cfooreturned 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.
Common Weakness Enumeration (CWE)
Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
GitHub
-