Crate strict_path

Crate strict_path 

Source
Expand description

§strict-path

Handle paths from external or unknown sources securely. Uses Rust’s type system to mathematically prove paths stay within defined boundaries—no escapes in any shape or form, symlinks included. API is minimal, restrictive, and explicit to prevent human and LLM API misuse.

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, config loading, shared system resources, file uploads to shared storage (admin panels, CMS)
  • 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 file uploads (SaaS per-user storage), 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 (with proc-canonicalize for Linux container realpath support), 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

Trade-off: Security is prioritized above performance. This crate verifies paths on disk and follows symlinks for validation. If your use case doesn’t involve symlinks and you need maximum performance, a lexical-only solution may be a better fit.

→ 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()
  • serdePathBoundary/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::strict_path::StrictOpenOptions;
pub use path::strict_path::StrictReadDir;
pub use validator::path_boundary::BoundaryReadDir;
pub use path::virtual_path::VirtualPath;
pub use path::virtual_path::VirtualReadDir;
pub use validator::virtual_root::VirtualRoot;
pub use validator::virtual_root::VirtualRootReadDir;

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.