use super::*;
use crate::operation::Operation;
use crate::test_helpers::StateBuilder;
#[test]
fn read_on_ro_mount_passes() {
let state = StateBuilder::new()
.operation(Operation::Read)
.mount("/mnt", "ext4", "ro")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/data.txt", 0, 0, 0o644)
.build();
let result = check_mount(&state);
assert!(result.is_pass());
}
#[test]
fn write_on_ro_mount_fails() {
let state = StateBuilder::new()
.operation(Operation::Write)
.mount("/mnt", "ext4", "ro")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/data.txt", 0, 0, 0o644)
.build();
let result = check_mount(&state);
assert!(result.is_fail());
if let LayerResult::Fail { detail, .. } = &result {
assert!(detail.contains("read-only"));
assert!(detail.contains("/mnt"));
}
}
#[test]
fn execute_on_noexec_mount_fails() {
let state = StateBuilder::new()
.operation(Operation::Execute)
.mount("/mnt", "ext4", "noexec")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/script.sh", 0, 0, 0o755)
.build();
let result = check_mount(&state);
assert!(result.is_fail());
if let LayerResult::Fail { detail, .. } = &result {
assert!(detail.contains("noexec"));
assert!(detail.contains("/mnt"));
}
}
#[test]
fn execute_on_rw_mount_passes() {
let state = StateBuilder::new()
.operation(Operation::Execute)
.mount("/usr", "ext4", "rw")
.component("/usr", 0, 0, 0o755)
.component_file("/usr/bin/ls", 0, 0, 0o755)
.build();
let result = check_mount(&state);
assert!(result.is_pass());
}
#[test]
fn write_on_rw_mount_passes() {
let state = StateBuilder::new()
.operation(Operation::Write)
.mount("/var", "ext4", "rw")
.component("/var", 0, 0, 0o755)
.component_file("/var/log/app.log", 0, 0, 0o644)
.build();
let result = check_mount(&state);
assert!(result.is_pass());
}
#[test]
fn delete_on_ro_mount_fails() {
let state = StateBuilder::new()
.operation(Operation::Delete)
.mount("/mnt", "ext4", "ro")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/old.txt", 0, 0, 0o644)
.build();
let result = check_mount(&state);
assert!(result.is_fail());
if let LayerResult::Fail { detail, .. } = &result {
assert!(detail.contains("read-only"));
}
}
#[test]
fn create_on_ro_mount_fails() {
let state = StateBuilder::new()
.operation(Operation::Create)
.mount("/mnt", "ext4", "ro")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/new.txt", 0, 0, 0o644)
.build();
let result = check_mount(&state);
assert!(result.is_fail());
if let LayerResult::Fail { detail, .. } = &result {
assert!(detail.contains("read-only"));
}
}
#[test]
fn no_mount_info_returns_degraded() {
let state = StateBuilder::new()
.operation(Operation::Write)
.component("/orphan", 0, 0, 0o755)
.component_file("/orphan/file.txt", 0, 0, 0o644)
.build();
let result = check_mount(&state);
assert!(result.is_degraded());
}
#[test]
fn stat_on_ro_mount_passes() {
let state = StateBuilder::new()
.operation(Operation::Stat)
.mount("/mnt", "ext4", "ro")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/data.txt", 0, 0, 0o644)
.build();
let result = check_mount(&state);
assert!(result.is_pass());
}
#[test]
fn detail_includes_fs_type() {
let state = StateBuilder::new()
.operation(Operation::Write)
.mount("/mnt", "xfs", "ro")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/data.txt", 0, 0, 0o644)
.build();
let result = check_mount(&state);
if let LayerResult::Fail { detail, .. } = &result {
assert!(detail.contains("xfs"));
} else {
panic!("expected Fail");
}
}
#[test]
fn execute_nosuid_suid_target_emits_warning() {
let state = StateBuilder::new()
.operation(Operation::Execute)
.mount("/mnt", "ext4", "nosuid")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/suid_bin", 0, 0, 0o4755)
.build();
let result = check_mount(&state);
assert!(result.is_pass());
if let LayerResult::Pass { warnings, .. } = &result {
assert_eq!(warnings.len(), 1);
assert!(warnings[0].contains("nosuid"));
assert!(warnings[0].contains("setuid/setgid"));
} else {
panic!("expected Pass");
}
}
#[test]
fn execute_nosuid_sgid_target_emits_warning() {
let state = StateBuilder::new()
.operation(Operation::Execute)
.mount("/mnt", "ext4", "nosuid")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/sgid_bin", 0, 0, 0o2755)
.build();
let result = check_mount(&state);
assert!(result.is_pass());
if let LayerResult::Pass { warnings, .. } = &result {
assert_eq!(warnings.len(), 1);
assert!(warnings[0].contains("nosuid"));
} else {
panic!("expected Pass");
}
}
#[test]
fn execute_nosuid_no_suid_bits_no_warning() {
let state = StateBuilder::new()
.operation(Operation::Execute)
.mount("/mnt", "ext4", "nosuid")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/normal_bin", 0, 0, 0o755)
.build();
let result = check_mount(&state);
assert!(result.is_pass());
if let LayerResult::Pass { warnings, .. } = &result {
assert!(warnings.is_empty());
} else {
panic!("expected Pass");
}
}
#[test]
fn execute_no_nosuid_suid_target_no_warning() {
let state = StateBuilder::new()
.operation(Operation::Execute)
.mount("/usr", "ext4", "rw")
.component("/usr", 0, 0, 0o755)
.component_file("/usr/bin/sudo", 0, 0, 0o4755)
.build();
let result = check_mount(&state);
assert!(result.is_pass());
if let LayerResult::Pass { warnings, .. } = &result {
assert!(warnings.is_empty());
} else {
panic!("expected Pass");
}
}
#[test]
fn write_nosuid_suid_target_no_warning() {
let state = StateBuilder::new()
.operation(Operation::Write)
.mount("/mnt", "ext4", "nosuid")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/suid_bin", 0, 0, 0o4755)
.build();
let result = check_mount(&state);
assert!(result.is_pass());
if let LayerResult::Pass { warnings, .. } = &result {
assert!(warnings.is_empty());
} else {
panic!("expected Pass");
}
}
#[test]
fn delete_single_component_returns_degraded() {
let state = StateBuilder::new()
.operation(Operation::Delete)
.mount("/", "ext4", "ro")
.component("/", 0, 0, 0o755)
.build();
let result = check_mount(&state);
assert!(result.is_degraded());
}