GHSA-w8jc-g24h-crhw

Suggest an improvement
Source
https://github.com/advisories/GHSA-w8jc-g24h-crhw
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-w8jc-g24h-crhw/GHSA-w8jc-g24h-crhw.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-w8jc-g24h-crhw
Aliases
Published
2026-10-27T15:26:43Z
Modified
2026-10-27T15:47:24Z
Severity
  • 7.5 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H CVSS Calculator
Summary
GitPython command wrappers omit unsafe protocol and option checks
Details

Summary

GitPython has spent 2022-2026 rolling out two reusable guards method-by-method: Git.check_unsafe_protocols(url) (raises UnsafeProtocolError for <transport>:: remote-helper URLs - CVE-2022-24439) and Git.check_unsafe_options(...) (raises UnsafeOptionError for denylisted option spellings - CVE-2023-40267, CVE-2026-42215, CVE-2026-67323). Three wrapper methods that build a git argv from caller-controlled values still have neither:

  1. Git.ls_remote (git/cmd.py:1128-1143) - checks options (:1139-1141) but forwards the URL positional unchecked; no allow_unsafe_protocols parameter exists. The same URL through clone_from raises UnsafeProtocolError.
  2. Repo.merge_base (git/repo/base.py:895-936) - forwards **kwargs/*rev to self.git.merge_base with no guard; guarded sibling iter_commits sits 12 lines up. Its exception handler additionally swallows every non-128 GitCommandError into [], masking the injection.
  3. IndexFile.move (git/index/base.py:1092-1168) - forwards **kwargs to git mv twice (:1147, :1167); guarded sibling remove() is 23 lines above.

Verified unfixed on main @ a54d159 (2026-09-21), post-dating latest release 3.1.62 (2026-09-07) - all three sites re-verified today.

Impact

Site 1 (ls_remote) - RCE conditional on the victim git transport policy. The common 'probe first, then clone' pattern (Git(...).ls_remote(attacker_url)) with an ext::sh -c ... URL executes arbitrary commands when the victim git permits the ext transport: dynamically verified with protocol.ext.allow=always (marker executed). git >= 2.38.1 stock config refuses the transport (fatal: transport 'ext' not allowed, rc 128) - GitPython adds no defense-in-depth and surfaces a bare GitCommandError. Environments on git < 2.38.1 are exposed with stock config - the exact population CVE-2022-24439 was scored against.

Sites 2/3 - latent argument-injection surface: caller kwargs flow 1:1 into the git argv before operands and -- (cmd.py:1850 opt_args + ext_args), and execute_kwargs (incl. shell=True/env - the CVE-2023-40267 Windows mechanism) passes through unguarded. Today git merge-base/git mv expose no exec option, so we claim the missing-guard class (vendor-settled by CVE-2026-67323, which bundled archive/ls_remote/iter_commits/blame in one advisory), not direct RCE.

Dynamic verification (today; GitPython main @ a54d159, git 2.55.0, Linux)

# Call Result
T1 repo.git.ls_remote('ext::/tmp/gp_touch.sh'), stock config git refuses (rc 128); no UnsafeProtocolError from GitPython
T1b same + victim config protocol.ext.allow=always marker created - arbitrary command executed
T2 repo.git.ls_remote('.', upload_pack=...) blocked by cmd.py:1141 (options-side fixed - protocol-side is the residual gap)
T3 repo.merge_base('main','side', upload_pack=...) not blocked; argv git merge-base --upload-pack=... main side; rc 129 silently converted to []
T4 repo.index.move(['f1','f2'], output=...) not blocked; option reaches git mv (rc 129)

Coverage matrix (main @ a54d159)

check_unsafe_protocols: 7 call sites (_clone, archive, set_url, create, fetch, pull, push) - none on ls_remote. check_unsafe_options: 24 call sites incl. ls_remote, iter_commits, IndexFile.remove - absent on merge_base and move.

Precedents (same repo, same guard family)

  • CVE-2022-24439 / GHSA-hcpj-qp55-gfph (critical): check_unsafe_protocols on the clone path - Site 1 is the same ext:: primitive on a sibling API.
  • CVE-2023-40267 / GHSA-pr76-5cm5-w9cj (critical, 'incomplete fix for CVE-2022-24439'): check_unsafe_options - unguarded kwargs to argv treated as RCE (shell=True on Windows).
  • CVE-2026-42215 / GHSA-rpm5-65cw-6hj4 (high, fixed 3.1.47): underscored-spelling bypass - rollout still active in 2026.
  • CVE-2026-67323 / GHSA-956x-8gvw-wg5v (high, 8.4, fixed 3.1.51): added check_unsafe_options to archive/ls_remote/iter_commits/blame; its PoC is literally repo.git.ls_remote('.', upload_pack=...). Establishes that a wrapper missing the check is itself a vulnerability. This report is the residual gap of that rollout: ls_remote got options but not protocols; merge_base/move got neither.

Recommended fix (existing helpers, 1-3 lines per site)

  1. Git.ls_remote: if not allow_unsafe_protocols: self.check_unsafe_protocols(args[0]) + new allow_unsafe_protocols: bool = False (the _clone pattern, base.py:1536-1538).
  2. Repo.merge_base: check_unsafe_options(Git._option_candidates(list(rev), kwargs), unsafe_options=self.unsafe_git_revision_options) (the iter_commits pattern); stop masking non-128 statuses.
  3. IndexFile.move: the remove() pattern with unsafe_git_pathspec_from_file_options.

One advisory, three affected methods, severity anchored on Site 1 - mirroring CVE-2026-67323's bundling.

Database specific
{
    "cwe_ids":  [
        "CWE-1287"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-10-27T15:26:43Z",
    "nvd_published_at":  null,
    "severity":  "HIGH"
}
References

Affected packages

PyPI / gitpython

Package

Affected ranges

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

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
3.1.58
3.1.59
3.1.60
3.1.61
3.1.62

Database specific

last_known_affected_version_range
"<= 3.1.62"
source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-w8jc-g24h-crhw/GHSA-w8jc-g24h-crhw.json"