GitHub Advisoryadvisory · PyPI
GHSA-v396-v7q4-x2qj GitPython unsafe clone option gate bypass through joined short options
HIGH
Official page ↗
- Ecosystem
- PyPI
- Packages
- GitPython
- Severity
- High
- CVSS vector
- CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
- Weaknesses
- CWE-78
- Reviewed
- 2026-07-21
Affected packages
• PyPI GitPython: >= 3.1.50, < 3.1.51 → fixed in 3.1.51
Description
`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` as the short form of `--upload-pack=`. As a result, `Repo.clone_from(..., multi_options=["-u"], 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=` 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=` form is blocked by GitPython. It then calls `Repo.clone_from(..., multi_options=["-u"], allow_unsafe_options=False)`.
Observed sanitized output:
```text
gitpython_version=3.1.50
git_version=git version 2.53.0.windows.1
tmp_dir=
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` still bypasses because the validator canonicalizes it to `u` rather than `u`.
• `GHSA-rpm5-65cw-6hj4` describes unsafe underscored kwargs such as `upload_pack=...`. The current PoC uses `multi_options=["-u"]` 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` is treated as option name `u`, not blocked short option `u`.
• GitHub issue searches for `u 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=` / `--option ` forms.
Related
Weakness · CWE (1)
- CWE-78 Improper Neutralization of Special Elements used in an OS Command weakness