{"id":30136482,"url":"https://github.com/dk26/jailed-path-rs","last_synced_at":"2025-08-10T23:09:12.231Z","repository":{"id":305023631,"uuid":"1020865350","full_name":"DK26/jailed-path-rs","owner":"DK26","description":"Prevent directory traversal with type-safe virtual path jails and safe symlinks","archived":false,"fork":false,"pushed_at":"2025-08-09T19:57:33.000Z","size":444,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-09T21:23:20.061Z","etag":null,"topics":["directory-traversal","file-security","filesystem-security","path-traversal-prevention","path-validation","rust","rust-crate","security","type-safety","web-security"],"latest_commit_sha":null,"homepage":"https://docs.rs/jailed-path","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DK26.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-07-16T14:07:55.000Z","updated_at":"2025-08-09T19:57:37.000Z","dependencies_parsed_at":"2025-07-18T00:41:40.632Z","dependency_job_id":"667d0a48-1a34-43ce-9995-8e904ec58c5a","html_url":"https://github.com/DK26/jailed-path-rs","commit_stats":null,"previous_names":["dk26/jailed-path-rs"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/DK26/jailed-path-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DK26%2Fjailed-path-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DK26%2Fjailed-path-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DK26%2Fjailed-path-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DK26%2Fjailed-path-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DK26","download_url":"https://codeload.github.com/DK26/jailed-path-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DK26%2Fjailed-path-rs/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269803855,"owners_count":24477658,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["directory-traversal","file-security","filesystem-security","path-traversal-prevention","path-validation","rust","rust-crate","security","type-safety","web-security"],"created_at":"2025-08-10T23:09:07.474Z","updated_at":"2025-08-10T23:09:12.214Z","avatar_url":"https://github.com/DK26.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# jailed-path\n\n[![Crates.io](https://img.shields.io/crates/v/jailed-path.svg)](https://crates.io/crates/jailed-path)\n[![Documentation](https://docs.rs/jailed-path/badge.svg)](https://docs.rs/jailed-path)\n[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](https://github.com/DK26/jailed-path-rs#license)\n[![CI](https://github.com/DK26/jailed-path-rs/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/DK26/jailed-path-rs/actions/workflows/ci.yml)\n[![Type-State Police](https://img.shields.io/badge/protected%20by-Type--State%20Police-blue.svg)](https://github.com/DK26/jailed-path-rs)\n\n**Prevent directory traversal with type-safe virtual path jails and safe symlinks**\n\n\u003e *Putting your paths in jail by the Type-State Police Department*  \n\u003e *because your LLM can't be trusted with security*\n\n`JailedPath` is a filesystem path **mathematically proven** to stay within directory boundaries. Unlike libraries that hope validation works, we mathematically prove it at compile time using Rust's type system. Two ways to create `JailedPath` instances: `try_jail()` for one-shot path validation, and `Jail::try_new()` + `jail.try_path()` for reusable jail instances. Both guarantee containment—even malicious input like `../../../etc/passwd` gets safely clamped.\n\n**Zero Learning Curve**: Two simple functions solve 99% of use cases. **Attack Impossibility**: Not just \"hard to bypass\" - actually impossible due to API design.\n\n```rust\nuse jailed_path::{Jail, JailedPath, try_jail};\n\n// ✅ SECURE - Guaranteed safe by construction\nfn serve_file(safe_path: \u0026JailedPath) -\u003e std::io::Result\u003cVec\u003cu8\u003e\u003e {\n    safe_path.read_bytes()  // Built-in safe operations\n}\n\n# std::fs::create_dir_all(\"users/alice_workspace/documents\")?;\n# std::fs::write(\"users/alice_workspace/documents/report.pdf\", b\"Alice's report\")?;\n\n// Main pattern: Reusable jail for multiple validations (most common)\nlet user_jail: Jail = Jail::try_new(\"users/alice_workspace\")?;\nlet safe_path: JailedPath = user_jail.try_path(\"documents/report.pdf\")?;\n\n// Alternative: One-shot validation (for occasional use)\nlet one_shot_path: JailedPath = try_jail(\"users/alice_workspace\", \"documents/report.pdf\")?;\n\n// Even attacks are neutralized:\nlet attack_path = user_jail.try_path(\"../../../etc/passwd\")?;\nassert!(attack_path.ends_with(\"users/alice_workspace\"));  // Attack contained!\n# std::fs::remove_dir_all(\"users\").ok();\n# Ok::\u003c(), Box\u003cdyn std::error::Error\u003e\u003e(())\n```\n\n## Key Features: Security-First Design\n\n🔒 **Security First**: API makes unsafe operations impossible, not just difficult  \n🏛️ **Mathematical Guarantees**: Rust's type system proves security at compile time  \n🛡️ **Zero Attack Surface**: No `Deref` to `Path`, no `AsRef\u003cPath\u003e`, validation cannot be bypassed  \n📁 **Built-in Safe Operations**: Direct file operations on jailed paths without exposing raw filesystem paths  \n👁️ **Virtual Root Display**: Clean user-facing paths that never leak filesystem structure  \n🎯 **Multi-Jail Safety**: Marker types prevent cross-jail contamination  \n📦 **Minimal Attack Surface**: Only one dependency - our auditable `soft-canonicalize` crate (handles non-existent paths unlike `std::fs::canonicalize`)  \n🔗 **Type-History Design**: Internal pattern ensures paths carry proof of validation stages  \n🧪 **Comprehensive Testing**: 100%+ test coverage with attack scenario simulation  \n🌍 **Cross-Platform**: Works on Windows, macOS, and Linux  \n🤖 **LLM-Friendly**: Documentation and APIs designed for both humans and AI systems to understand and use correctly  \n\n## The Problem: Every Path Is a Security Risk\n\n```rust\n// 🚨 DANGEROUS - This code looks innocent but has a critical vulnerability\nfn serve_file(path: \u0026str) -\u003e std::io::Result\u003cVec\u003cu8\u003e\u003e {\n    std::fs::read(format!(\"./public/{path}\"))  // ← Path traversal attack possible!\n}\n\n// Attacker sends: \"../../../etc/passwd\" \n// Your server happily serves: ./public/../../../etc/passwd → /etc/passwd 💀\n```\n\n**The brutal truth**: Manual path validation is error-prone and easy to bypass. Even security-conscious developers get it wrong.\n\n## See The Promise In Action: Detailed Examples\n\n```rust\nuse jailed_path::{try_jail, Jail, JailedPath};\n\n// ═══════════════════════════════════════════════════════════════════════════════\n// THE PROMISE HANDLES ATTACKS - Even escape attempts honor the containment promise:\n// ═══════════════════════════════════════════════════════════════════════════════\n\nlet escape_attempt = \"../../../etc/passwd\";\nlet attack_path: JailedPath = try_jail(\"users/alice_workspace\", escape_attempt)?;\n\n// Virtual display shows clamped path - the promise includes hiding real filesystem structure\nassert_eq!(attack_path.virtual_display(), \"/etc/passwd\");  // Clean display, but SAFELY clamped\n\n// ✅ The promise is verified: this path is actually contained within the jail\nassert!(attack_path.ends_with(\"users/alice_workspace\"));  // PROOF: Real path is inside the jail!\n\n// Option 2: Reusable jail with try_path()\nlet alice_home_jail = Jail::try_new(\"./users/alice_workspace\")?;\nlet vacation_photo_path: JailedPath = alice_home_jail.try_path(\"photos/vacation.jpg\")?;\nassert_eq!(vacation_photo_path.virtual_display(), \"/photos/vacation.jpg\");  // Promise: within alice's space!\n\nlet cross_user_attack_path: JailedPath = alice_home_jail.try_path(\"../bob_workspace/secrets.txt\")?;\nassert_eq!(cross_user_attack_path.virtual_display(), \"/bob_workspace/secrets.txt\");  // Clean display\nassert!(cross_user_attack_path.ends_with(\"users/alice_workspace\"));  // PROOF: Still within alice's jail!\n```\n\n**The revolutionary insight**: Every `JailedPath` you hold is a cryptographic-strength promise that has been mathematically verified by Rust's type system. You cannot forge this promise—there are no other constructors!\n\n## Understanding the Generic Marker System\n\nYou might have noticed something in the examples above. Let's explore the \"generic trap\" and why it's actually a feature:\n\n```rust\nuse jailed_path::Jail;\n\n// Simple approach - no type annotation needed\nlet static_files_jail = Jail::try_new(\"./static/css\")?;\nlet stylesheet_path = static_files_jail.try_path(\"bootstrap.css\")?;  // Type: JailedPath\u003c()\u003e\n\n// Or be explicit with the \"turbofish\" syntax  \nlet static_files_jail: Jail\u003c()\u003e = Jail::try_new(\"./static/css\")?;\nlet stylesheet_path: JailedPath\u003c()\u003e = static_files_jail.try_path(\"bootstrap.css\")?;\n```\n\n**\"Why the generic `\u003c()\u003e` parameter?\"** - This is Rust's way of saying \"no special marker.\" But the real power comes when you DO use markers...\n\n## The Power of Multiple Jails: Promises with Specific Identities\n\nReal applications have multiple directories. Here's where the promise system becomes even more powerful by adding **identity** to the containment promise:\n\n```rust\nuse jailed_path::{Jail, JailedPath};\n\n// Define semantic markers for different promise types\nstruct WebAssets;\nstruct UserUploads;\n\n// Create type-safe path jails that make specific promises\nlet cdn_assets_jail: Jail\u003cWebAssets\u003e = Jail::try_new(\"./cdn/assets\")?;\nlet user_uploads_jail: Jail\u003cUserUploads\u003e = Jail::try_new(\"./uploads\")?;\n\n// Get paths with specific promises\nlet css_bundle_path: JailedPath\u003cWebAssets\u003e = cdn_assets_jail.try_path(\"app.bundle.css\")?;\n// ↑ Promise: \"I am contained within ./cdn/assets AND I am a WebAssets path\"\n\nlet profile_pic_path: JailedPath\u003cUserUploads\u003e = user_uploads_jail.try_path(\"avatars/user123.png\")?;\n// ↑ Promise: \"I am contained within ./uploads AND I am a UserUploads path\"\n\n// Functions can require specific promise types\nfn serve_cdn_asset(asset: \u0026JailedPath\u003cWebAssets\u003e) -\u003e std::io::Result\u003cVec\u003cu8\u003e\u003e {\n    asset.read_bytes() // ✅ This function ONLY accepts the WebAssets promise\n}\n\n// The type system enforces promise contracts\nserve_cdn_asset(\u0026css_bundle_path)?;  // ✅ Correct promise type\n// serve_cdn_asset(\u0026profile_pic_path)?;  // ❌ Compile error! Wrong promise type!\n```\n\n**The magic**: The compiler mathematically guarantees that different promise types cannot be mixed up. A `JailedPath\u003cWebAssets\u003e` promises both containment AND identity—you can never accidentally serve a user upload as a CDN asset.\n\n## Even Single Jails Benefit from Semantic Markers\n\n```rust\nuse jailed_path::{Jail, JailedPath};\n\nstruct DocumentStorage;\n\nlet docs_jail: Jail\u003cDocumentStorage\u003e = Jail::try_new(\"./company_docs\")?;\n\nfn access_document(file: \u0026JailedPath\u003cDocumentStorage\u003e) -\u003e std::io::Result\u003cVec\u003cu8\u003e\u003e {\n    // The type signature makes it crystal clear what this function expects\n    file.read_bytes() // ✅ Safe built-in operation\n}\n```\n\nThe marker adds semantic meaning and prevents accidental misuse.\n\n## Safe File Operations: Why Direct Path Access Can Be Dangerous\n\nHere's a critical security insight: even with a `JailedPath`, getting a raw `\u0026Path` can be risky if misused:\n\n```rust\nuse jailed_path::Jail;\nuse std::path::Path;\n\nlet customer_data_jail = Jail::try_new(\"./customer_data\")?;\nlet invoice_path = customer_data_jail.try_path(\"invoices/2024/invoice-001.pdf\")?;\n\n// 🚨 DANGEROUS - A raw \u0026Path can be misused!\nlet raw_path: \u0026Path = invoice_path.as_ref();\nlet dangerous = raw_path.join(\"../../../etc/passwd\");  // Oops! Escaped the jail!\n```\n\n**The solution**: Use our built-in safe operations instead.\n\n## Built-in Safe File Operations\n\n```rust\nuse jailed_path::Jail;\n\nlet customer_uploads_jail = Jail::try_new(\"./customer_uploads\")?;\nlet contract_path = customer_uploads_jail.try_path(\"contracts/acme-corp-2024.pdf\")?;\n\n// ✅ SAFE - All operations stay within the jail automatically\ncontract_path.write_string(\"Contract updated with new terms\")?;\nlet content = contract_path.read_to_string()?;\nassert_eq!(content, \"Contract updated with new terms\");\n\n// Write operations - always safe\ncontract_path.write_bytes(b\"PDF binary data\")?;\nlet data = contract_path.read_bytes()?;\nassert_eq!(data, b\"PDF binary data\");\n\n// Directory operations - always safe  \nlet client_folder_path = customer_uploads_jail.try_path(\"new_client_folder\")?;\nclient_folder_path.create_dir_all()?;\nassert!(client_folder_path.exists());\n\n// Metadata operations - always safe\ncontract_path.write_string(\"Updated contract content\")?;\nlet metadata = contract_path.metadata()?;\nassert!(metadata.len() \u003e 0);\n```\n\n**No raw path access needed!** All operations are mathematically guaranteed to stay within the jail.\n\n## Virtual Root Display: Clean User-Facing Paths\n\n```rust\nuse jailed_path::Jail;\n\nlet saas_tenant_jail = Jail::try_new(\"./tenant_data/company_xyz\")?;\nlet report_path = saas_tenant_jail.try_path(\"reports/quarterly/2024-q1.xlsx\")?;\n\n// User sees clean, intuitive paths - never internal filesystem details\nassert_eq!(format!(\"{report_path}\"), \"/reports/quarterly/2024-q1.xlsx\");\n\n// The real path is hidden (and you shouldn't need it anyway!)\nassert_eq!(report_path.to_string_lossy(), \"./tenant_data/company_xyz/reports/quarterly/2024-q1.xlsx\");\n```\n\nThis prevents leaking internal filesystem structure in logs, error messages, or user interfaces.\n\n## Mathematical Security: Our Type-State Design\n\nThis crate uses a sophisticated \"Type-History\" design pattern internally. Every path carries mathematical proof of what validation stages it has passed through:\n\n```rust\n// Internal type-state progression (you don't see this, but it's happening):\n// Raw → Clamped → JoinedJail → Canonicalized → BoundaryChecked → JailedPath\n```\n\nOur comprehensive test coverage (100%+) and LLM-friendly documentation ensure that every security property is verified mathematically, not just hoped for.\n\n## Complete Attack Immunity Demonstration\n\n```rust\nuse jailed_path::Jail;\n\nlet web_server_jail: Jail = Jail::try_new(\"./www/htdocs\")?;\n\n// ✅ Normal paths work as expected - legitimate web requests\nlet homepage_path = web_server_jail.try_path(\"index.html\")?;\nassert_eq!(homepage_path.to_string_lossy(), \"./www/htdocs/index.html\");\nassert_eq!(format!(\"{homepage_path}\"), \"/index.html\");\n\nlet stylesheet_path = web_server_jail.try_path(\"css/main.css\")?;\nassert_eq!(stylesheet_path.to_string_lossy(), \"./www/htdocs/css/main.css\");\nassert_eq!(stylesheet_path.virtual_display(), \"/css/main.css\");\n\n// 🛡️ ATTACK ATTEMPTS ARE MATHEMATICALLY IMPOSSIBLE TO SUCCEED\nlet shadow_attack_path = web_server_jail.try_path(\"/etc/shadow\")?;\nassert_eq!(shadow_attack_path.to_string_lossy(), \"./www/htdocs/etc/shadow\");  // Harmless!\nassert_eq!(shadow_attack_path.virtual_display(), \"/etc/shadow\");  // In jail\n\nlet config_attack_path = web_server_jail.try_path(\"../config.ini\")?;\nassert_eq!(config_attack_path.to_string_lossy(), \"./www/htdocs\");  // Jail root\nassert_eq!(config_attack_path.virtual_display(), \"/\");\n\nlet passwd_attack_path = web_server_jail.try_path(\"../../../etc/passwd\")?;\nassert_eq!(passwd_attack_path.to_string_lossy(), \"./www/htdocs\");  // Jail root\nassert_eq!(passwd_attack_path.virtual_display(), \"/\");\n\n// 🔒 The attacker CANNOT access the real /etc/passwd - it's mathematically impossible!\nassert!(config_attack_path.ends_with(\"htdocs\"));  // PROOF: Clamped to jail root\nassert!(passwd_attack_path.ends_with(\"htdocs\"));  // PROOF: Clamped to jail root\n```\n\n## Advanced: Real-World Integration Examples\n\n### Axum Web Server with Multi-Tenant File Serving\n\n```rust\nuse axum::{extract::Path, response::Response, http::StatusCode};\nuse jailed_path::Jail;\n\nstruct StaticAssets;\nstruct UserContent;\n\n// Set up path jails for different content types\nlet static_jail = Jail::\u003cStaticAssets\u003e::try_new(\"./public\")?;\nlet content_jail = Jail::\u003cUserContent\u003e::try_new(\"./user_content\")?;\n\n// Axum route handlers with compile-time path safety\nasync fn serve_static(Path(file_path): Path\u003cString\u003e) -\u003e Result\u003cResponse, StatusCode\u003e {\n    let safe_path = static_jail.try_path(\u0026file_path)\n        .map_err(|_| StatusCode::NOT_FOUND)?;\n    \n    if !safe_path.exists() {\n        return Err(StatusCode::NOT_FOUND);\n    }\n    \n    let content = safe_path.read_bytes()\n        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;\n    \n    Ok(Response::new(content.into()))\n}\n\nasync fn serve_user_content(Path((tenant_id, file_path)): Path\u003c(String, String)\u003e) -\u003e Result\u003cResponse, StatusCode\u003e {\n    let tenant_path = format!(\"{tenant_id}/{file_path}\");\n    let safe_path = content_jail.try_path(\u0026tenant_path)\n        .map_err(|_| StatusCode::FORBIDDEN)?;  // Auto-blocks traversal attacks\n        \n    // Rest of handler logic...\n    Ok(Response::new(\"content\".into()))\n}\n```\n    \n    Ok(Response::new(content.into()))\n}\n\nasync fn serve_user_content(Path((tenant_id, file_path)): Path\u003c(String, String)\u003e) -\u003e Result\u003cResponse, StatusCode\u003e {\n    let tenant_path = format!(\"{tenant_id}/{file_path}\");\n    let safe_path = content_jail.try_path(\u0026tenant_path)\n        .map_err(|_| StatusCode::FORBIDDEN)?;  // Auto-blocks traversal attacks\n        \n    // Rest of handler logic...\n    Ok(Response::new(\"content\".into()))\n}\n```\n\n### Cloud Storage Sync Service\n\n```rust\nuse jailed_path::Jail;\n\nstruct LocalCache;\nstruct RemoteSync;\n\n// Automation service that syncs cloud storage locally\nlet cache_jail = Jail::\u003cLocalCache\u003e::try_new(\"./cache/downloads\")?;\nlet sync_jail = Jail::\u003cRemoteSync\u003e::try_new(\"./sync_staging\")?;\n\nasync fn download_cloud_file(cloud_path: \u0026str, local_name: \u0026str) -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    // Ensure downloaded files stay in designated cache area\n    let local_path = cache_jail.try_path(local_name)?;\n    \n    // Download from cloud service (S3, GCS, etc.)\n    let cloud_data = fetch_from_cloud(cloud_path).await?;\n    \n    // Safe write - guaranteed to stay in cache jail\n    local_path.write_bytes(\u0026cloud_data)?;\n    \n    println!(\"Downloaded to: {}\", local_path.display());  // Clean path display\n    Ok(())\n}\n\nasync fn sync_to_staging(cached_file: \u0026str) -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let cache_path = cache_jail.try_path(cached_file)?;\n    let staging_path = sync_jail.try_path(cached_file)?;\n    \n    // Move between jails safely\n    let data = cache_path.read_bytes()?;\n    staging_path.write_bytes(\u0026data)?;\n    \n    Ok(())\n}\n```\n\n### Resource Bundling Tool\n\n```rust\nuse jailed_path::Jail;\n\nstruct SourceAssets;\nstruct BuildOutput;\n\n// Build tool that processes resources from multiple sources\nlet source_jail = Jail::\u003cSourceAssets\u003e::try_new(\"./src/assets\")?;\nlet build_jail = Jail::\u003cBuildOutput\u003e::try_new(\"./dist\")?;\n\nfn bundle_css_files(css_files: \u0026[\u0026str]) -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let mut combined_css = String::new();\n    \n    for css_file in css_files {\n        // Safe access to source files - no traversal possible\n        let source_path = source_jail.try_path(css_file)?;\n        let css_content = source_path.read_to_string()?;\n        combined_css.push_str(\u0026css_content);\n        combined_css.push('\\n');\n    }\n    \n    // Safe output to build directory\n    let bundle_path = build_jail.try_path(\"bundle.css\")?;\n    bundle_path.write_string(\u0026combined_css)?;\n    \n    println!(\"CSS bundle created: {}\", bundle_path.display());\n    Ok(())\n}\n\nfn process_image_assets(image_dir: \u0026str) -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let source_dir = source_jail.try_path(image_dir)?;\n    \n    // Process all images in the source directory\n    for entry in std::fs::read_dir(source_dir.as_ref())? {\n        let entry = entry?;\n        let filename = entry.file_name().to_string_lossy().to_string();\n        \n        if filename.ends_with(\".png\") || filename.ends_with(\".jpg\") {\n            let source_img = source_jail.try_path(\u0026format!(\"{image_dir}/{filename}\"))?;\n            let output_img = build_jail.try_path(\u0026format!(\"images/{filename}\"))?;\n            \n            // Safe image processing - both paths are jailed\n            let img_data = source_img.read_bytes()?;\n            // ... image optimization logic ...\n            output_img.write_bytes(\u0026img_data)?;\n        }\n    }\n    \n    Ok(())\n}\n```\n\n## For One-Shot Validation: Banking Application\n\nSometimes you need quick path validation without the overhead of creating a validator:\n\n```rust\nuse jailed_path::try_jail;\n\n// Banking application handling customer statements\nfn generate_customer_statement(customer_id: \u0026str, year: \u0026str) -\u003e Result\u003cString, Box\u003cdyn std::error::Error\u003e\u003e {\n    // Quick validation: keep customer statements within their secure directory\n    let statement_path = try_jail(\"./bank_statements\", format!(\"customer_{}/statements/{}.pdf\", customer_id, year))?;\n    \n    if !statement_path.exists() {\n        return Err(\"Statement not found\".into());\n    }\n    \n    Ok(format!(\"Statement available at: {}\", statement_path.display()))\n}\n\n// Example usage - secure by design\nmatch generate_customer_statement(\"12345\", \"2023\") {\n    Ok(location) =\u003e println!(\"{}\", location),\n    Err(e) =\u003e println!(\"Access denied: {}\", e), // Handles traversal attacks automatically\n}\n\n// What happens with attacks:\n// generate_customer_statement(\"../../../etc\", \"passwd\") -\u003e Error: path escapes jail\n// generate_customer_statement(\"12345\", \"../other_customer\") -\u003e Error: path escapes jail\n```\n\n### With External Crates (Portable Paths)\n\n```rust\nuse app_path::app_path;\nuse jailed_path::Jail;\n\nstruct ConfigFiles;\nstruct DataFiles;\n\n// Portable paths relative to your executable\nlet config: Jail\u003cConfigFiles\u003e = Jail::try_new(app_path!(\"config\"))?;\nlet data: Jail\u003cDataFiles\u003e = Jail::try_new(app_path!(\"data\"))?;\n\n// Type-safe, attack-proof file access\nlet settings_path = config.try_path(\"app.toml\")?;\nlet database_path = data.try_path(\"users.db\")?;\n```\n\n## Installation\n\nAdd this to your `Cargo.toml`:\n\n```toml\n[dependencies]\njailed-path = \"0.0.4\"\n```\n\n## License\n\nLicensed under either of:\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))\n- MIT License ([LICENSE-MIT](LICENSE-MIT))\n\nat your option.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdk26%2Fjailed-path-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdk26%2Fjailed-path-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdk26%2Fjailed-path-rs/lists"}