use super::generators;
use crate::checks::CoreLayer;
use crate::fix::scoring;
use crate::fix::FixAction;
use crate::operation::Operation;
use crate::test_helpers::StateBuilder;
#[test]
fn traversal_fixes_returns_acl_and_chmod() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.operation(Operation::Read)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component("/locked", 0, 0, 0o700)
.component_file("/locked/file.txt", 0, 0, 0o644)
.build();
let fixes = generators::traversal_fixes(&state, 1);
assert_eq!(fixes.len(), 2);
assert_eq!(fixes[0].impact, scoring::ACL_USER_GRANT);
assert_eq!(fixes[1].impact, scoring::CHMOD_OTHER);
assert_eq!(fixes[0].layer, CoreLayer::Traversal);
assert!(matches!(&fixes[0].action, FixAction::SetAcl { entry, .. } if entry.contains("1000")));
assert!(
matches!(&fixes[1].action, FixAction::Chmod { mode_change, .. } if mode_change == "o+x")
);
}
#[test]
fn traversal_fixes_out_of_bounds_index_returns_empty() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.operation(Operation::Read)
.mount("/", "ext4", "rw")
.component_file("/file.txt", 1000, 1000, 0o644)
.build();
let fixes = generators::traversal_fixes(&state, 99);
assert!(fixes.is_empty());
}
#[test]
fn dac_fixes_execute_operation_uses_x_perm() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.operation(Operation::Execute)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component_file("/script.sh", 0, 0, 0o644)
.build();
let fixes = generators::dac_fixes(&state);
assert!(fixes.iter().any(|f| matches!(
&f.action,
FixAction::SetAcl { entry, .. } if entry.contains('x')
)));
assert!(fixes.iter().any(|f| matches!(
&f.action,
FixAction::Chmod { mode_change, .. } if mode_change.contains('x')
)));
}
#[test]
fn dac_fixes_write_operation_uses_w_perm() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.operation(Operation::Write)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component_file("/data.txt", 0, 0, 0o444)
.build();
let fixes = generators::dac_fixes(&state);
assert!(fixes.iter().any(|f| matches!(
&f.action,
FixAction::SetAcl { entry, .. } if entry.contains('w')
)));
}
#[test]
fn dac_fixes_delete_operation_uses_w_perm() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.operation(Operation::Delete)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component_file("/data.txt", 0, 0, 0o444)
.build();
let fixes = generators::dac_fixes(&state);
assert!(fixes.iter().any(|f| matches!(
&f.action,
FixAction::SetAcl { entry, .. } if entry.contains('w')
)));
}
#[test]
fn dac_fixes_stat_operation_uses_r_perm() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.operation(Operation::Stat)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component_file("/data.txt", 0, 0, 0o000)
.build();
let fixes = generators::dac_fixes(&state);
assert!(fixes.iter().any(|f| matches!(
&f.action,
FixAction::SetAcl { entry, .. } if entry.contains('r')
)));
}
#[test]
fn dac_fixes_no_target_returns_empty() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.operation(Operation::Delete)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.build();
let fixes = generators::dac_fixes(&state);
assert!(fixes.is_empty());
}
#[test]
fn dac_fixes_unknown_stat_probe_returns_empty() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.operation(Operation::Read)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component_unknown("/mystery.txt")
.build();
let fixes = generators::dac_fixes(&state);
assert!(fixes.is_empty());
}
#[test]
fn acl_fixes_returns_single_set_acl_fix() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.operation(Operation::Read)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component_file("/file.txt", 0, 0, 0o600)
.build();
let fixes = generators::acl_fixes(&state);
assert_eq!(fixes.len(), 1);
assert_eq!(fixes[0].layer, CoreLayer::Acl);
assert_eq!(fixes[0].impact, scoring::ACL_USER_GRANT);
assert!(matches!(
&fixes[0].action,
FixAction::SetAcl { entry, .. } if entry.contains("1000") && entry.contains('r')
));
}
#[test]
fn acl_fixes_write_op_uses_w_perm() {
let state = StateBuilder::new()
.subject(2000, 2000, vec![])
.operation(Operation::Write)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component_file("/file.txt", 0, 0, 0o600)
.build();
let fixes = generators::acl_fixes(&state);
assert_eq!(fixes.len(), 1);
assert!(matches!(
&fixes[0].action,
FixAction::SetAcl { entry, .. } if entry.contains('w')
));
}
#[test]
fn acl_fixes_no_target_returns_empty() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.operation(Operation::Delete)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.build();
let fixes = generators::acl_fixes(&state);
assert!(fixes.is_empty());
}