Owasp.Untrust.BoxedPath 1.2.0

dotnet add package Owasp.Untrust.BoxedPath --version 1.2.0
                    
NuGet\Install-Package Owasp.Untrust.BoxedPath -Version 1.2.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Owasp.Untrust.BoxedPath" Version="1.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Owasp.Untrust.BoxedPath" Version="1.2.0" />
                    
Directory.Packages.props
<PackageReference Include="Owasp.Untrust.BoxedPath" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Owasp.Untrust.BoxedPath --version 1.2.0
                    
#r "nuget: Owasp.Untrust.BoxedPath, 1.2.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Owasp.Untrust.BoxedPath@1.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Owasp.Untrust.BoxedPath&version=1.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Owasp.Untrust.BoxedPath&version=1.2.0
                    
Install as a Cake Tool

Owasp.Untrust.BoxedPath (.NET)

NuGet License

A secure path management library for .NET designed to prevent Directory Traversal (Path Traversal) and Symbolic Link (Symlink) Jailbreak attacks. It provides a robust "sandbox" mechanism to ensure that all file system operations remain strictly within a defined root directory.

Changes

  • 1.2.0 - Renamed namespace to plural Owasp.Untrust.BoxedPaths to avoid "class name same as namespace name" problem
  • 1.1.0 - [DEPRECTATED/REVERTED] Renamed primary class from BoxedPath to Path to avoid "class name same as namespace name" problem - !!! this change conflicts with System.IO.Path
  • 1.0.0 - First release

The Need

Modern .NET applications frequently interact with the file system. However, accepting file paths from untrusted sources (user input, uploads, configuration files) introduces critical security risks:

  • Directory Traversal: Attackers using ../ sequences to access sensitive system files (e.g., ../../windows/system32/config/SAM).
  • Symlink Jailbreak: Attackers using symbolic links (symlinks) to trick the application into reading or writing files outside the intended directory, even if the path looks safe lexically.
  • TOCTOU Vulnerabilities: Time-of-Check to Time-of-Use race conditions where a path is validated but then swapped (e.g., via symlink) before it is used.

Standard .NET classes like System.IO.Path provide lexical manipulation but do not enforce security boundaries or handle advanced symlink resolution securely.

The Solution

Owasp.Untrust.BoxedPath introduces two core concepts:

  1. PathSandbox: Defines the secure root directory and the security policy (e.g., whether symlinks are allowed).
  2. BoxedPath: An immutable, secure wrapper around a file path. It guarantees that the path it holds has been validated to be inside its associated sandbox.

All operations on BoxedPath (like Combine, GetParent) return a new, validated BoxedPath or throw a SecurityException if the operation would result in a path outside the sandbox.

Key Features

  • Strict Sandboxing: Enforces that all paths must resolve to a location inside the sandbox root.
  • Symlink Defense: Offers a robust, component-by-component path traversal algorithm (ResolveAndValidatePath) that physically resolves symlinks to prevent jailbreaks.
  • TOCTOU Protection: Validation resolves the physical path, ensuring that the path used for I/O is the same one that was checked.
  • Drop-in API Feel: Designed to mimic the System.IO.Path and FileInfo APIs for ease of adoption.
  • Secure Wrappers: Provides BoxedFileInfo, BoxedDirectoryInfo, and BoxedFileStream to perform I/O operations safely without exposing the raw path.

Installation

Install via NuGet:

dotnet add package Owasp.Untrust.BoxedPath

Usage

1. Initialization

Create a PathSandbox instance to define your secure root.

using Owasp.Untrust.BoxedPaths; // note plural Path_s_

// Define a sandbox rooted at "C:\Safe\Uploads"
// Default policy: DISALLOW (Symlinks are followed but must stay inside the sandbox)
PathSandbox sandbox = PathSandbox.BoxRoot(@"C:\Safe\Uploads");

2. Creating Secure Paths

Use the Of() factory method to create a BoxedPath from an untrusted string.

try
{
    // User input: "user_data.txt" (Safe)
    BoxedPath safePath = BoxedPath.Of(sandbox, "user_data.txt");
    
    // User input: "../../../windows/system.ini" (Malicious)
    // THROWS SecurityException immediately!
    BoxedPath maliciousPath = BoxedPath.Of(sandbox, "../../../windows/system.ini");
}
catch (SecurityException ex)
{
    Console.WriteLine($"Attack blocked: {ex.Message}");
}

3. Path Manipulation

Combine paths securely. The library ensures the result is still valid.

BoxedPath basePath = BoxedPath.Of(sandbox, "users");

// Safe combination: C:\Safe\Uploads\users\alice
BoxedPath userPath = BoxedPath.Combine(basePath, "alice"); 

// Malicious combination attempt
// THROWS SecurityException
BoxedPath hackAttempt = BoxedPath.Combine(basePath, "../../admin"); 

4. Performing File I/O

Crucial: Do not convert the BoxedPath to a string to use standard File methods directly, as that breaks the chain of trust. Instead, use the secure wrappers or the ValidateAndExpose() method at the very last moment.

// Check existence safely
if (BoxedFile.Exists(safePath)) 
{
    // Read text securely
    string content = BoxedFile.ReadAllText(safePath);
}

// Use FileInfo wrapper (Does NOT expose .FullName)
var fileInfo = new BoxedFileInfo(safePath);
long size = fileInfo.Length;

// Secure FileStream
using (var stream = new BoxedFileStream(safePath, FileMode.Open))
{
    // ... read/write ...
}
Option B: Explicit Exposure (Use with Caution)

If you must use an API that strictly requires a string path:

// ValidateAndExpose either relies on previous validation (for an absolute path) or validates the path.
// The raw physical path string is returned.
// Use this result IMMEDIATELY and do not store it.
string rawPath = safePath.ValidateAndExpose(); 

System.IO.File.Delete(rawPath); // Note that this is only for example purposes! Better use BoxedFile.Delete(safePath);

Advanced Configuration

You can control how symbolic links are handled.

// Default: Follows symlinks, but throws if the target is outside the sandbox.
var secureSandbox = PathSandbox.BoxRoot("/data", SandboxJailbreak.DISALLOW);

// Dangerous: Allows symlinks to point anywhere (relies on OS permissions).
var looseSandbox = PathSandbox.BoxRoot("/data", SandboxJailbreak.UNCHECKED_SYMLINKS);

To prevent infinite loops or Denial of Service (DoS) via "symlink bombs," you can limit the recursion depth.

// Limit to 20 link hops (Default is 5)
var deepSandbox = PathSandbox.BoxRoot("/data", SandboxJailbreak.DISALLOW, maxLinkFollows: 20);

Contributing

This project is an official OWASP contribution. Issues and Pull Requests are welcome on the GitHub Repository.

License

Licensed under the Apache License 2.0.

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 192 12/5/2025
1.1.0 279 12/5/2025 1.1.0 is deprecated because it has critical bugs.
1.0.0 416 11/19/2025