use std::path::Path;
pub fn check_forbidden_device_type(external_attributes: u32) -> Result<(), &'static str> {
let mode = (external_attributes >> 16) & 0o170000;
match mode {
0o060000 => Err("Archive entry is a block device (S_IFBLK), which is forbidden"),
0o020000 => Err("Archive entry is a character device (S_IFCHR), which is forbidden"),
0o010000 => Err("Archive entry is a named pipe/FIFO (S_IFIFO), which is forbidden"),
0o140000 => Err("Archive entry is a socket (S_IFSOCK), which is forbidden"),
_ => Ok(()),
}
}
pub fn sanitize_unix_mode(external_attributes: u32, is_dir: bool) -> u32 {
let raw_mode = (external_attributes >> 16) & 0o7777;
if is_dir {
let base = if raw_mode == 0 { 0o755 } else { raw_mode };
(base & !0o7022) | 0o700
} else {
let base = if raw_mode == 0 { 0o644 } else { raw_mode };
(base & !0o7022) | 0o600
}
}
#[cfg(unix)]
pub fn apply_safe_permissions(
path: &Path,
external_attributes: u32,
is_dir: bool,
) -> std::io::Result<()> {
use std::os::unix::fs::PermissionsExt;
let safe_mode = sanitize_unix_mode(external_attributes, is_dir);
let perms = std::fs::Permissions::from_mode(safe_mode);
std::fs::set_permissions(path, perms)
}
#[cfg(not(unix))]
pub fn apply_safe_permissions(
_path: &Path,
_external_attributes: u32,
_is_dir: bool,
) -> std::io::Result<()> {
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_forbidden_device_rejection() {
assert!(check_forbidden_device_type(0o060666 << 16).is_err()); assert!(check_forbidden_device_type(0o020666 << 16).is_err()); assert!(check_forbidden_device_type(0o010666 << 16).is_err()); assert!(check_forbidden_device_type(0o140666 << 16).is_err());
assert!(check_forbidden_device_type(0o100644 << 16).is_ok()); assert!(check_forbidden_device_type(0o040755 << 16).is_ok()); assert!(check_forbidden_device_type(0o120777 << 16).is_ok()); }
#[test]
fn test_suid_sgid_stripped() {
let mode = sanitize_unix_mode(0o6755 << 16, false);
assert_eq!(mode & 0o4000, 0, "SUID bit must be stripped");
assert_eq!(mode & 0o2000, 0, "SGID bit must be stripped");
assert_eq!(mode & 0o1000, 0, "Sticky bit must be stripped");
assert_eq!(mode & 0o0020, 0, "Group writable bit must be stripped");
assert_eq!(mode & 0o0002, 0, "World writable bit must be stripped");
assert_eq!(mode, 0o755);
}
#[test]
fn test_group_and_world_writable_stripped() {
let mode = sanitize_unix_mode(0o777 << 16, false);
assert_eq!(mode, 0o755); assert_eq!(mode & 0o0020, 0);
assert_eq!(mode & 0o0002, 0);
}
#[test]
fn test_default_permissions() {
assert_eq!(sanitize_unix_mode(0, false), 0o644);
assert_eq!(sanitize_unix_mode(0, true), 0o755);
}
}