Crate strict_path

Crate strict_path 

Source
Expand description

Β§strict-path

Strictly enforce path boundaries to prevent directory traversal attacks.

This crate performs full normalization/canonicalization and boundary enforcement with:

  • Safe symlink/junction handling (including cycle detection)
  • Windows-specific quirks (8.3 short names, UNC and verbatim prefixes, ADS)
  • Robust Unicode normalization and mixed-separator handling across platforms
  • Canonicalized path proofs encoded in the type system

If a StrictPath<Marker> value exists, it is already proven to be inside its designated boundary by construction β€” not by best-effort string checks.

πŸ“š Complete Guide & Examples | πŸ“– API Reference

Β§Quick Start

// GET /download?file=report.pdf
let user_input = request.get("file").unwrap(); // Untrusted: "report.pdf" or "../../etc/passwd"
let untrusted_user_input = user_input.to_string();

let file: StrictPath = StrictPath::with_boundary(temp.path())?
    .strict_join(&untrusted_user_input)?; // Validates untrusted input - attack blocked!

let contents = file.read()?; // Built-in safe I/O

Β§Core Types

  • StrictPath β€” The fundamental security primitive. Every StrictPath is mathematically proven to be within its designated boundary via canonicalization and type-level guarantees.
  • PathBoundary β€” Creates and validates StrictPath instances from external input.
  • VirtualPath (feature virtual-path) β€” Extends StrictPath with user-friendly virtual root semantics (treating the boundary as β€œ/”).
  • VirtualRoot (feature virtual-path) β€” Creates VirtualPath instances with containment semantics.

β†’ Read the security methodology

Β§Which Type Should I Use?

Path/PathBuf (std) β€” When the path comes from a safe source within your control, not external input.

StrictPath β€” When you want to restrict paths to a specific boundary and error if they escape.

  • Use for: Archive extraction, file uploads, config loading, shared system resources
  • Behavior: Returns Err(PathEscapesBoundary) on escape attempts (detect attacks)
  • Coverage: 90% of use cases

VirtualPath (feature virtual-path) β€” When you want to provide path freedom under isolation.

  • Use for: Multi-tenant systems, malware sandboxes, security research, per-user filesystem views
  • Behavior: Silently clamps/redirects escapes within virtual boundary (contain behavior)
  • Coverage: 10% of use cases

β†’ Read the detailed comparison

Β§Type-System Guarantees

Use marker types to encode policy directly in your APIs:

struct PublicAssets;
struct UserUploads;

let assets = PathBoundary::<PublicAssets>::try_new("./assets")?;
let uploads = PathBoundary::<UserUploads>::try_new("./uploads")?;

// User input from request parameters, form data, database, etc.
let requested_css = "style.css";      // From request: /static/style.css
let uploaded_avatar = "avatar.jpg";   // From form: <input type="file">

let css: StrictPath<PublicAssets> = assets.strict_join(requested_css)?;
let avatar: StrictPath<UserUploads> = uploads.strict_join(uploaded_avatar)?;

fn serve_public_asset(file: &StrictPath<PublicAssets>) { /* ... */ }

serve_public_asset(&css);       // βœ… OK
// serve_public_asset(&avatar); // ❌ Compile error (wrong marker)

Β§Security Foundation

Built on soft-canonicalize, this crate protects against:

  • CVE-2025-8088 (NTFS ADS path traversal)
  • CVE-2022-21658 (TOCTOU attacks)
  • CVE-2019-9855, CVE-2020-12279 (Windows 8.3 short names)
  • Path traversal, symlink attacks, Unicode normalization bypasses, race conditions

β†’ Read attack surface analysis

Β§Interop with External APIs

Use .interop_path() to pass paths to external APIs expecting AsRef<Path>:

let restriction: PathBoundary = PathBoundary::try_new_create("./safe")?;

// User input from CLI args, API request, config file, etc.
let user_input = "file.txt";
let jp = restriction.strict_join(user_input)?;

// βœ… Preferred: borrow as &OsStr (implements AsRef<Path>)
external_api(jp.interop_path());

// Escape hatches (use sparingly):
let owned: std::path::PathBuf = jp.clone().unstrict();

β†’ Read the anti-patterns guide

Β§Critical Anti-Patterns

  • NEVER wrap .interop_path() in Path::new() or PathBuf::from() β€” defeats all security
  • NEVER use std path operations on untrusted input β€” use .strict_join(), not Path::new().join()
  • Use .interop_path() directly for external APIs β€” it’s already AsRef<Path>, no wrapping needed
  • Use proper display methods β€” .strictpath_display() not .interop_path().to_string_lossy()

Note: .interop_path() returns &OsStr (which is AsRef<Path>). After .unstrict() (explicit escape hatch), you own a PathBuf and can do whatever you need.

β†’ See full anti-patterns list

Β§Feature Flags

  • virtual-path β€” Enables VirtualRoot/VirtualPath for containment scenarios
  • junctions (Windows) β€” Built-in NTFS junction helpers for strict/virtual paths

Β§Ecosystem Integration

Use ecosystem crates directly with PathBoundary for maximum flexibility:

  • tempfile β€” RAII temporary directories via tempfile::tempdir() β†’ PathBoundary::try_new()
  • dirs β€” OS standard directories via dirs::config_dir() β†’ PathBoundary::try_new_create()
  • app-path β€” Portable app paths via AppPath::with("subdir") β†’ PathBoundary::try_new_create()
  • serde β€” PathBoundary/VirtualRoot implement FromStr for automatic deserialization

β†’ See Ecosystem Integration Guide

β†’ Read the getting started guide

Β§Additional Resources

Re-exportsΒ§

pub use error::StrictPathError;
pub use path::strict_path::StrictPath;
pub use validator::path_boundary::PathBoundary;
pub use path::virtual_path::VirtualPath;
pub use validator::virtual_root::VirtualRoot;

ModulesΒ§

error
SUMMARY: Define error types and helpers for boundary creation and strict/virtual path validation.
path
validator

Type AliasesΒ§

Result
Result type alias for this crate’s operations.