---
schema_version: '1.0'
id: security-20260725-aa9760
url: https://osv.dev/vulnerability/GHSA-fjr4-x663-mwxc
url_hash: aa9760f3b89521c15f04fd31bd46dfad646fe8ba558253aaf23b42ac935e75c2
canonical_url: https://osv.dev/vulnerability/GHSA-fjr4-x663-mwxc
source: osv:ghsa
category: security/library
category_raw: cve/library
region: null
tags:
- cve
- GHSA-fjr4-x663-mwxc
- severity:CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
- gitpython
- PyPI
lang: en
published_at: '2026-07-24T16:41:20Z'
fetched_at: '2026-07-25T07:04:02.189716Z'
updated_at: '2026-07-25T07:04:18Z'
status: published
content_hash: 56097b07c1c44e2afce42771db5312a057cd4249b5c5c2a764c7b134e84d82eb
content_changed_at: null
license_note: full
summary: 'GitPython: Arbitrary file overwrite via git diff --output argument injection
  in Diffable.diff (key- and value-controlled)'
summary_source: rss
summary_en: 'GitPython: Arbitrary file overwrite via git diff --output argument injection
  in Diffable.diff (key- and value-controlled)'
entities:
- name: GitPython
  type: artifact
- name: Latvia
  type: organization
key_facts: []
related: []
related_auto:
- name: Belarus
  type: UNKNOWN
  weight: 1.0
- name: Lithuania
  type: location
  weight: 1.0
- name: Russia
  type: UNKNOWN
  weight: 1.0
title: 'GHSA-fjr4-x663-mwxc: GitPython: Arbitrary file overwrite via git diff --output
  argument injection in Diffable.diff (key- and value-controlled)'
---

# GHSA-fjr4-x663-mwxc: GitPython: Arbitrary file overwrite via git diff --output argument injection in Diffable.diff (key- and value-controlled)

## TL;DR
GitPython: Arbitrary file overwrite via git diff --output argument injection in Diffable.diff (key- and value-controlled)

## Key Points
- cve / GHSA-fjr4-x663-mwxc / severity:CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H / gitpython / PyPI

## Details
**Severity:** CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:H
**Advisory:** GHSA-fjr4-x663-mwxc

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

**Details:**
## Summary
`Diffable.diff()` forwards `**kwargs` straight into `diff`/`diff_tree` with **no** `check_unsafe_options` guard. `Diffable` is mixed into `Commit`, `Tree`, `IndexFile`, and `Submodule`, 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-283` builds and runs the diff command with no `check_unsafe_options` anywhere in the method (grep-confirmed). Additionally `diff.py:265` does `args.insert(0, other)`, placing the caller-supplied `other` ref BEFORE the `--` separator, so a value of `--output=/path` is 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
```python
# Key-control:
commit.diff(other_commit, output='/home/app/.ssh/authorized_keys')   # victim overwritten with diff (105 bytes verified)
# Value-control (attacker controls only the ref string):
commit.diff(other='--output=/home/app/.ssh/authorized_keys')          # 14-byte victim -> 146 bytes of diff-tree output
```

## Attack Chain
1. Entry (value-control): `commit.diff(other=<user ref>)` with `other = "--output=/home/app/.ssh/authorized_keys"`. Guard: none in `Diffable.diff`. Bypass proof: no `check_unsafe_options` in the method body (grep); `other` inserted pre-`--` at diff.py:265.
2. Sink: `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 for `iter_commits(rev='--output=')` — but `diff` is a distinct, unguarded sink NOT touched by that fix.

## Affected Versions
`<= 3.1.53`

## Suggested Fix
Add `check_unsafe_options` to `Diffable.diff` (mirroring `iter_commits`/`archive`), and/or place `--end-of-options` before the `other` ref so it cannot be parsed as an option.

---
Reported by **zx (Jace)** — GitHub: @manus-use

**References:**
- https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-fjr4-x663-mwxc
- https://github.com/gitpython-developers/GitPython/pull/2180
- https://github.com/gitpython-developers/GitPython/commit/1d51b891d7f236044a6aa17498ec682b63dad6e6
- https://github.com/gitpython-developers/GitPython
- https://github.com/gitpython-developers/GitPython/releases/tag/3.1.54

_Data: OSV.dev (upstream: ghsa) — https://osv.dev/vulnerability/GHSA-fjr4-x663-mwxc_

## Source
元記事: [GHSA-fjr4-x663-mwxc: GitPython: Arbitrary file overwrite via git diff --output argument injection in Diffable.diff (key- and value-controlled)](https://osv.dev/vulnerability/GHSA-fjr4-x663-mwxc) — published 2026-07-24T16:41:20Z
