use std::path::{ Path, Component };
use crate::Error;
pub fn validate_path( path: &Path ) -> Result< (), Error >
{
for component in path.components()
{
if component == Component::ParentDir
{
return Err( Error::InvalidTemplate(
format!( "Path contains directory traversal: {}", path.display() )
));
}
}
Ok( () )
}
#[ cfg( test ) ]
mod tests
{
use super::*;
use std::path::Path;
#[ test ]
fn rejects_parent_dir()
{
assert!( validate_path( Path::new( "../etc/passwd" ) ).is_err() );
}
#[ test ]
fn rejects_nested_parent_dir()
{
assert!( validate_path( Path::new( "foo/../../bar" ) ).is_err() );
}
#[ test ]
fn rejects_single_parent_in_middle()
{
assert!( validate_path( Path::new( "a/../b" ) ).is_err() );
}
#[ test ]
fn allows_normal_paths()
{
assert!( validate_path( Path::new( "foo/bar.txt" ) ).is_ok() );
assert!( validate_path( Path::new( "src/lib.rs" ) ).is_ok() );
assert!( validate_path( Path::new( "a/b/c/d.txt" ) ).is_ok() );
}
#[ test ]
fn allows_current_dir()
{
assert!( validate_path( Path::new( "./foo/bar.txt" ) ).is_ok() );
assert!( validate_path( Path::new( "foo/./bar.txt" ) ).is_ok() );
}
#[ test ]
fn allows_simple_filename()
{
assert!( validate_path( Path::new( "file.txt" ) ).is_ok() );
}
#[ test ]
fn error_message_includes_path()
{
let result = validate_path( Path::new( "../malicious" ) );
assert!( result.is_err() );
let err = result.unwrap_err();
let msg = format!( "{err}" );
assert!( msg.contains( "directory traversal" ) );
assert!( msg.contains( "../malicious" ) );
}
}