CVE-2026-44471: Verified Repro With Script Download
CVE-2026-44471: gitoxide gix-fs : symlink worktree escape on checkout writes files outside the worktree
CVE-2026-44471 is verified against gix-fs · cargo. Affected versions: gix-fs <= 0.21.0 (gitoxide < 0.21.1). Fixed in gix-fs 0.21.1. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00155.
What Is CVE-2026-44471?
CVE-2026-44471 (CWE-59, Link Following) is a high-severity symlink worktree escape in the gix-fs crate (part of the gitoxide project), where checking out a malicious git tree can write attacker-controlled files outside the intended worktree directory. Pruva reproduced it (reproduction REPRO-2026-00155).
CVE-2026-44471 Severity & CVSS Score
CVE-2026-44471 is rated high severity, with a CVSS base score of 7.8 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected gix-fs Versions
gix-fs · cargo versions gix-fs <= 0.21.0 (gitoxide < 0.21.1) are affected.
How to Reproduce CVE-2026-44471
pruva-verify REPRO-2026-00155 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00155/artifacts/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-44471
Reproduced by Pruva's autonomous agents — 334 tool calls over 33 min. Full root-cause analysis and the complete transcript are below.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-44471
Summary
CVE-2026-44471 is a symlink worktree escape vulnerability in the gix-fs crate (part of the gitoxide project). During worktree checkout, when the gix-fs::Stack reuses a previously cached leaf path component (e.g., a symlink) as a directory parent for a subsequent path, it skips re-invoking the delegate's push() callback. This bypasses collision detection and allows a malicious symlink planted earlier in the checkout to redirect later file writes outside the intended worktree directory.
Impact
- Package/component affected:
gix-fscrate (used transitively bygix-worktreeandgixduring checkout) - Affected versions:
gix-fs < 0.21.1 - Fixed versions:
gix-fs 0.21.1 - Risk level: High (CVSS 7.8)
- Consequences: An attacker who controls a git repository can cause files to be written to arbitrary locations writable by the victim user during checkout, via a malicious tree containing a symlink entry followed by a file entry that reuses the symlink's path prefix.
Root Cause
The bug is in gix_fs::Stack::make_relative_path_current() (in gix-fs/src/stack.rs). When a previously cached leaf component needs to be promoted to a directory parent (because a later path has additional child components), the old code called delegate.push_directory() directly without first calling delegate.push(false, self).
The push() callback is where gix-worktree's delegate performs collision detection: it checks whether the path already exists as a symlink or file and either errors out or unlinks it. By skipping this callback during leaf-to-directory transitions, a symlink could persist and later paths would resolve through it.
The fix (commit 93d0ff6 in the gitoxide repository) adds delegate.push(false, self)?; before delegate.push_directory(self)?; when promoting a cached leaf, and also adds proper state restoration if either callback fails.
Reproduction Steps
The reproduction script is repro/reproduction_steps.sh. It performs the following steps:
- Creates a minimal Rust binary that depends on
gix-worktree = "=0.52.0"and pinsgix-fsto either=0.21.0(vulnerable) or=0.21.1(fixed). A local patched copy ofgix-hash = "=0.25.0"is used via[patch.crates-io]to resolve a compilation incompatibility with modern Rust. - The binary creates a worktree directory and a "forbidden" directory outside it.
- It instantiates a
gix_worktree::Stackconfigured for checkout (stack::State::for_checkout). - It calls
cache.at_path("link", IS_SYMLINK)to get the path where a symlink would be created, then manually creates a symlink at that path pointing to the forbidden directory. - It calls
cache.at_path("link/file", IS_FILE)— this exercises the vulnerable leaf-to-directory promotion. - If the call succeeds (vulnerable), the script writes a file to the returned path and verifies it appears inside the forbidden directory, proving the escape.
- If the call errors with
AlreadyExists(fixed), the script confirms no file was written outside the worktree.
Expected evidence of reproduction
- Vulnerable (
gix-fs 0.21.0): the binary printsVULNERABLE: file escaped worktree to .../forbidden/fileand exits 0. The forbidden directory contains the escaped file. - Fixed (
gix-fs 0.21.1): the binary printsFIXED: symlink collision blocked, no escapeand exits 1 (indicating the bug is no longer present). The forbidden directory remains empty.
Evidence
logs/vulnerable.log— captures the vulnerable run showing the file was written to the forbidden directory:VULNERABLE: file escaped worktree to .../forbidden/file Exit code: 0 Forbidden: -rw-r--r-- 1 root root 7 ... filelogs/fixed.log— captures the fixed run showing the collision was blocked:FIXED: symlink collision blocked, no escape Exit code: 1 Forbidden: (empty)logs/summary.log— confirms the differential outcome:Vulnerable exit code: 0 Fixed exit code: 1 CONFIRMED: Symlink worktree escape reproduced on vulnerable version and blocked on fixed versionlogs/runtime_manifest.json— structured record of the two runs.- Environment: Rust 1.94.1 on Linux x86_64.
Recommendations / Next Steps
- Upgrade: All consumers of
gix-fsshould upgrade to>= 0.21.1. - Audit callers: Any custom delegates using
gix_fs::Stackshould ensure theirpush()implementation performs security validation on all components, not just leaf components. - Regression testing: The fix commit includes regression tests in
gix-worktree/tests/worktree/stack/create_directory.rsthat should be maintained and run in CI.
Additional Notes
- The reproduction was confirmed idempotent: running
repro/reproduction_steps.shtwice produced identical results. - The reproduction requires a patched
gix-hash 0.25.0to compile on Rust 1.94+ due to upstreammatch selfexhaustiveness lint issues; this is a build-time incompatibility unrelated to the vulnerability under test.
CVE-2026-44471 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Artifacts and Evidence for CVE-2026-44471
Scripts, logs, diffs, and output captured during the reproduction.
No artifacts available
How to Fix CVE-2026-44471
Upgrade gix-fs · cargo to gix-fs 0.21.1 or later.
FAQ: CVE-2026-44471
How does the CVE-2026-44471 symlink escape attack work?
Which gix-fs versions are affected by CVE-2026-44471, and where is it fixed?
How severe is CVE-2026-44471?
How can I reproduce CVE-2026-44471?
References for CVE-2026-44471
Authoritative sources for CVE-2026-44471 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.