Skip to main content

strict_path/path/
mod.rs

1pub mod strict_path;
2
3#[cfg(feature = "virtual-path")]
4pub mod virtual_path;
5
6#[cfg(test)]
7mod tests;
8
9use crate::StrictPathError;
10use std::ffi::OsStr;
11use std::path::PathBuf;
12
13/// Reject extensions that would panic inside `Path::with_extension`.
14///
15/// `Path::with_extension` has a runtime panic when the extension contains a path
16/// separator (`/`, and also `\` on Windows). When the extension comes from
17/// untrusted input, that turns into a DoS primitive. We translate the guard
18/// into a normal `Err` before calling `with_extension`, so callers always see a
19/// `Result` instead of an unwind.
20pub(crate) fn validate_extension(
21    extension: &OsStr,
22    context_path: &std::path::Path,
23) -> Result<(), StrictPathError> {
24    for &byte in extension.as_encoded_bytes() {
25        // `is_separator` takes a char; these two bytes cover every byte value
26        // `std::path::is_separator` would accept on the respective platforms.
27        if byte == b'/' {
28            return Err(StrictPathError::path_resolution_error(
29                context_path.to_path_buf(),
30                std::io::Error::new(
31                    std::io::ErrorKind::InvalidInput,
32                    "extension must not contain path separators",
33                ),
34            ));
35        }
36        #[cfg(windows)]
37        if byte == b'\\' {
38            return Err(StrictPathError::path_resolution_error(
39                context_path.to_path_buf(),
40                std::io::Error::new(
41                    std::io::ErrorKind::InvalidInput,
42                    "extension must not contain path separators",
43                ),
44            ));
45        }
46        if byte == 0 {
47            return Err(StrictPathError::path_resolution_error(
48                context_path.to_path_buf(),
49                std::io::Error::new(
50                    std::io::ErrorKind::InvalidInput,
51                    "extension must not contain NUL bytes",
52                ),
53            ));
54        }
55    }
56    Ok(())
57}
58
59/// Build a `PathBuf` like `Path::with_extension`, but return an error instead of
60/// panicking on invalid input. The extension has already been screened via
61/// `validate_extension`; this wrapper exists so the call site reads cleanly.
62#[inline]
63pub(crate) fn with_validated_extension(
64    base: &std::path::Path,
65    extension: &OsStr,
66) -> Result<PathBuf, StrictPathError> {
67    validate_extension(extension, base)?;
68    Ok(base.with_extension(extension))
69}