{"id":"CVE-2026-73621","aliases":["GHSA-p538-c434-8v24","PYSEC-2026-3950"],"title":"GitPython: Arbitrary file truncation via git rev-list --output argument injection in unguarded Commit.count","summary":"GitPython: Arbitrary file truncation via git rev-list --output argument injection in unguarded Commit.count","severity":"medium","cvss":5.4,"cvssVector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L","vendor":"gitpython","product":"gitpython","ecosystem":"pip","affected":["gitpython < 3.1.56"],"patched":["gitpython 3.1.56"],"published":"2026-08-03","updated":"2026-09-10","sourceUpdated":"2026-09-10T13:10:55.177009469Z","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-p538-c434-8v24","references":[{"url":"https://github.com/gitpython-developers/GitPython/security/advisories/GHSA-p538-c434-8v24"},{"url":"https://github.com/gitpython-developers/GitPython/pull/2184"},{"url":"https://github.com/gitpython-developers/GitPython/commit/38553b6fddc7f6a667cdb45a6762343a08fc72b2"},{"url":"https://github.com/gitpython-developers/GitPython"},{"url":"https://github.com/gitpython-developers/GitPython/releases/tag/3.1.56"},{"url":"https://www.vulncheck.com/advisories/gitpython-before-arbitrary-file-truncation-via-commit-count"}],"tags":["osv","pip"],"epss":0.00211,"epssPercentile":0.11631,"ingestedAt":"2026-08-14T19:18:45.627Z","slug":"CVE-2026-73621","body":"## Overview\n\n## Summary\n`Commit.count()` forwards `**kwargs` into `rev_list` with **no** `check_unsafe_options` guard (the guard exists only in the sibling `iter_items`, commit.py:341). `git rev-list --output=<path>` opens and truncates the target file to 0 bytes before revision parsing, so `count(output='/victim')` destroys/blanks an arbitrary file.\n\n## Root Cause\n`commit.py:290-291` calls `self.repo.git.rev_list(self.hexsha, **kwargs)` with no `check_unsafe_options` and no `allow_unsafe_options` parameter. The sibling `iter_items` (commit.py:341) is guarded; `count` is not. This is a distinct, uncovered sink — GHSA-956x-8gvw-wg5v fixed `iter_commits`/`blame`, not `count`.\n\n## Impact\nDestroy/blank an arbitrary file at process privilege (integrity/availability). Reachability is key-control only (`count` uses `self.hexsha`, not a user ref), and the write is a 0-byte truncation (no content control), so MEDIUM.\n\n## Proof of Concept\n```python\ncommit.count(output='/path/to/victim')   # victim truncated to 0 bytes (verified)\n# control: commit.iter_commits(output=...) raises UnsafeOptionError\n```\n\n## Attack Chain\n1. Entry: app forwards user options -> `commit.count(output='/victim')`. Guard: none. Bypass proof: `iter_commits(output=)` raises UnsafeOptionError; `count(output=)` does not — verified side-by-side.\n2. Sink: `git rev-list <sha> --output=/victim` -> file truncated to 0 bytes. Impact: destroy/blank arbitrary file.\n\n## Bypass Evidence\nLive-verified on HEAD (tag 3.1.53): `count(output=<victim>)` truncated a pre-existing file to 0 bytes; guarded `iter_commits(output=)` raised UnsafeOptionError. Same CNA-accepted \"app forwards user options dict\" model as GHSA-956x-8gvw-wg5v's `archive(**kwargs)`. Uncovered sink, not a duplicate.\n\n## Affected Versions\n`<= 3.1.53`\n\n## Suggested Fix\nAdd `check_unsafe_options` to `Commit.count` (mirroring `iter_items`).\n\n---\nReported by **zx (Jace)** — GitHub: @manus-use\n\n## Affected packages\n\n- `gitpython < 3.1.56`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `gitpython 3.1.56`","depth":"sunlit","depthScore":30,"depthScoreParts":{"impact":29.7,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}