use crate::Result;
use crate::SecurityConfig;
pub fn sanitize_permissions(
_path: &std::path::Path,
mode: u32,
config: &SecurityConfig,
) -> Result<u32> {
let mut sanitized = mode;
sanitized &= !0o4000;
sanitized &= !0o2000;
if !config.allowed.world_writable {
sanitized &= !0o002;
}
Ok(sanitized)
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn test_sanitize_permissions_normal() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o644, &config);
assert_eq!(result.unwrap(), 0o644);
}
#[test]
fn test_sanitize_permissions_executable() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o755, &config);
assert_eq!(result.unwrap(), 0o755);
}
#[test]
fn test_sanitize_permissions_strip_setuid() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o4755, &config);
assert_eq!(result.unwrap(), 0o755);
}
#[test]
fn test_sanitize_permissions_strip_setgid() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o2755, &config);
assert_eq!(result.unwrap(), 0o755);
}
#[test]
fn test_sanitize_permissions_strip_both() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o6755, &config);
assert_eq!(result.unwrap(), 0o755);
}
#[test]
fn test_sanitize_permissions_strip_world_writable() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o777, &config);
assert_eq!(result.unwrap(), 0o775);
}
#[test]
fn test_sanitize_permissions_world_readable_ok() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o644, &config);
assert!(result.is_ok());
}
#[test]
fn test_sanitize_permissions_owner_writable_ok() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o600, &config);
assert!(result.is_ok());
}
#[test]
fn test_sanitize_permissions_group_writable_ok() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o664, &config);
assert!(result.is_ok());
}
#[test]
fn test_sanitize_permissions_edge_case_zero() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o000, &config);
assert_eq!(result.unwrap(), 0o000);
}
#[test]
fn test_sticky_bit_preservation() {
let config = SecurityConfig::default();
let mode_with_sticky = 0o1755; let result = sanitize_permissions(std::path::Path::new("dir/"), mode_with_sticky, &config);
assert!(result.is_ok(), "sticky bit should be allowed");
let sanitized = result.unwrap();
assert_eq!(sanitized & 0o1000, 0o1000, "sticky bit should be preserved");
assert_eq!(sanitized, 0o1755, "full mode should be preserved");
}
#[test]
fn test_sticky_bit_with_setuid_stripped() {
let config = SecurityConfig::default();
let mode = 0o7755; let result = sanitize_permissions(std::path::Path::new("dir/"), mode, &config);
assert!(result.is_ok());
let sanitized = result.unwrap();
assert_eq!(sanitized & 0o1000, 0o1000, "sticky bit should remain");
assert_eq!(sanitized & 0o4000, 0, "setuid should be stripped");
assert_eq!(sanitized & 0o2000, 0, "setgid should be stripped");
assert_eq!(sanitized, 0o1755, "result should be sticky + rwxr-xr-x");
}
#[test]
fn test_world_writable_allowed_with_config() {
let mut config = SecurityConfig::default();
config.allowed.world_writable = true;
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o777, &config);
assert!(
result.is_ok(),
"world-writable should be allowed with config"
);
let sanitized = result.unwrap();
assert_eq!(
sanitized & 0o002,
0o002,
"world-writable bit should be preserved"
);
assert_eq!(sanitized & 0o4000, 0, "setuid should be stripped");
assert_eq!(sanitized & 0o2000, 0, "setgid should be stripped");
assert_eq!(sanitized, 0o777, "result should be rwxrwxrwx");
}
#[test]
fn test_world_writable_stripped_by_default() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o777, &config);
assert_eq!(
result.unwrap(),
0o775,
"world-writable bit should be stripped by default"
);
}
#[test]
fn test_world_writable_bit_only_stripped() {
let config = SecurityConfig::default();
let result = sanitize_permissions(std::path::Path::new("file.txt"), 0o666, &config);
assert_eq!(
result.unwrap(),
0o664,
"only world-writable bit should be stripped, not group-write"
);
}
}