Skip to content

Prevent directory traversal attacks with compile-time path boundary enforcement. Choose StrictPath to detect or reject escape attempts, or VirtualPath to contain and isolate them. Defending against 19+ real-world CVEs

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT
Notifications You must be signed in to change notification settings

DK26/strict-path-rs

strict-path

Crates.io Documentation License: MIT OR Apache-2.0 CI Security Audit Type-State Police

πŸ“š Complete Guide & Examples | πŸ“– API Docs | 🧭 Choosing Canonicalized vs Lexical Solution

Prevent directory traversal attacks with compile-time path boundary enforcement. File paths are mathematically proven to stay within designated boundaries. Choose StrictPath to detect and reject escape attempts, or VirtualPath to contain and isolate them. Built on battle-tested canonicalization defending against 19+ real-world CVEs including symlinks, Windows 8.3 short names, and encoding tricks.


⚑ Get Secure in 30 Seconds

[dependencies]
strict-path = "0.1.0-rc.1"
use strict_path::StrictPath;

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

let file = StrictPath::with_boundary("/var/app/downloads")?
    .strict_join(&untrusted_user_input)?; // Validates untrusted input - attack blocked!

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

That's it. Simple, safe, and path traversal attacks are blocked automatically.

Analogy: StrictPath is to paths what prepared statements are to SQL. The boundary is your prepared statement (the policy). The untrusted segment is the parameter (validated safely). Injection attempts become inert.

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.
  • VirtualPath: When you want to provide path freedom under isolation.

See the detailed decision matrix below: StrictPath vs VirtualPath: When to Use What.

For reusable boundaries (e.g., passing to functions):

use strict_path::PathBoundary;

fn extract_archive(
    extraction_dir: PathBoundary,
    entries: Vec<(String, Vec<u8>)>) -> std::io::Result<()> {
    
    for (entry_name, data) in entries {
        let safe_file = extraction_dir.strict_join(&entry_name)?;
        safe_file.create_parent_dir_all()?;
        safe_file.write(&data)?;
    }
    Ok(())
}

πŸ“– New to strict-path? Start with the Tutorial: Stage 1 - The Basic Promise β†’

Note: Our doc comments and LLM_API_REFERENCE.md are designed for LLMs with function callingβ€”enabling AI agents to use this crate safely and correctly for file and path operations.

πŸ€– LLM agent prompt (copy/paste)

Fetch and follow this reference (single source of truth):
https://github.com/DK26/strict-path-rs/blob/main/LLM_API_REFERENCE.md

Context7 Style

Fetch and follow this reference (single source of truth):
https://github.com/DK26/strict-path-rs/blob/main/LLM_USER.md

πŸš€ More Real-World Examples

Archive Extraction (Zip Slip Prevention)

use strict_path::PathBoundary;

fn extract_archive(
    extraction_dir: PathBoundary,
    archive_entries: impl IntoIterator<Item=(String, Vec<u8>)>) -> std::io::Result<()> {

    for (entry_path, data) in archive_entries {
        // Malicious paths like "../../../etc/passwd" β†’ Err(PathEscapesBoundary)
        let safe_file = extraction_dir.strict_join(&entry_path)?;
        safe_file.create_parent_dir_all()?;
        safe_file.write(&data)?;
    }
    Ok(())
}

Prevents CVE-2018-1000178 (Zip Slip) automatically.

Multi-Tenant Isolation

use strict_path::VirtualRoot;

fn handle_file_request(tenant_id: &str, requested_path: &str) -> std::io::Result<Vec<u8>> {
    let tenant_root = VirtualRoot::try_new_create(format!("./tenants/{tenant_id}"))?;
    
    // "../../other_tenant/secrets.txt" β†’ clamped to "/other_tenant/secrets.txt" in THIS tenant
    let user_file = tenant_root.virtual_join(requested_path)?;
    user_file.read()
}

πŸ›‘οΈ Complete Security Solution

strict-path handles edge cases you'd never think to check:

  1. πŸ”§ soft-canonicalize foundation: Battle-tested against 19+ real-world path CVEs
  2. πŸ”— Full canonicalization: Resolves symlinks, junctions, ./.. components, handles race conditions
  3. 🚫 Advanced attacks: Catches Windows 8.3 short names (PROGRA~1), UNC paths, NTFS ADS, encoding tricks
  4. πŸ“ Compile-time proof: Rust's type system enforces path boundaries
  5. πŸ‘οΈ Explicit operations: Method names like strict_join() make security visible in code review
  6. πŸ›‘οΈ Built-in I/O: Complete filesystem API without needing .interop_path()
  7. πŸ€– LLM-aware: Built for untrusted AI-generated paths and modern threat models
  8. ⚑ Dual modes: Strict (detect & reject) or Virtual (clamp & contain)

Real attacks we handle automatically:

  • Windows 8.3 short names (PROGRA~1 β†’ Program Files)
  • NTFS Alternate Data Streams (file.txt:hidden:$DATA)
  • Unicode normalization bypasses (..βˆ•..βˆ•etcβˆ•passwd)
  • Symlink time-bombs (TOCTOU race conditions)
  • Mixed separators (../\../etc/passwd)
  • UNC path tricks (\\?\C:\..\..\etc\passwd)

Recently Addressed CVEs:

  • CVE-2025-8088 (WinRAR): NTFS Alternate Data Stream traversal
  • CVE-2022-21658: Race condition protection (TOCTOU)
  • CVE-2019-9855, CVE-2020-12279, CVE-2017-17793: Windows 8.3 short names

πŸ“– Read our complete security methodology β†’ | πŸ“š Built-in I/O Methods β†’

🎯 StrictPath vs VirtualPath: When to Use What

The Core Question: Are path escapes attacks or expected behavior?

Mode Philosophy Returns on Escape Use When
StrictPath "Detect & reject escape attempts" Err(PathEscapesBoundary) Archive extraction, file uploads
VirtualPath "Contain & clamp escape attempts" Clamped within virtual root Multi-tenant apps, malware sandboxes

Choose StrictPath (90% of cases):

  • Archive extraction, file uploads, config loading
  • LLM/AI agent file operations
  • Shared system resources (logs, cache, assets)
  • Any case where escapes = attacks

Choose VirtualPath (10% of cases):

  • Multi-tenant isolation (per-user filesystem views)
  • Malware analysis sandboxes
  • Container-like plugins
  • Any case where escapes = expected but must be controlled

πŸ“– Complete Decision Matrix β†’ | πŸ“š More Examples β†’


🧠 Compile-Time Safety with Markers

StrictPath<Marker> enables domain separation and authorization at compile time:

struct UserFiles;
struct SystemFiles;

fn process_user(f: &StrictPath<UserFiles>) -> Vec<u8> { f.read().unwrap() }

let user_boundary = PathBoundary::<UserFiles>::try_new_create("user_data")?;
let sys_boundary = PathBoundary::<SystemFiles>::try_new_create("system")?;

let user_input = get_filename_from_request();
let user_file = user_boundary.strict_join(user_input)?;
// process_user(&sys_file)?; // ❌ Compile error - wrong marker type!

πŸ“– Complete Marker Tutorial β†’ - Authorization patterns, permission matrices, change_marker() usage


⚠️ Security Coverage

Protects Against (99% of attacks):

  • Path traversal (../../../etc/passwd)
  • Symlink/junction escapes
  • Archive attacks (Zip slip - CVE-2018-1000178)
  • Encoding tricks (Unicode, null bytes)
  • Windows attacks (8.3 names, UNC, NTFS ADS)
  • Race conditions (TOCTOU - CVE-2022-21658)

What This Is / Is NOT:

  • βœ… Follows filesystem links and resolves paths properly
  • βœ… Works with your permissions (doesn't replace them)
  • ❌ Not just string checking (handles symlinks, Windows quirks)
  • ❌ Not an OS-level sandbox (path-level security)
  • ❌ Not a replacement for proper auth (validates paths, not users)

vs. soft-canonicalize:

  • soft-canonicalize = low-level path resolution engine (returns PathBuf)
  • strict-path = high-level security API (returns StrictPath<Marker> with compile-time guarantees)

πŸ“– Security Methodology β†’ | πŸ“š Anti-Patterns Guide β†’


πŸ”Œ Ecosystem Integration

Compose with standard Rust crates for complete solutions:

Integration Purpose Guide
tempfile Secure temp directories Guide
dirs OS standard directories Guide
app-path Application directories Guide
serde Safe deserialization Guide
Axum Web server extractors Tutorial
Archives ZIP/TAR extraction Guide

πŸ“š Complete Integration Guide β†’


πŸ“š Learn More


πŸ“„ License

MIT OR Apache-2.0

About

Prevent directory traversal attacks with compile-time path boundary enforcement. Choose StrictPath to detect or reject escape attempts, or VirtualPath to contain and isolate them. Defending against 19+ real-world CVEs

Topics

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Contributing

Stars

Watchers

Forks