Summary
GitPython: TagReference.create positional reference bypasses kwargs-only --file guard, enabling arbitrary file read (incomplete fix of 3af0c251)
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 unsafe_git_tag_options = ["--file","-F"] and a guard call, but the guard is Git.check_unsafe_options(options=Git._option_candidates([], kwargs), unsafe_options=...) 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.
Proof of Concept
from git import TagReference
t = TagReference.create(repo, "vpwn", reference="--file=/home/app/.ssh/id_rsa")
print(t.tag.message) # contents of the file
Attack Chain
- Entry: app calls
TagReference.create(repo, name, reference=<user>)withreference="--file=/home/app/.ssh/id_rsa". - Check:
Git.check_unsafe_options(_option_candidates([], kwargs), ["--file","-F"])@ tag.py:137-141. Guard: denylist includes--file/-F. Bypass proof:_option_candidatesreceivesargs=[]→ the positionalreferenceis never a candidate (the kwarg spellingfile="…"IS blocked; only the positional escapes). - Sink:
repo.git.tag(*args, **kwargs)@ tag.py:158 → no--. argv (observed):['git','tag','-f','vpwn','--file=<secret>']. - Impact: annotated tag created;
tagref.tag.message== file contents (arbitrary file read).
Bypass Evidence
Independently reproduced (independent test harness, git 2.43.0, default allow_unsafe_options=False): TagReference.create(repo,'vp','--file=<secret>') → PASSED; tag.message == 'GATE_SECRET_LINE_A\nGATE_SECRET_LINE_B'. Control: TagReference.create(..., file='<secret>') → UnsafeOptionError: --file is not allowed. Fix-commit read: 3af0c251 adds _option_candidates([], 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).
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 allow_unsafe_options=False.
CVE-2026-78679 has a CVSS score of 6.5 (Medium). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (3.1.59); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Include the positional reference (and path) in the option-candidate list passed to check_unsafe_options, or place a -- separator before the positional arguments in TagReference.create().
Reported by zx (Jace), GitHub: @manus-use
Frequently Asked Questions
- What is CVE-2026-78679? CVE-2026-78679 is a medium-severity security vulnerability in GitPython (pip), affecting versions <= 3.1.58. It is fixed in 3.1.59.
- How severe is CVE-2026-78679? CVE-2026-78679 has a CVSS score of 6.5 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of GitPython are affected by CVE-2026-78679? GitPython (pip) versions <= 3.1.58 is affected.
- Is there a fix for CVE-2026-78679? Yes. CVE-2026-78679 is fixed in 3.1.59. Upgrade to this version or later.
- Is CVE-2026-78679 exploitable, and should I be worried? Whether CVE-2026-78679 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-78679 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-78679? Upgrade
GitPythonto 3.1.59 or later.