#[must_use]
pub fn is_windows_absolute(path: &str) -> bool {
let bytes = path.as_bytes();
if bytes.len() >= 3
&& bytes[0].is_ascii_alphabetic()
&& bytes[1] == b':'
&& (bytes[2] == b'\\' || bytes[2] == b'/')
{
return true;
}
if bytes.len() >= 2 && bytes[0] == b'\\' && bytes[1] == b'\\' {
return true;
}
false
}
#[must_use]
pub fn has_traversal(path: &str) -> bool {
path.split(['/', '\\']).any(|seg| seg == "..")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn windows_drive_letter_backslash() {
assert!(is_windows_absolute(r"C:\photo.jpg"));
assert!(is_windows_absolute(r"D:\Users\photo.jpg"));
}
#[test]
fn windows_drive_letter_forward_slash() {
assert!(is_windows_absolute("C:/photo.jpg"));
}
#[test]
fn windows_unc_path() {
assert!(is_windows_absolute(r"\\server\share\photo.jpg"));
}
#[test]
fn relative_path_not_windows_absolute() {
assert!(!is_windows_absolute("images/photo.jpg"));
assert!(!is_windows_absolute("photo.jpg"));
}
#[test]
fn unix_absolute_not_windows_absolute() {
assert!(!is_windows_absolute("/etc/passwd"));
}
#[test]
fn forward_slash_traversal() {
assert!(has_traversal("../photo.jpg"));
assert!(has_traversal("images/../../etc/passwd"));
}
#[test]
fn backslash_traversal() {
assert!(has_traversal(r"..\photo.jpg"));
assert!(has_traversal(r"images\..\..\etc\passwd"));
}
#[test]
fn no_traversal() {
assert!(!has_traversal("images/photo.jpg"));
assert!(!has_traversal("photo.jpg"));
assert!(!has_traversal(r"images\photo.jpg"));
}
#[test]
fn double_dot_in_filename_not_traversal() {
assert!(!has_traversal("file..name.jpg"));
}
}