use std::path::{Component, Path};
use crate::{Error, Result};
pub(crate) fn sanitize_archive_path(path: &str) -> Result<String> {
if path.is_empty() {
return Err(Error::BadParam("Empty path not allowed".to_string()));
}
if path.contains('\\') {
return Err(Error::BadParam(format!(
"Backslash not allowed in archive path: {path}"
)));
}
let mut sanitized = String::new();
for component in Path::new(path).components() {
match component {
Component::Normal(part) => {
let part = part.to_str().ok_or_else(|| {
Error::BadParam(format!("Non-UTF-8 path component in: {path}"))
})?;
if !sanitized.is_empty() {
sanitized.push('/');
}
sanitized.push_str(part);
}
Component::CurDir => {}
Component::RootDir | Component::Prefix(_) | Component::ParentDir => {
return Err(Error::BadParam(format!(
"Path traversal not allowed: {path}"
)));
}
}
}
if sanitized.is_empty() {
return Err(Error::BadParam("Empty path not allowed".to_string()));
}
Ok(sanitized)
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::sanitize_archive_path;
#[test]
fn normal_path_accepted() {
assert_eq!(
sanitize_archive_path("resources/thumbnail.jpg").unwrap(),
"resources/thumbnail.jpg"
);
}
#[test]
fn dot_stripped() {
assert_eq!(
sanitize_archive_path("./resources/thumb.jpg").unwrap(),
"resources/thumb.jpg"
);
}
#[test]
fn parent_dir_rejected() {
assert!(sanitize_archive_path("../etc/passwd").is_err());
}
#[test]
fn inner_parent_dir_rejected() {
assert!(sanitize_archive_path("resources/../../../etc/passwd").is_err());
}
#[test]
fn absolute_rejected() {
assert!(sanitize_archive_path("/etc/passwd").is_err());
}
#[test]
fn empty_rejected() {
assert!(sanitize_archive_path("").is_err());
}
#[test]
fn dot_only_rejected() {
assert!(sanitize_archive_path(".").is_err());
}
#[test]
fn backslash_separator_rejected() {
assert!(sanitize_archive_path("resources\\thumb.jpg").is_err());
}
#[test]
fn backslash_traversal_rejected() {
assert!(sanitize_archive_path("..\\..\\etc\\passwd").is_err());
}
#[test]
fn mixed_slash_traversal_rejected() {
assert!(sanitize_archive_path("resources/..\\..\\etc/passwd").is_err());
}
}