gix-fs: Symlink prefix-reuse allows worktree escape during checkout
Date: 2026-05
Affected: rust/gix-fs
Attack vector: path-traversal
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-44471
CWE: CWE-59
CVSS: 7.8 (CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H)

### Summary A malicious tree can be constructed that will, when checked out with gitoxide, permit writing an attacker-controlled symlink into any existing directory the user has write access to. ### Details During checkout, all symlink index entries are deferred and created after regular files using a single shared `gix_worktree::Stack`. Internally, this uses a `gix_fs::Stack`. `gix_fs::Stack::make_relative_path_current()` caches validated path prefixes: when the previously-processed leaf component exactly matches the leading component(s) of the next path, the leaf-to-directory transition at `gix-fs/src/stack.rs:195-197` invokes only `delegate.push_directory()`, never `delegate.push()`. In `gix_worktree::stack::delegate::StackDelegate`, when the state member is `State::CreateDirectoryAndAttributesStack`, `Attributes::push_directory()` only loads attributes (from the ODB, in the clone case), and does not perform any other checks. The on-disk `symlink_metadata()` check and unlink-on-collision live in `StackDelegate::push()`'s invocation of `create_leading_directory()`, which is therefore bypassed for the cached prefix. The final symlink is created with plain `std::os::unix::fs::symlink`, which follows symlinks in parent directories. Therefore, it's possible to provide a tree with duplicate symlink and directory entries that exploits this. If a tree is constructed with: 1. A `120000` (symlink) entry `a` that points to `.git/hooks`. 2. A `040000` (directory) entry `a` with


Mitigations:

Architectural layers (MAESTRO):