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
//! A zero-dependency filesystem sandbox for Rust.
//!
//! Restricts paths to a root directory, preventing traversal attacks
//! while supporting files that don't exist yet.
//!
//! # Quick Start
//!
//! For one-off validation, use the [`join`] function:
//!
//! ```no_run
//! let safe_path = path_jail::join("/var/uploads", "user/file.txt")?;
//! std::fs::write(&safe_path, b"hello")?;
//! # Ok::<(), path_jail::JailError>(())
//! ```
//!
//! For validating multiple paths, create a [`Jail`] and reuse it:
//!
//! ```no_run
//! use path_jail::Jail;
//!
//! let jail = Jail::new("/var/uploads")?;
//! let path1 = jail.join("report.pdf")?;
//! let path2 = jail.join("data.csv")?;
//! # Ok::<(), path_jail::JailError>(())
//! ```
//!
//! # TOCTOU-Safe File Operations (guard API)
//!
//! Enable the `guard` feature for kernel-enforced containment via
//! `openat2(RESOLVE_BENEATH)` on Linux 5.6+:
//!
//! ```toml
//! [dependencies]
//! path_jail = { version = "0.4", features = ["guard"] }
//! ```
//!
//! ```no_run
//! # #[cfg(all(feature = "guard", unix))] {
//! use path_jail::guard::{FdJail, OpenOptions};
//!
//! let jail = FdJail::new("/var/uploads")?;
//! let mut gf = jail.open("report.pdf", OpenOptions::new().read(true))?;
//! if gf.has_hard_links() {
//! // Enforce hard-link policy here
//! }
//! use std::io::Read;
//! let mut buf = Vec::new();
//! gf.read_to_end(&mut buf)?;
//! # }
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! # Type-Safe Paths
//!
//! For compile-time guarantees, use [`JailedPath`]:
//!
//! ```no_run
//! use path_jail::{Jail, JailedPath};
//!
//! fn save_upload(path: JailedPath, data: &[u8]) -> std::io::Result<()> {
//! // path is guaranteed to be inside the jail
//! std::fs::write(&path, data)
//! }
//!
//! let jail = Jail::new("/var/uploads")?;
//! let path = jail.join_typed("report.pdf")?;
//! save_upload(path, b"data")?;
//! # Ok::<(), path_jail::JailError>(())
//! ```
//!
//! # Security
//!
//! This crate blocks:
//! - Path traversal (`../../etc/passwd`)
//! - Symlink escapes (symlinks pointing outside the jail)
//! - Absolute path injection (`/etc/passwd`)
//! - Null byte injection (`file\x00.txt`)
//! - Broken symlinks (cannot verify target)
//!
//! With `guard`: all of the above plus TOCTOU races, magic links
//! (`/proc/self/fd`), and intermediate directory attacks (Linux 5.6+).
//!
//! See [`Jail`] and [`guard::FdJail`] for details.
// `secure-open` is Unix-only (uses `OpenOptionsExt::custom_flags`).
// On Windows, enabling the feature is a no-op rather than a build error.
// openat2 wrapper is only compiled for Linux architectures that have a
// raw-asm syscall shim (x86_64 and aarch64). Other Linux arches and all
// non-Linux platforms fall back to the O_NOFOLLOW path in guard/fd_jail.rs.
pub
// `guard` is Unix-only — the implementation uses `OwnedFd`, `MetadataExt`,
// `OpenOptionsExt::custom_flags`, etc., which only exist on `cfg(unix)`.
// Windows is explicitly out of scope (see SECURITY.md). Compiling
// `--features guard` on Windows is a no-op rather than a build failure.
use ;
pub use JailError;
pub use Jail;
pub use JailedPath;
pub use JailedFile;
/// Validate a path in one shot.
///
/// This is a convenience wrapper around [`Jail::new`] and [`Jail::join`].
/// For validating multiple paths against the same root, create a [`Jail`]
/// and reuse it for better performance.
///
/// # Arguments
///
/// * `root` : The jail root directory (must exist and be a directory)
/// * `path` : A relative path to validate and join to the root
///
/// # Returns
///
/// The canonicalized safe path, or an error if:
/// - The root doesn't exist or isn't a directory
/// - The path would escape the jail
/// - The path is absolute
///
/// # Example
///
/// ```no_run
/// // Validate user input before saving a file
/// # let user_input = "report.pdf";
/// # let data = b"contents";
/// let safe = path_jail::join("/var/uploads", user_input)?;
/// std::fs::write(&safe, data)?;
/// # Ok::<(), path_jail::JailError>(())
/// ```