1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//! # strict-path
//!
//! **Secure path handling for untrusted input.** Prevents directory traversal, symlink escapes,
//! and [19+ real-world CVE attack patterns](https://dk26.github.io/strict-path-rs/security_methodology.html).
//! If a `StrictPath` value exists, the path is proven to be inside its boundary — by construction,
//! not by string checks.
//!
//! 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](https://dk26.github.io/strict-path-rs/)** | 📖 **[API Reference](https://docs.rs/strict-path)**
//!
//! ## Quick Start
//!
//! ```rust
//! # use strict_path::StrictPath;
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let temp = tempfile::tempdir()?;
//! # let request = std::collections::HashMap::from([("file", "report.pdf")]);
//! # std::fs::write(temp.path().join("report.pdf"), b"file contents")?;
//! // 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
//! # let _ = &contents; // send_response(contents);
//! # Ok(()) }
//! ```
//!
//! ## 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](https://dk26.github.io/strict-path-rs/security_methodology.html)**
//!
//! ## 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](https://dk26.github.io/strict-path-rs/best_practices.html)**
//!
//! ## Type-System Guarantees
//!
//! Use marker types to encode policy directly in your APIs:
//!
//! ```rust
//! # use strict_path::{PathBoundary, StrictPath};
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! struct PublicAssets;
//! struct UserUploads;
//!
//! # std::fs::create_dir_all("./assets")?;
//! # std::fs::create_dir_all("./uploads")?;
//! 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)
//! # std::fs::remove_dir_all("./assets").ok();
//! # std::fs::remove_dir_all("./uploads").ok();
//! # Ok(()) }
//! ```
//!
//! ## Security Foundation
//!
//! Built on [`soft-canonicalize`](https://crates.io/crates/soft-canonicalize) (with
//! [`proc-canonicalize`](https://crates.io/crates/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, CVE-2017-17793** (Windows 8.3 short names)
//! - Path traversal, symlink attacks, Unicode normalization bypasses, null byte injection, 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.
//!
//! > **Scope:** Validation happens at join-time (canonicalization + boundary check). Filesystem
//! > changes between validation and I/O are outside scope (TOCTOU) — the same limitation as SQL
//! > prepared statements, which prevent injection but don't protect against concurrent schema changes.
//!
//! **[→ Read attack surface analysis](https://dk26.github.io/strict-path-rs/security_methodology.html#12-coverage-what-we-protect-against)**
//!
//! ## Interop with External APIs
//!
//! Use `.interop_path()` to pass paths to external APIs expecting `AsRef<Path>`:
//!
//! ```rust
//! # use strict_path::PathBoundary;
//! # fn external_api<P: AsRef<std::path::Path>>(_p: P) {}
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! 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();
//! # let root_cleanup: strict_path::StrictPath = strict_path::StrictPath::with_boundary("./safe")?;
//! # root_cleanup.remove_dir_all().ok();
//! # Ok(()) }
//! ```
//!
//! **[→ Read the anti-patterns guide](https://dk26.github.io/strict-path-rs/anti_patterns.html)**
//!
//! ## 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](https://dk26.github.io/strict-path-rs/anti_patterns.html)**
//!
//! ## 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](https://dk26.github.io/strict-path-rs/ecosystem_integration.html)**
//!
//! **[→ Read the getting started guide](https://dk26.github.io/strict-path-rs/getting_started.html)**
//!
//! ## Additional Resources
//!
//! - **[LLM Context (Full)](https://github.com/DK26/strict-path-rs/blob/main/LLM_CONTEXT_FULL.md)** —
//! Concise, copy-pastable reference optimized for AI assistants
//! - **[LLM Context (Context7)](https://github.com/DK26/strict-path-rs/blob/main/LLM_CONTEXT.md)** —
//! Context7-style usage guide for AI agents
//! - **[Complete Guide](https://dk26.github.io/strict-path-rs/)** — Comprehensive documentation with examples
//! - **[API Reference](https://docs.rs/strict-path)** — Full type and method documentation
//! - **[Repository](https://github.com/DK26/strict-path-rs)** — Source code and issue tracker
// Public exports
pub use StrictPathError;
pub use StrictPath;
pub use PathBoundary;
// Iterator exports
pub use StrictOpenOptions;
pub use StrictReadDir;
pub use BoundaryReadDir;
pub use VirtualPath;
pub use VirtualReadDir;
pub use VirtualRoot;
pub use VirtualRootReadDir;
/// Result type alias for this crate's operations.
pub type Result<T> = Result;