PT-2026-35622 · Nuget · Zio

Published

2026-04-18

·

Updated

2026-04-18

CVSS v3.1

3.8

Low

VectorAV:L/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:N

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.

Fix

Path traversal

Found an issue in the description? Have something to add? Feel free to write us 👾

Weakness Enumeration

Related Identifiers

GHSA-H39G-6X3C-7FQ9

Affected Products

Zio