CVE-2026-78679: GitPython before 3.1.59 Arbitrary File Read via TagReference.create
Summary TagReference.create() forwards a caller-influenced positional reference value into git tag without it ever being inspected by the unsafe-option guard, allowing an arbitrary file read (the file's contents are returned in-band as the annotated tag message). This is an incomplete-fix bypass of commit 3af0c251 (the fix for GHSA-3f7w-8rr8-f37f's tag instance).
Root Cause The fix 3af0c251 added unsafegittagoptions = ["--file","-F"] and a guard call, but the guard is Git.checkunsafeoptions(options=Git.optioncandidates([], kwargs), unsafeoptions=...) at git/refs/tag.py:139 — it passes an EMPTY args list and inspects kwargs only. The dangerous values path and reference are POSITIONALS (args = (path, reference), tag.py:156), placed before any --. A user-influenced reference="--file=<path>" therefore reaches git tag as the exact --file option the fix intended to block, creating an annotated tag whose message is the file's contents.
Impact Arbitrary local file read at the privileges of the host process; contents returned in-band via tagref.tag.message. Requires the embedding application to forward a caller-influenced reference value into TagReference.create() (pure VALUE control — the CVE-2026-42215 threat model). Default allowunsafeoptions=False.
Proof of Concept python from git import TagReference t = TagReference.create(repo, "vpwn", reference="--file=/home/app/.ssh/idrsa") print(t.tag.message) # contents of the file
Attack Chain 1. Entry: app calls TagReference.create(repo, name, reference=<user>) with reference="--file=/home/app/.ssh/idrsa". 2. Check: Git.checkunsafeoptions(optioncandidates([], kwargs), ["--file","-F"]) @ tag.py:137-141. Guard: denylist includes --file/-F. Bypass proof: optioncandidates receives args=[] → the positional reference is never a candidate (the kwarg spelling file="…" IS blocked; only the positional escapes). 3. Sink: repo.git.tag(args, kwargs) @ tag.py:158 → no --. argv (observed): ['git','tag','-f','vpwn','--file=<secret>']. 4. Impact: annotated tag created; tagref.tag.message == file contents (arbitrary file read).
Bypass Evidence Independently reproduced (independent test harness, git 2.43.0, default allowunsafeoptions=False): TagReference.create(repo,'vp','--file=<secret>') → PASSED; tag.message == 'GATESECRETLINEA\nGATESECRETLINEB'. Control: TagReference.create(..., file='<secret>') → UnsafeOptionError: --file is not allowed. Fix-commit read: 3af0c251 adds optioncandidates([], kwargs) (empty args → positional never a candidate).
Affected Versions GitPython <= 3.1.58 (sink present verbatim on the latest release tag; git diff 3.1.57..HEAD touches only test files).
Suggested Fix Include the positional reference (and path) in the option-candidate list passed to checkunsafeoptions, or place a -- separator before the positional arguments in TagReference.create().
--- Reported by zx (Jace) — GitHub: @manus-use
Other sources
GitPython before 3.1.59 contains an arbitrary file read vulnerability in TagReference.create() where a positional reference parameter bypasses the unsafe option guard. Attackers can supply a reference value like --file=<path> to read arbitrary files, with contents returned in the annotated tag message.
— MITRE
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 - Upgrade
Upgrade
GitPythonto a version that resolves this vulnerability.Fixed in 3.1.59 - Configuration
Adjust TagReference.create so the guard call inspects the actual user-influenced positional arguments (reference/path). Concretely, change the guard input from an empty args list to include positionals (e.g., pass the positional reference/path into _option_candidates) or ensure git tag receives a '--' separator before any positional reference so that '--file' cannot be interpreted as an option.
TagReference.create (git/refs/tag.py) Unsafe option guard = Include positional reference/path in unsafe option candidate list (args) or insert '--' separator before positional args
Event History
Frequently Asked Questions
Who can exploit this issue?
An attacker needs privileges to invoke application functionality that calls TagReference.create() and control its positional reference argument. The supplied CVSS vector indicates network reachability, low attack complexity, no user interaction, and low privileges required.
Are default GitPython installations affected?
The issue is in TagReference.create() before version 3.1.59, but the provided data does not establish whether any particular application uses that method or exposes attacker-controlled reference values by default.
What is the impact if exploitation succeeds?
An attacker can provide a value such as --file=<path> and cause arbitrary file contents to be included in an annotated tag message. The stated impact is high confidentiality impact, with no integrity or availability impact.
What can be done if upgrading is not immediately possible?
Do not allow untrusted users to control the positional reference parameter passed to TagReference.create(). Reject option-like reference values, particularly values beginning with -- and values using --file=<path>.