use super::*;
use crate::checks::CoreLayer;
use crate::fix::FixAction;
use crate::operation::{MetadataParams, Operation};
use crate::test_helpers::StateBuilder;
#[test]
fn nginx_reading_log_traversal_and_dac_fail() {
let state = StateBuilder::new()
.subject(33, 33, vec![])
.operation(Operation::Read)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component("/var", 0, 0, 0o755)
.component("/var/log", 0, 0, 0o755)
.component("/var/log/app", 0, 0, 0o700)
.component_file("/var/log/app/current.log", 0, 0, 0o640)
.build();
let report = crate::checks::run_checks(&state, &MetadataParams::default());
assert!(report.core_results[CoreLayer::Traversal].is_fail());
assert!(report.core_results[CoreLayer::Dac].is_fail());
let plan = generate_fixes(&report, &state, &MetadataParams::default());
let trav_fixes: Vec<_> = plan
.fixes
.iter()
.filter(|f| f.layer == CoreLayer::Traversal)
.collect();
let dac_fixes: Vec<_> = plan
.fixes
.iter()
.filter(|f| f.layer == CoreLayer::Dac)
.collect();
assert!(trav_fixes.len() >= 2);
assert!(dac_fixes.len() >= 2);
let last_trav_pos = plan
.fixes
.iter()
.rposition(|f| f.layer == CoreLayer::Traversal)
.unwrap();
let first_dac_pos = plan
.fixes
.iter()
.position(|f| f.layer == CoreLayer::Dac)
.unwrap();
assert!(last_trav_pos < first_dac_pos);
assert_eq!(trav_fixes[0].impact, scoring::ACL_USER_GRANT);
assert_eq!(dac_fixes[0].impact, scoring::ACL_USER_GRANT);
}
#[test]
fn fixes_ordered_by_layer_position() {
let state = StateBuilder::new()
.subject(2000, 2000, vec![])
.operation(Operation::Write)
.mount("/mnt", "ext4", "ro")
.component("/mnt", 0, 0, 0o755)
.component_file("/mnt/file.txt", 0, 0, 0o600)
.build();
let report = crate::checks::run_checks(&state, &MetadataParams::default());
let plan = generate_fixes(&report, &state, &MetadataParams::default());
let layers: Vec<CoreLayer> = plan.fixes.iter().map(|f| f.layer).collect();
if let Some(mount_pos) = layers.iter().position(|l| *l == CoreLayer::Mount) {
if let Some(dac_pos) = layers.iter().position(|l| *l == CoreLayer::Dac) {
assert!(mount_pos < dac_pos);
}
}
}
#[test]
fn chmod_failure_fix_plan_contains_chown_and_grant_cap_fowner() {
let state = StateBuilder::new()
.subject(1000, 1000, vec![])
.with_capabilities(0)
.operation(Operation::Chmod)
.mount("/", "ext4", "rw")
.component("/", 0, 0, 0o755)
.component_file("/file.txt", 0, 0, 0o644)
.build();
let report = crate::checks::run_checks(&state, &MetadataParams::default());
assert!(report.core_results[CoreLayer::Metadata].is_fail());
let plan = generate_fixes(&report, &state, &MetadataParams::default());
let meta_fixes: Vec<_> = plan
.fixes
.iter()
.filter(|f| f.layer == CoreLayer::Metadata)
.collect();
assert!(
meta_fixes.len() >= 2,
"expected at least Chown and GrantCap fixes"
);
let chown_pos = meta_fixes
.iter()
.position(|f| matches!(f.action, FixAction::Chown { .. }));
let grant_pos = meta_fixes.iter().position(|f| {
matches!(&f.action, FixAction::GrantCap { capability, .. } if capability == "cap_fowner")
});
assert!(chown_pos.is_some(), "expected Chown fix (impact 4)");
assert!(
grant_pos.is_some(),
"expected GrantCap cap_fowner fix (impact 5)"
);
let chown_fix = meta_fixes[chown_pos.unwrap()];
let grant_fix = meta_fixes[grant_pos.unwrap()];
assert_eq!(chown_fix.impact, 4);
assert_eq!(grant_fix.impact, 5);
assert!(
chown_pos.unwrap() < grant_pos.unwrap(),
"Chown fix should precede GrantCap in plan"
);
}