#[derive(Debug, Clone)]
pub(crate) struct SecurityConfig {
pub max_decompressed_size: u64,
pub max_file_count: usize,
pub max_file_size: u64,
pub max_input_file_size: u64,
}
impl Default for SecurityConfig {
fn default() -> Self {
Self {
max_decompressed_size: 1_073_741_824, max_file_count: 10_000,
max_file_size: 104_857_600, max_input_file_size: 2_147_483_648, }
}
}
impl SecurityConfig {
#[allow(dead_code)]
pub fn new() -> Self {
Self::default()
}
}
pub(crate) fn validate_zip_path(path: &str) -> Result<(), String> {
if path.is_empty() {
return Err("Empty path is not allowed".to_string());
}
if path.starts_with('/') || path.starts_with("C:\\") || path.starts_with("c:\\") {
return Err(format!("Absolute path is not allowed: {}", path));
}
if path.contains("..") {
return Err(format!("Path traversal detected: {}", path));
}
if path.contains('\\') {
return Err(format!("Backslash in path is not allowed: {}", path));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_zip_path_valid() {
assert!(validate_zip_path("xl/workbook.xml").is_ok());
assert!(validate_zip_path("xl/worksheets/sheet1.xml").is_ok());
assert!(validate_zip_path("xl/sharedStrings.xml").is_ok());
}
#[test]
fn test_validate_zip_path_empty() {
assert!(validate_zip_path("").is_err());
}
#[test]
fn test_validate_zip_path_absolute_unix() {
assert!(validate_zip_path("/etc/passwd").is_err());
assert!(validate_zip_path("/xl/workbook.xml").is_err());
}
#[test]
fn test_validate_zip_path_absolute_windows() {
assert!(validate_zip_path("C:\\Windows\\system32").is_err());
assert!(validate_zip_path("c:\\xl\\workbook.xml").is_err());
}
#[test]
fn test_validate_zip_path_traversal() {
assert!(validate_zip_path("../etc/passwd").is_err());
assert!(validate_zip_path("xl/../../etc/passwd").is_err());
assert!(validate_zip_path("xl/..").is_err());
assert!(validate_zip_path("..").is_err());
}
#[test]
fn test_validate_zip_path_backslash() {
assert!(validate_zip_path("xl\\workbook.xml").is_err());
}
}