use super::*;
use crate::state::acl::{AclEntry, AclPerms, AclTag};
use crate::state::path::StatResult;
#[test]
fn defaults_produce_root_subject_and_read_op() {
let state = StateBuilder::new().component("/", 0, 0, 0o755).build();
assert_eq!(state.subject.uid, 0);
assert_eq!(state.subject.gid, 0);
assert!(state.subject.groups.is_empty());
assert_eq!(state.operation, Operation::Read);
}
#[test]
fn subject_overrides_defaults() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![33, 44])
.component("/", 0, 0, 0o755)
.build();
assert_eq!(state.subject.uid, 1000);
assert_eq!(state.subject.gid, 1000);
assert_eq!(state.subject.groups, vec![33, 44]);
}
#[test]
fn component_creates_directory_with_correct_stat() {
let state = StateBuilder::new().component("/var", 0, 0, 0o755).build();
let comp = &state.walk[0];
assert_eq!(comp.path, PathBuf::from("/var"));
let stat = comp.stat.known().expect("stat should be Known");
assert_eq!(stat.uid, 0);
assert_eq!(stat.gid, 0);
assert_eq!(stat.mode, 0o755);
assert_eq!(stat.nlink, 2);
assert_eq!(stat.file_type, FileType::Directory);
}
#[test]
fn component_file_creates_regular_file() {
let state = StateBuilder::new()
.component_file("/etc/passwd", 0, 0, 0o644)
.build();
let comp = &state.walk[0];
let stat = comp.stat.known().expect("stat should be Known");
assert_eq!(stat.file_type, FileType::Regular);
assert_eq!(stat.nlink, 1);
assert_eq!(stat.mode, 0o644);
}
#[test]
fn component_with_acl_sets_acl() {
let entries = vec![AclEntry {
tag: AclTag::User,
qualifier: Some(33),
perms: AclPerms {
read: true,
write: false,
execute: false,
},
}];
let state = StateBuilder::new()
.component_with_acl("/var", 0, 0, 0o750, entries.clone())
.build();
let comp = &state.walk[0];
let acl = comp.acl.known().expect("acl should be Known");
assert_eq!(acl.0.len(), 1);
assert_eq!(acl.0[0].tag, AclTag::User);
assert_eq!(acl.0[0].qualifier, Some(33));
}
#[test]
fn component_with_flags_sets_flags() {
let flags = FsFlags {
immutable: true,
append_only: false,
};
let state = StateBuilder::new()
.component_with_flags("/var", 0, 0, 0o755, flags)
.build();
let comp = &state.walk[0];
let fs_flags = comp.flags.known().expect("flags should be Known");
assert!(fs_flags.immutable);
assert!(!fs_flags.append_only);
}
#[test]
fn component_inaccessible_has_inaccessible_probes() {
let state = StateBuilder::new()
.component_inaccessible("/secret")
.build();
let comp = &state.walk[0];
assert_eq!(comp.stat, Probe::Inaccessible);
assert_eq!(comp.acl, Probe::Inaccessible);
assert_eq!(comp.flags, Probe::Inaccessible);
}
#[test]
fn component_unknown_has_unknown_probes() {
let state = StateBuilder::new().component_unknown("/mystery").build();
let comp = &state.walk[0];
assert_eq!(comp.stat, Probe::<StatResult>::Unknown);
assert_eq!(comp.acl, Probe::<PosixAcl>::Unknown);
assert_eq!(comp.flags, Probe::<FsFlags>::Unknown);
}
#[test]
fn mount_autolink_component_under_var() {
let state = StateBuilder::new()
.mount("/var", "ext4", "rw")
.component("/var/log", 0, 0, 0o755)
.build();
let comp = &state.walk[0];
assert_eq!(comp.mount, Some(0));
let stat = comp.stat.known().expect("stat should be Known");
assert_eq!(stat.dev, 1); }
#[test]
fn mount_autolink_longest_prefix_wins() {
let state = StateBuilder::new()
.mount("/var", "ext4", "rw")
.mount("/var/log", "ext4", "rw,noexec")
.component("/var/log/app.log", 0, 0, 0o644)
.build();
let comp = &state.walk[0];
assert_eq!(comp.mount, Some(1));
let stat = comp.stat.known().expect("stat should be Known");
assert_eq!(stat.dev, 2); }
#[test]
fn mount_options_parsing_ro() {
let state = StateBuilder::new()
.mount("/mnt", "ext4", "ro")
.component("/mnt/data", 0, 0, 0o755)
.build();
let mount = &state.mounts.0[0];
assert!(mount.options.read_only);
assert!(!mount.options.noexec);
assert!(!mount.options.nosuid);
}
#[test]
fn mount_options_parsing_multiple() {
let state = StateBuilder::new()
.mount("/mnt", "ext4", "ro,noexec,nosuid")
.component("/mnt/data", 0, 0, 0o755)
.build();
let mount = &state.mounts.0[0];
assert!(mount.options.read_only);
assert!(mount.options.noexec);
assert!(mount.options.nosuid);
}
#[test]
fn mount_options_parsing_rw_all_false() {
let state = StateBuilder::new()
.mount("/", "ext4", "rw")
.component("/tmp", 0, 0, 0o1777)
.build();
let mount = &state.mounts.0[0];
assert!(!mount.options.read_only);
assert!(!mount.options.noexec);
assert!(!mount.options.nosuid);
}
#[test]
fn nginx_reading_log_file_scenario() {
let state = StateBuilder::new()
.subject(33, 33, vec![])
.operation(Operation::Read)
.mount("/var", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component("/var", 0, 0, 0o755)
.component("/var/log", 0, 4, 0o2775)
.component("/var/log/app", 0, 33, 0o750)
.component_file("/var/log/app/current.log", 0, 33, 0o640)
.build();
assert_eq!(state.subject.uid, 33);
assert_eq!(state.subject.gid, 33);
assert_eq!(state.operation, Operation::Read);
assert_eq!(state.walk.len(), 5);
assert!(state.walk[0].mount.is_none());
for comp in &state.walk[1..] {
assert_eq!(comp.mount, Some(0));
}
let target = &state.walk[4];
let stat = target.stat.known().expect("stat should be Known");
assert_eq!(stat.file_type, FileType::Regular);
assert_eq!(stat.mode, 0o640);
assert_eq!(stat.uid, 0);
assert_eq!(stat.gid, 33);
}
#[test]
#[should_panic(expected = "at least one path component is required")]
fn build_with_no_components_panics() {
let _state = StateBuilder::new().build();
}
#[test]
fn operation_override_works() {
let state = StateBuilder::new()
.operation(Operation::Write)
.component("/tmp/file", 0, 0, 0o644)
.build();
assert_eq!(state.operation, Operation::Write);
}
#[test]
fn inaccessible_component_gets_no_mount_link() {
let state = StateBuilder::new()
.mount("/var", "ext4", "rw")
.component_inaccessible("/var/secret")
.build();
let comp = &state.walk[0];
assert_eq!(comp.mount, Some(0));
assert_eq!(comp.stat, Probe::Inaccessible);
}
#[test]
fn multiple_mounts_assign_sequential_device_ids() {
let state = StateBuilder::new()
.mount("/", "ext4", "rw")
.mount("/var", "ext4", "rw")
.mount("/tmp", "tmpfs", "rw,noexec")
.component("/home", 0, 0, 0o755)
.build();
assert_eq!(state.mounts.0[0].device, 1);
assert_eq!(state.mounts.0[1].device, 2);
assert_eq!(state.mounts.0[2].device, 3);
}