GHSA-5fqc-mrg8-w798
Dulwich: Symlink directory traversal in filter-branch index_filter via cross-commit state persistence
- Risk score
- 57/100
- CVSS
- 8.6 v3.1
- Published
- 2 Oct 2026
Open in the live forecast Today's forecast
What to know
- What it is
- Dulwich's filter_branch.py CommitFilter._apply_index_filter() is vulnerable to symlink directory traversal. When processing commit history, materialized tree entries (including symlinks) persist in the working directory between commits, allowing a symlink from an ancestor commit to redirect file writes from a descenda…
- Affected
- dulwich (pip) >= 0.23.1, <= 1.2.7
- Fix
- Upgrade dulwich to 1.2.8 or later.
- Exploitation
- No known exploitation
Story map
At a glance
dulwich: Amber, patch this week. dulwich/index.py (Methods: validate_path_element_ntfs, _tree_to_fs_path) dulwich/porcelain/__init__.py (Method: _checked_worktree_path) A High-severity Path Traversal vulnerability exists in Dulwich's checkout logic when running on Windows. The functions responsible for validating NTFS paths strictly reject .git varia…
The flaws
- GHSA-5fqc-mrg8-w798: Dulwich: Symlink directory traversal in filter-branch index_filter via cross-commit state persistence (CVSS 8.6)
- GHSA-8mcx-5rqc-vhmf: Dulwich: Arbitrary File Write (RCE) on Windows via Unvalidated Drive Letters in Tree Paths (CVSS 8.8)
- GHSA-8w8g-wq8h-fq33: Dulwich: Symlink write-through in checkout(paths=[]) via raw os.open bypasses all symlink protections (CVSS 8.6)
- GHSA-cm62-gvxx-vmxx: Dulwich: Symlink directory traversal in stash pop allows arbitrary file write via intermediate directory symlinks (CVSS 8.6)
The fix
Fix: Upgrade dulwich to 1.2.9 or later.
Who is exposed: dulwich (pip) < 1.2.9
Where it stands
- Public proof of concept
Connects to
- Same weakness: Path traversal (CWE-22). 397 other warnings in 30 days
Why it is Yellow
- CVSS 8.6 high
- On your watchlist: PyPI
Affected packages
| Package | Ecosystem | Vulnerable | Fixed in |
|---|---|---|---|
| dulwich | pip | >= 0.23.1, <= 1.2.7 | 1.2.8 |
References
- https://github.com/advisories/GHSA-5fqc-mrg8-w798
- https://github.com/jelmer/dulwich/security/advisories/GHSA-5fqc-mrg8-w798
- https://github.com/jelmer/dulwich/commit/9571ac60b851fce228dae7ededb380c6ce9b3fb8
- https://github.com/jelmer/dulwich/releases/tag/dulwich-1.2.8
- https://github.com/advisories/GHSA-5fqc-mrg8-w798
Original description
## Summary Dulwich's `filter_branch.py` `CommitFilter._apply_index_filter()` is vulnerable to symlink directory traversal. When processing commit history, materialized tree entries (including symlinks) persist in the working directory between commits, allowing a symlink from an ancestor commit to redirect file writes from a descendant commit to arbitrary filesystem locations. ## Root Cause `_apply_index_filter()` at `dulwich/filter_branch.py:212` calls `build_index_from_tree(".", tmp_index_path, ...)` which materializes all tree entries to the current working directory. The `finally` block (line 229-230) only cleans up the temporary index file (`os.unlink(tmp_index_path)`) — NOT the filesystem files written to CWD. When `process_commit()` processes parents recursively first (line 260), files materialized from ancestor commits persist and affect processing of descendant commits. On dulwich 1.2.7, `build_file_from_blob()` has no symlink protection, and `validate_path_element` only validates name patterns, not filesystem state. ## Impact An attacker can craft a malicious repository where running `filter_branch` with an index filter writes attacker-controlled content to arbitrary filesystem locations via symlink traversal. This achieves RCE if the write targets `.git/hooks/`. ## Attack Scenario 1. Attacker creates a repository where commit history (linearized) has: - Ancestor commit: tree entry `evil` (mode 120000, symlink → `/target_dir`) - Descendant commit: tree entry `evil/payload` (mode 100644, attacker content) 2. Victim clones repository and runs `filter_branch` with an index filter 3. `process_commit()` processes ancestor first → materializes `evil` as symlink to `/target_dir` in CWD 4. CWD is NOT cleaned between commits 5. Processing descendant: `os.path.exists("./evil")` → True (symlink exists). `build_file_from_blob(blob, mode, "./evil/payload")` → `open("./evil/payload", "wb")` follows intermediate symlink → writes to `/target_dir/payload` ## Suggested Fix Clean the CWD between commit iterations in `_apply_index_filter()`, or verify that no intermediate path components are symlinks before writing files. Reported by **zx (Jace)**
Last activity 2 Oct 2026. Patch Forecast updates every hour.