GHSA-5xxx-qhh7-9287: Infoleak
Summary Repo.blame() / Repo.blameincremental() guard forwarded revision options against unsafegitrevisionoptions, but that denylist only contains the file-WRITE options --output/-o. git blame also honors --contents <file> and -S <file>, which cause the file's lines to be echoed into the blame result — an arbitrary file READ. Neither option is in the denylist, so a caller-influenced revision value of --contents=<path> passes the guard and leaks file contents. This is a distinct sink-option and impact class (READ) from GHSA-956x-8gvw-wg5v (which addressed the blame --output WRITE), directly analogous to GHSA-539m-9xh6-q6rr (archive READ gap accepted separately from the archive write/exec advisory).
Root Cause unsafegitrevisionoptions = ["--output","-o"] (git/repo/base.py:188). The rev string is passed to optioncandidates([rev], kwargs) and placed BEFORE the -- separator (base.py:841). The canonical name of --contents=... is contents, which is not on the denylist, so no UnsafeOptionError is raised. The trailing -- protects only the pathspec, not the option before the revision.
Impact Arbitrary local file read at the privileges of the host process; the file's line contents appear in the blame result returned to the caller. Pure VALUE control (the caller forwards a user-influenced revision string). Default allowunsafeoptions=False.
Proof of Concept python result = repo.blame("--contents=/etc/passwd", "a.txt") result rows carry the victim file's line text
Attack Chain 1. Entry: app calls repo.blame(rev, file) with attacker rev="--contents=/etc/passwd" (or kwarg contents="/etc/passwd", or -S). 2. Check: Git.checkunsafeoptions(optioncandidates([rev,...], kwargs), unsafegitrevisionoptions) @ base.py:841. Guard: denylist = ["--output","-o"] only. Bypass proof: canonical name contents ∉ denylist → no error. 3. Sink: self.git.blame(rev, "--", file, p=True, ...). argv (observed): ['git','blame','-p','--contents=<secret>','HEAD','--','a.txt']. 4. Impact: blame result rows carry the victim file's line text.
Bypass Evidence Independently reproduced (independent test harness, default allowunsafeoptions=False): blame('--contents=<secret>','a.txt') → guard PASSED; result rows = ['GATESECRETLINEA','GATESECRETLINEB']. Control: blame('--output=…') still BLOCKED (guard active on this path). -S kwarg argv also reaches git unguarded.
Affected Versions GitPython <= 3.1.58 (denylist present verbatim on the latest release tag).
Suggested Fix Prefer an allowlist of blame options; at minimum add --contents/-S (and any other path-taking blame options) to unsafegitrevisionoptions, and make the membership rule "the option takes a filesystem path" rather than "the option writes output".
--- Reported by zx (Jace) — GitHub: @manus-use
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/GitPythonto a version that resolves this vulnerability.Fixed in 3.1.59 - Configuration
Update the deny/unsafe option list used by `Git.check_unsafe_options(_option_candidates([rev,...], kwargs), unsafe_git_revision_options)` so that caller-influenced revision/options cannot include blame file path readers. At minimum, add `--contents`/`-S` (and any other blame options that take a filesystem path) to `unsafe_git_revision_options`, and base the rule on whether the option takes a filesystem path (not whether the option writes output).
GitPython Repo.blame / Repo.blame_incremental (unsafe option guard) unsafe_git_revision_options = Add/extend to include path-taking blame options such as --contents and -S
Event History
Frequently Asked Questions
Which deployments are exposed?
Applications using GitPython are exposed when they pass caller-influenced revision values to Repo.blame() or Repo.blame_incremental(). The affected process can disclose local files that it is permitted to read.
What must an attacker be able to do?
An attacker needs the ability to influence the revision argument supplied to one of the affected blame methods. A value using --contents=<path> can be interpreted as a Git option before the argument separator and cause the referenced file's contents to appear in the blame result.