Disclosure summary
### Summary `Repo.__init__` decides which directory is the git directory by testing candidate paths in an order that considers the real `.git` **last**. Two earlier tests can be satisfied by ordinary tracked files. Git reserves only the literal name `.git`, so `HEAD`, `objects/`, `refs/`, `config`, `gitdir`, `commondir` and `hooks/` at a repository root are all legal tracked content. Consequently, after a victim opens or clones an attacker's repository, GitPython resolves `git_dir` to the **working-tree root** while real git correctly resolves `/.git`. Everything GitPython then treats as "inside the git directory" is attacker-authored content — including `hooks/`, which it executes. ### CVE-2026-87817 ### Affected code (3.1.59) The discovery loop in `git/repo/base.py` tests, in order: 1. `git/repo/base.py:299` — `isfile(curpath/gitdir)` **and** `isfile(curpath/commondir)` **and** `isfile(curpath/HEAD)` 2. `git/repo/base.py:320` — `is_git_dir(curpath)` 3. `git/repo/base.py:341` — `dotgit = osp.join(curpath, ".git")` ← the real git dir, considered last `is_git_dir` (`git/repo/fun.py:60`) requires only that `objects/` and `refs/` are directories and that `HEAD` is a file; **`HEAD`'s c
Source-specific records & product guidance
Sources retain their own attribution and scoring. Follow the original record to confirm affected versions, fixed releases, and configuration conditions.
GitHub Reviewed Security Advisories · GHSA-239g-whfq-7xj9
Open original source · Updated Sep 30, 2026
GitPython: Repository content can impersonate the git directory, leading to arbitrary code execution
Source severity: HIGH / 0
| Ecosystem | Package | Affected range | First patched |
|---|---|---|---|
| pip | gitpython | 3.1.60 |
Original records & references
- NIST NVD record
- CVE Program record
- github.com — Reviewed advisory
PUBLISHED 2026-09-30T19:27:59-04:00
MODIFIED 2026-09-30T19:28:01-04:00
INGESTED 2026-10-06T11:45:01-04:00