Dulwich: Symlink directory traversal in stash pop allows arbitrary file write via intermediate directory symlinks
CVE-2026-114098 · GHSA-mw9m-763w-f8m9 · EUVD-2026-106901 · GHSA-cm62-gvxx-vmxx · published 2 Oct 2026
How serious is it
- Severity: high, CVSS score 7.3 out of 10.
- No exploit prediction (EPSS) score yet.
- Not on the CISA list of exploited vulnerabilities.
- No public exploit in Exploit-DB.
Is your app affected by CVE-2026-114098?
Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.
Who is affected
dulwich(PyPI) >= 0.22.5, < 1.2.8
How to check
- Run
pip show dulwichin your project to see the installed version, including copies pulled in by other packages. - Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.
- Or run
npx shippable-dev scan localhost:3000in your project folder. It reads your lockfile and flags this vulnerability along with every other known one.
How to fix
Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
- Upgrade
dulwichto 1.2.8 or later, commit the lockfile and redeploy. - If a vulnerable version was live, check your logs and rotate the secrets that server could read.
The advisory
## Summary
Dulwich's `stash.py:pop()` function is vulnerable to symlink directory traversal, allowing an attacker to write arbitrary files outside the repository worktree when a victim pops a stash in a malicious repository.
## Root Cause
The `pop()` function at `dulwich/stash.py:236` uses `os.path.exists(parent_dir)` to check if a parent directory exists before writing stashed files. `os.path.exists()` follows symlinks, so when an intermediate directory in the path is a symlink pointing outside the worktree (e.g., `link → ../../.git/hooks`), the check passes and subsequent file writes resolve through the symlink.
The `validate_path()` function (line 228) only validates path component names against `INVALID_DOTNAMES` — it performs zero filesystem symlink detection. On dulwich 1.2.7 (latest release), `build_file_from_blob()` has no symlink protection whatsoever.
## Impact
An attacker can craft a malicious repository that, when a victim clones it and performs a stash pop operation, writes attacker-controlled content to arbitrary filesystem locations. Writing to `.git/hooks/post-checkout` achieves Remote Code Execution on the victim's machine on the next git checkout operation.
## Attack Scenario
1. Attacker creates a repository with branch `main` containing `link` (symlink → `../../.git/hooks`) and branch `feature` containing `link/post-checkout` (executable payload)
2. Victim clones the repository (landing on `main` — symlink `link` exists in worktree)
3. Victim checks out `feature`, makes changes, runs `stash.push()`
4. Victim checks out `main` (restoring the `link` symlink)
5. Victim runs `stash.pop(0)` — stash contains `link/post-checkout`
6. `os.path.exists("link")` returns True (symlink to existing directory), `os.makedirs` skipped
7. `build_file_from_blob(blob, mode, "link/post-checkout")` → `open("link/post-checkout", "wb")` follows the intermediate symlink → payload written to `.git/hooks/post-checkout`
8. Next checkout operation triggers the hook → RCE
## Suggested Fix
Before writing any file, verify that no component of the target path resolves through a symlink outside the worktree. Use `os.path.realpath(parent_dir)` and confirm it stays within the repository root. Alternatively, use `os.open()` with `O_NOFOLLOW` on each path component.
Reported by **zx (Jace)**
Check your whole app
Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.
References
- github.com/jelmer/dulwich/security/advisories/GHSA-cm62-gvxx-vmxx
- github.com/jelmer/dulwich/commit/40a542cb02f9ac39a9eeac1193472903098698f9
- github.com/jelmer/dulwich
- github.com/jelmer/dulwich/releases/tag/dulwich-1.2.8
- www.vulncheck.com/advisories/dulwich-0.22.5-before-1.2.8-path-traversal-via-stash-pop-symlinks
Merged from OSV, ENISA EUVD. Last updated 28 Oct 2026. All vulnerabilities · RSS · JSON