Files
2026-09-04 14:45:05 +02:00

186 lines
7.7 KiB
Rust

// file: crates/ksp-worker-api/tests/security_hardening.rs
// version: 1
//! Adversarial lifecycle, stop and redaction canaries for the frozen Worker API.
const HOSTILE_MARKER: &str = "WORKER-IDENTITY-SECRET-CANARY";
const TEST_FAULT: ksp_worker_api::ErrorCode = ksp_worker_api::ErrorCode::new("worker_test", "fault");
fn running_lifecycle(id_value: &str) -> std::option::Option<ksp_worker_api::WorkerLifecycle> {
let id = match ksp_worker_api::WorkerId::new(id_value) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let kind = match ksp_worker_api::WorkerKindCode::new("continuous_worker") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let mut lifecycle = ksp_worker_api::WorkerLifecycle::new(id, kind);
if lifecycle.start().is_err() {
return std::option::Option::None;
}
if lifecycle.mark_running().is_err() {
return std::option::Option::None;
}
return std::option::Option::Some(lifecycle);
}
fn assert_all_mutators_reject(lifecycle: &mut ksp_worker_api::WorkerLifecycle, terminal: ksp_worker_api::WorkerState) {
assert!(lifecycle.start().is_err());
assert!(lifecycle.mark_running().is_err());
assert!(lifecycle.mark_stopping().is_err());
assert!(lifecycle.mark_stopped().is_err());
assert!(lifecycle.fault(TEST_FAULT).is_err());
assert_eq!(lifecycle.state(), terminal);
return;
}
#[test]
fn pre_003_worker_identity_lifecycle_and_snapshot_debug_redact_hostile_identity() {
let lifecycle = running_lifecycle(HOSTILE_MARKER);
assert!(lifecycle.is_some());
let lifecycle = match lifecycle {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let id = lifecycle.id();
assert_eq!(std::format!("{id:?}"), "WorkerId(..)");
let lifecycle_debug = std::format!("{lifecycle:?}");
assert!(lifecycle_debug.contains("WorkerLifecycle"));
assert!(lifecycle_debug.contains("continuous_worker"));
assert!(lifecycle_debug.contains("Running"));
assert!(!lifecycle_debug.contains(HOSTILE_MARKER));
let snapshot = ksp_worker_api::WorkerSnapshot::new(
lifecycle.id().clone(),
lifecycle.kind().clone(),
ksp_worker_api::WorkerSnapshotSequence::initial(),
lifecycle.state(),
ksp_worker_api::WorkerHealth::Healthy,
ksp_worker_api::WorkerActivity::Active,
);
let snapshot_debug = std::format!("{snapshot:?}");
assert!(snapshot_debug.contains("WorkerSnapshot"));
assert!(snapshot_debug.contains("continuous_worker"));
assert!(snapshot_debug.contains("Healthy"));
assert!(snapshot_debug.contains("Active"));
assert!(!snapshot_debug.contains(HOSTILE_MARKER));
return;
}
#[test]
fn pre_003_identity_errors_never_echo_hostile_values() {
let hostile = "WORKER-SECRET/CANARY";
let rejected = ksp_worker_api::WorkerId::new(hostile);
assert!(rejected.is_err());
let error = match rejected {
std::result::Result::Err(value) => value,
std::result::Result::Ok(_) => return,
};
assert_eq!(error.code(), ksp_worker_api::ERROR_CODE_WORKER_ID_INVALID);
assert!(!std::format!("{error}").contains(hostile));
assert!(!std::format!("{error:?}").contains(hostile));
for context in error.context() {
assert!(!context.value().contains(hostile));
}
return;
}
#[test]
fn pre_003_stopped_and_faulted_are_immutable_under_all_public_mutators() {
let id = match ksp_worker_api::WorkerId::new("terminal-created") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let kind = match ksp_worker_api::WorkerKindCode::new("terminal_worker") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let mut stopped = ksp_worker_api::WorkerLifecycle::new(id, kind);
assert!(stopped.mark_stopped().is_ok());
assert_all_mutators_reject(&mut stopped, ksp_worker_api::WorkerState::Stopped);
let faulted = running_lifecycle("terminal-faulted");
assert!(faulted.is_some());
let mut faulted = match faulted {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(faulted.fault(TEST_FAULT).is_ok());
assert_all_mutators_reject(&mut faulted, ksp_worker_api::WorkerState::Faulted(TEST_FAULT));
return;
}
#[test]
fn pre_003_stop_fault_terminal_orders_are_first_valid_terminal_wins() {
let direct_fault = running_lifecycle("race-direct-fault");
assert!(direct_fault.is_some());
let mut direct_fault = match direct_fault {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(direct_fault.fault(TEST_FAULT).is_ok());
assert!(direct_fault.mark_stopping().is_err());
assert_eq!(direct_fault.state(), ksp_worker_api::WorkerState::Faulted(TEST_FAULT));
let stopping_fault = running_lifecycle("race-stopping-fault");
assert!(stopping_fault.is_some());
let mut stopping_fault = match stopping_fault {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(stopping_fault.mark_stopping().is_ok());
assert!(stopping_fault.fault(TEST_FAULT).is_ok());
assert!(stopping_fault.mark_stopped().is_err());
assert_eq!(stopping_fault.state(), ksp_worker_api::WorkerState::Faulted(TEST_FAULT));
let stopping_stopped = running_lifecycle("race-stopping-stopped");
assert!(stopping_stopped.is_some());
let mut stopping_stopped = match stopping_stopped {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(stopping_stopped.mark_stopping().is_ok());
assert!(stopping_stopped.mark_stopped().is_ok());
assert!(stopping_stopped.fault(TEST_FAULT).is_err());
assert_eq!(stopping_stopped.state(), ksp_worker_api::WorkerState::Stopped);
return;
}
#[test]
fn pre_003_stop_token_is_send_sync_cross_thread_and_cannot_reanimate_terminal_lifecycle() {
fn require_send_sync<T: std::marker::Send + std::marker::Sync>() {}
require_send_sync::<ksp_worker_api::WorkerStopToken>();
let lifecycle = running_lifecycle("cross-thread-stop");
assert!(lifecycle.is_some());
let mut lifecycle = match lifecycle {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(lifecycle.fault(TEST_FAULT).is_ok());
let token = ksp_worker_api::WorkerStopToken::new();
let worker_token = token.clone();
let thread = std::thread::spawn(move || return worker_token.request_stop());
let first = match thread.join() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert!(first);
assert!(token.is_stop_requested());
assert!(!token.request_stop());
assert!(lifecycle.mark_stopping().is_err());
assert_eq!(lifecycle.state(), ksp_worker_api::WorkerState::Faulted(TEST_FAULT));
return;
}
#[test]
fn pre_003_public_worker_primitives_keep_promised_send_sync_contracts() {
fn require_send_sync<T: std::marker::Send + std::marker::Sync>() {}
require_send_sync::<ksp_worker_api::WorkerId>();
require_send_sync::<ksp_worker_api::WorkerKindCode>();
require_send_sync::<ksp_worker_api::WorkerState>();
require_send_sync::<ksp_worker_api::WorkerHealth>();
require_send_sync::<ksp_worker_api::WorkerActivity>();
require_send_sync::<ksp_worker_api::WorkerLifecycle>();
require_send_sync::<ksp_worker_api::WorkerSnapshotSequence>();
require_send_sync::<ksp_worker_api::WorkerSnapshot>();
require_send_sync::<ksp_worker_api::WorkerStopToken>();
return;
}