# GHSA-h39g-6x3c-7fq9: Zio has SubFileSystem Path Confinement Bypass via Unresolved `..` Segment

> Attackers can bypass SubFileSystem path confinement using unresolved '..' segments to access parent directories, potentially reading or modifying files outside the intended sandbox.

- **Severity:** Not scored
- **Exploited in the wild:** Not known
- **Login needed:** Not needed
- **Attackable:** Not stated
- **Victim action needed:** Not needed
- **Fix available:** Yes
- **Published:** 18 Apr 2026

## What is GHSA-h39g-6x3c-7fq9?

GHSA-h39g-6x3c-7fq9 is a path traversal vulnerability in Zio, published on 18 Apr 2026. 

### Official description

# Summary

`SubFileSystem` fails to confine operations to its declared sub path when the input path is `/../` (or equivalents `/../`, `/..\\`). This path passes all validation but resolves to the root of the parent filesystem, allowing directory level operations outside the intended boundary.

# Affected Component

`Zio.UPath.ValidateAndNormalize`
`Zio.FileSystems.SubFileSystem`

`UPath.ValidateAndNormalize` has a trailing slash optimisation.

```csharp
if (!processParts && i + 1 == path.Length)
    return path.Substring(0, path.Length - 1);
```

When the input ends with `/` or `\`, and `processParts` is still false, the function strips the trailing separator and returns immediately before the `..` resolution logic runs.  The input `/../` triggers this path: the trailing `/` is the last character, `processParts` has not been set (because `..` as the first relative segment after root is specifically exempted), so the function returns `/..` with the `..` segment unresolved.

The resulting `UPath` with `FullName = "/.."` is absolute, contains no control characters, and no colon so it passes `FileSystem.ValidatePath` without rejection.

When this path reaches `SubFileSystem.ConvertPathToDelegate`:

```csharp
protected override UPath ConvertPathToDelegate(UPath path)
{
    var safePath = path.ToRelative();     // "/..".ToRelative() = ".."
    return SubPath / safePath;            // "/jail" / ".." = "/"  (resolved by Combine)
}
```

The delegate filesystem receives `/` (the root) instead of a path under `/jail`.

# Proof of Concept

```csharp
using Zio;
using Zio.FileSystems;

var root = new MemoryFileSystem();
root.CreateDirectory("/sandbox");
var sub = new SubFileSystem(root, "/sandbox");

Console.WriteLine(sub.DirectoryExists("/../"));           // True (sees parent root)
Console.WriteLine(sub.ConvertPathToInternal("/../"));     // "/" (parent root path)
```

# Impact

The escape is limited to directory level operations because appending a filename after `..` (e.g., `/../file.txt`) causes normal `..` resolution to trigger, which correctly rejects the path as going above root.  Only the bare terminal `/../` (which strips to `/..`) survives.  This means that exploitability is limited, and this vulnerability does not escalate to file read/write.

## Which versions are affected by GHSA-h39g-6x3c-7fq9?

2 open source packages ship an affected version, including Zio.


| Package | Ecosystem | Affected | Fixed in |
| --- | --- | --- | --- |
| `Zio` | NuGet | All versions | 0.22.2 |
| `Zio` | NuGet | All versions | 0.22.2 |

## How do I fix GHSA-h39g-6x3c-7fq9?

Upgrade Zio to 0.22.2 or later.

## Is GHSA-h39g-6x3c-7fq9 being exploited?

There is no known exploitation of GHSA-h39g-6x3c-7fq9 in the wild and no public exploit code.

## Fix prompt for AI coding agents

```text
Check this codebase for GHSA-h39g-6x3c-7fq9 and fix it if it is affected.

What it is: GHSA-h39g-6x3c-7fq9 is a path traversal vulnerability in Zio, published on 18 Apr 2026. 
Exploitation: There is no known exploitation of GHSA-h39g-6x3c-7fq9 in the wild and no public exploit code.

Affected packages and fixed versions:
- Zio (NuGet): affected all versions; fixed in 0.22.2
- Zio (NuGet): affected all versions; fixed in 0.22.2

Task:
1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.
2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.
3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.
4. Run the tests and summarise what changed and why.
5. If nothing in the project is affected, say so and change nothing.

Reference: https://insights.sbomify.com/GHSA-h39g-6x3c-7fq9/ (Markdown: https://insights.sbomify.com/GHSA-h39g-6x3c-7fq9/index.md)
```

## Sources

- OSV: https://osv.dev/vulnerability/GHSA-h39g-6x3c-7fq9

---
From sbomify Insights: https://insights.sbomify.com/GHSA-h39g-6x3c-7fq9/
Summaries are generated and may contain mistakes; the official description above is the record.
