PYSEC-2026-3840

See a problem?
Import Source
https://github.com/pypa/advisory-database/blob/main/vulns/gitpython/PYSEC-2026-3840.yaml
JSON Data
https://api.osv.dev/v1/vulns/PYSEC-2026-3840
Aliases
Published
2026-09-10T09:44:51Z
Modified
2026-09-10T12:15:03Z
Severity
  • 7.5 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H CVSS Calculator
Summary
GitPython: Unguarded git option forwarding in Repo.init enables arbitrary command execution via --template clone hooks
Details

Summary

Repo.init() forwards **kwargs verbatim to git init with no unsafe-option guard and no allow_unsafe_options parameter. git init --template=<dir> copies <dir>/hooks/* into the new repo's .git/hooks, so an attacker-controlled template kwarg plants a hook that executes on the next git operation → arbitrary code execution. --template is already recognized as unsafe for clone (it is on unsafe_git_clone_options, and GHSA-6p8h-3wgx-97gf covers the clone path), but Repo.init is a distinct method that never received a guard and needs an independent fix.

Root Cause

Repo.init(path, mkdir, odbt, expand_vars, **kwargs) is a bare git.init(**kwargs) (git/repo/base.py:1435) with no check_unsafe_options and no allow_unsafe_options.

Impact

Arbitrary code execution (hook fires on next git op) at the privileges of the host process. Two preconditions raise attack complexity (AC:H): the app must forward a template= kwarg (KEY control) AND the attacker must stage an executable hook directory at a known path — the same profile GHSA-6p8h-3wgx-97gf accepted as HIGH for the clone path. Default allow_unsafe_options is irrelevant here because Repo.init has no guard at all.

Proof of Concept

# attacker stages /evil/hooks/post-commit (executable)
from git import Repo
Repo.init(path, template="/evil")
# next commit runs /evil/hooks/post-commit -> ACE

Attack Chain

  1. Entry: attacker stages /evil/hooks/post-commit (executable) and gets the app to call Repo.init(path, template='/evil').
  2. Check: NONE on Repo.init. Bypass proof: base.py:1435 is a bare git.init(**kwargs). argv (observed): ['git','init','--template=/evil'].
  3. Sink: git copies /evil/hooks/post-commit<repo>/.git/hooks/post-commit.
  4. Impact: next commit runs the hook → arbitrary code execution.

Bypass Evidence

Independently reproduced (gate harness): Repo.init(dst, template='<evil>') → argv ['git','init','--template=<evil>'] unguarded; hook copied into .git/hooks/post-commit; after git commit the INIT_ACE marker was created. --separate-git-dir=<path> is a parallel arbitrary-redirect vector through the same unguarded sink (value control only).

Affected Versions

GitPython <= 3.1.57 (unguarded git.init(**kwargs) present verbatim on the latest release tag).

Suggested Fix

Add a check_unsafe_options guard (with an allow_unsafe_options parameter) to Repo.init, consulting a denylist that includes --template and --separate-git-dir (path-taking / hook-installing options).


Reported by zx (Jace) — GitHub: @manus-use

References

Affected packages

PyPI / gitpython

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
3.1.58

Affected versions

0.*
0.1.7
0.2.0-beta1
0.3.0-beta1
0.3.0-beta2
0.3.1-beta2
0.3.2.RC1
0.3.2
0.3.2.1
0.3.3
0.3.4
0.3.5
0.3.6
0.3.7
1.*
1.0.0
1.0.1
1.0.2
2.*
2.0.0
2.0.1
2.0.2
2.0.3
2.0.4
2.0.5
2.0.6
2.0.7
2.0.8
2.0.9.dev0
2.0.9.dev1
2.0.9
2.1.0
2.1.1
2.1.2
2.1.3
2.1.4
2.1.5
2.1.6
2.1.7
2.1.8
2.1.9
2.1.10
2.1.11
2.1.12
2.1.13
2.1.14
2.1.15
3.*
3.0.0
3.0.1
3.0.2
3.0.3
3.0.4
3.0.5
3.0.6
3.0.7
3.0.8
3.0.9
3.1.0
3.1.1
3.1.2
3.1.3
3.1.4
3.1.5
3.1.6
3.1.7
3.1.8
3.1.9
3.1.10
3.1.11
3.1.12
3.1.13
3.1.14
3.1.15
3.1.16
3.1.17
3.1.18
3.1.19
3.1.20
3.1.22
3.1.23
3.1.24
3.1.25
3.1.26
3.1.27
3.1.28
3.1.29
3.1.30
3.1.31
3.1.32
3.1.33
3.1.34
3.1.35
3.1.36
3.1.37
3.1.38
3.1.40
3.1.41
3.1.42
3.1.43
3.1.44
3.1.45
3.1.46
3.1.47
3.1.48
3.1.49
3.1.50
3.1.51
3.1.52
3.1.53
3.1.54
3.1.55
3.1.56
3.1.57

Database specific

source
"https://github.com/pypa/advisory-database/blob/main/vulns/gitpython/PYSEC-2026-3840.yaml"