GHSA-p3hw-mv63-rf9w: gix's submodule name validation bypass + trust inheritance flaw enables path traversal and credential disclosure
SummarySubmodule name validation bypass plus missing validation in production code paths allows path traversal via crafted `.gitmodules`. Combined with a trust inheritance flaw in `Submodule::open()`, this enables reading arbitrary git repository configs (including credentials) from traversed paths with full trust (CWE-22, CWE-200).Details**Bug 1: Validation bypass in `gix-validate/src/submodule.rs` (lines 27-42)**The `name()` function uses `name.find(b"..")` which returns only the FIRST occurrence. If the first `..` is embedded in a non-traversal context, the function returns `Ok` without checking subsequent `../` sequences:Bypass: `a..b/../../../.git/` passes because `find(b"..")` returns position 1 (the `..` in `a..b`), checks `name[3] == b'b'`, and returns Ok. The real `/../../../` is never checked.**Bug 2: Validation never called in production**`gix_validate::submodule::name()` has zero production callers (only test code). The `names()` iterator in `gix-submodule/src/access.rs:29` explicitly documents it returns "unvalidated names."`git_dir()` at `gix/src/submodule/mod.rs:198-204` constructs filesystem paths from raw names:**Bug 3: Trust inheritance bypass in `Submodule::open()`**At `gix/src/submodule/mod.rs:270`, `open()` clones the parent repository's options:The parent's `options.git_dir_trust` is `Some(Trust::Full)`. At `gix/src/open/repository.rs:103-104`:Since trust is already `Some(Full)`, the ownership check is **skipped entirely**. The traversed path is opened with `Trust::Full` regardless of ownership, bypassing gitoxide's safe-directory protections.PoCCompiled and executed in Rust 1.94.1 `--release` mode. All bypass cases confirmed:Attack chainAttacker crafts a repository with `.gitmodules`:Victim clones the repository using a tool built on gitoxide.When the tool iterates submodules and calls `submodule.open()` or `submodule.status()`:`git_dir()` returns `.git/modules/x..y/../../..` which resolves to the parent `.git/``open_opts()` is called with `Trust::Full` (inherited from parent, ownership check skipped)The parent's `.git/config` is fully parsedThe returned `Repository` object exposes all config values from the traversed path:`remote.origin.url` (may contain `https://user:token@github.com/...`)`http.extraHeader` (often `Authorization: Bearer <token>`)`credential.*` sections`core.sshCommand`Accessible via standard API: `repo.config_snapshot().string("http.extraHeader")`, `repo.find_remote("origin")`, etc.ImpactA crafted `.gitmodules` in a malicious repository causes gitoxide to open arbitrary git directories as submodule repositories with full trust, exposing their configuration including credentials. This is the same class of vulnerability as GHSA-7w47-3wg8-547c (path traversal), but through the submodule name vector with an additional trust bypass.The trust inheritance is the critical amplifier: without it, the traversed path would undergo ownership checks that could block the attack. With it, any git directory reachable via `../` is opened with full trust.Honest limitationsThe traversed path must be a valid git directory (HEAD, objects/, refs/ must exist)The victim's tool must call `open()` or `status()` on submodules (tools that only list submodules are not affected)Credential exposure requires the target config to contain embedded credentialsSubmodule operations currently require explicit user actionSuggested fixFix the validation to check ALL `..` occurrences (iterate, not single `find`)Call `gix_validate::submodule::name()` in `git_dir()` before constructing the pathDo NOT inherit `git_dir_trust` from parent when opening submodule repos -- always re-derive trust from path ownershipSeverityHigh. Network vector (via clone), requires user interaction (submodule operations). The trust bypass enables credential disclosure from traversed git directories. Confidentiality impact is high.
CVSS v4.0: 7.5