use nucleus::security::SecurityState;
use nucleus::StateTransition;
#[test]
fn test_security_state_machine_valid_path() {
let states = [
SecurityState::Privileged,
SecurityState::CapabilitiesDropped,
SecurityState::SeccompApplied,
SecurityState::LandlockApplied,
SecurityState::Locked,
];
for i in 0..states.len() - 1 {
assert!(
states[i].can_transition_to(&states[i + 1]),
"Invalid transition from {:?} to {:?}",
states[i],
states[i + 1]
);
}
}
#[test]
fn test_security_property_irreversible_lockdown() {
let state = SecurityState::SeccompApplied;
assert!(state.can_transition_to(&SecurityState::SeccompApplied));
assert!(state.can_transition_to(&SecurityState::LandlockApplied));
assert!(!state.can_transition_to(&SecurityState::Locked));
assert!(!state.can_transition_to(&SecurityState::Privileged));
assert!(!state.can_transition_to(&SecurityState::CapabilitiesDropped));
let ll_state = SecurityState::LandlockApplied;
assert!(ll_state.can_transition_to(&SecurityState::LandlockApplied));
assert!(ll_state.can_transition_to(&SecurityState::Locked));
assert!(!ll_state.can_transition_to(&SecurityState::Privileged));
assert!(!ll_state.can_transition_to(&SecurityState::CapabilitiesDropped));
assert!(!ll_state.can_transition_to(&SecurityState::SeccompApplied));
}
#[test]
fn test_security_property_no_privilege_escalation() {
let state = SecurityState::CapabilitiesDropped;
assert!(!state.can_transition_to(&SecurityState::Privileged));
assert!(state.can_transition_to(&SecurityState::SeccompApplied));
}
#[test]
fn test_security_property_terminal_stable() {
let state = SecurityState::Locked;
assert!(state.is_terminal());
assert!(!state.can_transition_to(&SecurityState::Privileged));
assert!(!state.can_transition_to(&SecurityState::CapabilitiesDropped));
assert!(!state.can_transition_to(&SecurityState::SeccompApplied));
assert!(!state.can_transition_to(&SecurityState::LandlockApplied));
assert!(state.can_transition_to(&SecurityState::Locked));
}
#[test]
fn test_security_property_liveness() {
let states = [
SecurityState::Privileged,
SecurityState::CapabilitiesDropped,
SecurityState::SeccompApplied,
SecurityState::LandlockApplied,
];
for initial in states {
assert!(
!initial.is_terminal(),
"{:?} should not be terminal",
initial
);
let mut current = initial;
let mut can_progress = true;
for _ in 0..10 {
if current.is_terminal() {
can_progress = true;
break;
}
current = match current {
SecurityState::Privileged => SecurityState::CapabilitiesDropped,
SecurityState::CapabilitiesDropped => SecurityState::SeccompApplied,
SecurityState::SeccompApplied => SecurityState::LandlockApplied,
SecurityState::LandlockApplied => SecurityState::Locked,
SecurityState::Locked => break,
};
}
assert!(
can_progress,
"Should be able to reach terminal from {:?}",
initial
);
}
}
#[test]
fn test_security_all_transitions() {
let all_states = [
SecurityState::Privileged,
SecurityState::CapabilitiesDropped,
SecurityState::SeccompApplied,
SecurityState::LandlockApplied,
SecurityState::Locked,
];
let valid_transitions = [
(
SecurityState::Privileged,
SecurityState::CapabilitiesDropped,
),
(
SecurityState::CapabilitiesDropped,
SecurityState::SeccompApplied,
),
(
SecurityState::SeccompApplied,
SecurityState::LandlockApplied,
),
(SecurityState::LandlockApplied, SecurityState::Locked),
(SecurityState::Privileged, SecurityState::Privileged),
(
SecurityState::CapabilitiesDropped,
SecurityState::CapabilitiesDropped,
),
(SecurityState::SeccompApplied, SecurityState::SeccompApplied),
(
SecurityState::LandlockApplied,
SecurityState::LandlockApplied,
),
(SecurityState::Locked, SecurityState::Locked),
];
for from in &all_states {
for to in &all_states {
let should_be_valid = valid_transitions.contains(&(*from, *to));
let is_valid = from.can_transition_to(to);
assert_eq!(
is_valid, should_be_valid,
"Transition {:?} -> {:?}: expected {}, got {}",
from, to, should_be_valid, is_valid
);
}
}
}