use std::collections::{HashMap, HashSet};
use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::RwLock;
use std::time::{Duration, Instant, SystemTime};
use crate::KernelId;
#[cfg(feature = "crypto")]
use aes_gcm::{
aead::{Aead, KeyInit},
Aes256Gcm, Nonce as AesNonce,
};
#[cfg(feature = "crypto")]
use chacha20poly1305::{ChaCha20Poly1305, Nonce as ChaNonce, XChaCha20Poly1305, XNonce};
#[cfg(feature = "crypto")]
use rand::{rngs::OsRng, RngCore};
#[cfg(feature = "crypto")]
use zeroize::Zeroize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum EncryptionAlgorithm {
#[default]
Aes256Gcm,
Aes128Gcm,
ChaCha20Poly1305,
XChaCha20Poly1305,
}
impl fmt::Display for EncryptionAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Aes256Gcm => write!(f, "AES-256-GCM"),
Self::Aes128Gcm => write!(f, "AES-128-GCM"),
Self::ChaCha20Poly1305 => write!(f, "ChaCha20-Poly1305"),
Self::XChaCha20Poly1305 => write!(f, "XChaCha20-Poly1305"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum KeyDerivation {
#[default]
HkdfSha256,
HkdfSha384,
Argon2id,
Pbkdf2Sha256,
}
#[derive(Debug, Clone)]
pub struct EncryptionConfig {
pub algorithm: EncryptionAlgorithm,
pub key_derivation: KeyDerivation,
pub key_rotation_interval: Duration,
pub encrypt_control_blocks: bool,
pub encrypt_message_queues: bool,
pub encrypt_kernel_state: bool,
pub aad_prefix: Option<Vec<u8>>,
}
impl Default for EncryptionConfig {
fn default() -> Self {
Self {
algorithm: EncryptionAlgorithm::default(),
key_derivation: KeyDerivation::default(),
key_rotation_interval: Duration::from_secs(3600), encrypt_control_blocks: true,
encrypt_message_queues: true,
encrypt_kernel_state: true,
aad_prefix: None,
}
}
}
impl EncryptionConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_algorithm(mut self, algorithm: EncryptionAlgorithm) -> Self {
self.algorithm = algorithm;
self
}
pub fn with_key_derivation(mut self, kdf: KeyDerivation) -> Self {
self.key_derivation = kdf;
self
}
pub fn with_key_rotation_interval(mut self, interval: Duration) -> Self {
self.key_rotation_interval = interval;
self
}
pub fn with_control_block_encryption(mut self, enabled: bool) -> Self {
self.encrypt_control_blocks = enabled;
self
}
pub fn with_message_queue_encryption(mut self, enabled: bool) -> Self {
self.encrypt_message_queues = enabled;
self
}
pub fn with_kernel_state_encryption(mut self, enabled: bool) -> Self {
self.encrypt_kernel_state = enabled;
self
}
pub fn with_aad_prefix(mut self, prefix: Vec<u8>) -> Self {
self.aad_prefix = Some(prefix);
self
}
}
#[derive(Clone)]
pub struct EncryptionKey {
pub key_id: u64,
#[cfg(feature = "crypto")]
key_material: zeroize::Zeroizing<Vec<u8>>,
#[cfg(not(feature = "crypto"))]
key_material: Vec<u8>,
pub created_at: Instant,
pub expires_at: Option<Instant>,
pub algorithm: EncryptionAlgorithm,
}
impl EncryptionKey {
#[cfg(feature = "crypto")]
pub fn new(key_id: u64, algorithm: EncryptionAlgorithm) -> Self {
let key_size = match algorithm {
EncryptionAlgorithm::Aes256Gcm
| EncryptionAlgorithm::ChaCha20Poly1305
| EncryptionAlgorithm::XChaCha20Poly1305 => 32,
EncryptionAlgorithm::Aes128Gcm => 16,
};
let mut key_material = vec![0u8; key_size];
OsRng.fill_bytes(&mut key_material);
Self {
key_id,
key_material: zeroize::Zeroizing::new(key_material),
created_at: Instant::now(),
expires_at: None,
algorithm,
}
}
#[cfg(not(feature = "crypto"))]
pub fn new(key_id: u64, algorithm: EncryptionAlgorithm) -> Self {
let key_size = match algorithm {
EncryptionAlgorithm::Aes256Gcm
| EncryptionAlgorithm::ChaCha20Poly1305
| EncryptionAlgorithm::XChaCha20Poly1305 => 32,
EncryptionAlgorithm::Aes128Gcm => 16,
};
let key_material: Vec<u8> = (0..key_size)
.map(|i| ((key_id as u8).wrapping_add(i as u8)).wrapping_mul(17))
.collect();
Self {
key_id,
key_material,
created_at: Instant::now(),
expires_at: None,
algorithm,
}
}
#[cfg(feature = "crypto")]
pub fn from_material(key_id: u64, algorithm: EncryptionAlgorithm, material: Vec<u8>) -> Self {
Self {
key_id,
key_material: zeroize::Zeroizing::new(material),
created_at: Instant::now(),
expires_at: None,
algorithm,
}
}
#[cfg(not(feature = "crypto"))]
pub fn from_material(key_id: u64, algorithm: EncryptionAlgorithm, material: Vec<u8>) -> Self {
Self {
key_id,
key_material: material,
created_at: Instant::now(),
expires_at: None,
algorithm,
}
}
pub(crate) fn material(&self) -> &[u8] {
&self.key_material
}
pub fn is_expired(&self) -> bool {
self.expires_at
.map(|exp| Instant::now() > exp)
.unwrap_or(false)
}
pub fn key_size(&self) -> usize {
self.key_material.len()
}
}
impl fmt::Debug for EncryptionKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EncryptionKey")
.field("key_id", &self.key_id)
.field("algorithm", &self.algorithm)
.field("key_size", &self.key_material.len())
.field("created_at", &self.created_at)
.field("expires_at", &self.expires_at)
.finish()
}
}
#[derive(Debug, Clone)]
pub struct EncryptedRegion {
pub region_id: u64,
pub ciphertext: Vec<u8>,
pub nonce: Vec<u8>,
pub key_id: u64,
pub plaintext_size: usize,
pub algorithm: EncryptionAlgorithm,
pub encrypted_at: Instant,
}
#[derive(Debug, Clone, Default)]
pub struct EncryptionStats {
pub bytes_encrypted: u64,
pub bytes_decrypted: u64,
pub encrypt_ops: u64,
pub decrypt_ops: u64,
pub key_rotations: u64,
pub avg_encrypt_time_us: f64,
pub avg_decrypt_time_us: f64,
}
pub struct MemoryEncryption {
config: EncryptionConfig,
active_key: RwLock<EncryptionKey>,
previous_keys: RwLock<HashMap<u64, EncryptionKey>>,
next_key_id: AtomicU64,
region_counter: AtomicU64,
stats: RwLock<EncryptionStats>,
last_rotation: RwLock<Instant>,
}
impl MemoryEncryption {
pub fn new(config: EncryptionConfig) -> Self {
let key_id = 1;
let active_key = EncryptionKey::new(key_id, config.algorithm);
Self {
config,
active_key: RwLock::new(active_key),
previous_keys: RwLock::new(HashMap::new()),
next_key_id: AtomicU64::new(2),
region_counter: AtomicU64::new(1),
stats: RwLock::new(EncryptionStats::default()),
last_rotation: RwLock::new(Instant::now()),
}
}
#[cfg(feature = "crypto")]
pub fn encrypt_region(&self, plaintext: &[u8]) -> EncryptedRegion {
let start = Instant::now();
let key = self.active_key.read().unwrap();
let region_id = self.region_counter.fetch_add(1, Ordering::Relaxed);
let nonce_size = match self.config.algorithm {
EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => 12,
EncryptionAlgorithm::ChaCha20Poly1305 => 12,
EncryptionAlgorithm::XChaCha20Poly1305 => 24,
};
let mut nonce = vec![0u8; nonce_size];
OsRng.fill_bytes(&mut nonce);
let aad = self.config.aad_prefix.as_deref().unwrap_or(&[]);
let ciphertext = match self.config.algorithm {
EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => {
let cipher =
Aes256Gcm::new_from_slice(key.material()).expect("Invalid AES key length");
let aes_nonce = AesNonce::from_slice(&nonce);
cipher
.encrypt(aes_nonce, plaintext)
.expect("AES-GCM encryption failed")
}
EncryptionAlgorithm::ChaCha20Poly1305 => {
let cipher = ChaCha20Poly1305::new_from_slice(key.material())
.expect("Invalid ChaCha20 key length");
let cha_nonce = ChaNonce::from_slice(&nonce);
cipher
.encrypt(cha_nonce, plaintext)
.expect("ChaCha20-Poly1305 encryption failed")
}
EncryptionAlgorithm::XChaCha20Poly1305 => {
let cipher = XChaCha20Poly1305::new_from_slice(key.material())
.expect("Invalid XChaCha20 key length");
let x_nonce = XNonce::from_slice(&nonce);
cipher
.encrypt(x_nonce, plaintext)
.expect("XChaCha20-Poly1305 encryption failed")
}
};
let elapsed = start.elapsed();
{
let mut stats = self.stats.write().unwrap();
stats.bytes_encrypted += plaintext.len() as u64;
stats.encrypt_ops += 1;
let total_time = stats.avg_encrypt_time_us * (stats.encrypt_ops - 1) as f64;
stats.avg_encrypt_time_us =
(total_time + elapsed.as_micros() as f64) / stats.encrypt_ops as f64;
}
let _ = aad;
EncryptedRegion {
region_id,
ciphertext,
nonce,
key_id: key.key_id,
plaintext_size: plaintext.len(),
algorithm: self.config.algorithm,
encrypted_at: Instant::now(),
}
}
#[cfg(not(feature = "crypto"))]
pub fn encrypt_region(&self, plaintext: &[u8]) -> EncryptedRegion {
let start = Instant::now();
let key = self.active_key.read().unwrap();
let region_id = self.region_counter.fetch_add(1, Ordering::Relaxed);
let nonce_size = match self.config.algorithm {
EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => 12,
EncryptionAlgorithm::ChaCha20Poly1305 => 12,
EncryptionAlgorithm::XChaCha20Poly1305 => 24,
};
let nonce: Vec<u8> = (0..nonce_size)
.map(|i| ((region_id as u8).wrapping_add(i as u8)).wrapping_mul(23))
.collect();
let mut ciphertext = plaintext.to_vec();
for (i, byte) in ciphertext.iter_mut().enumerate() {
*byte ^= key.material()[i % key.material().len()];
*byte ^= nonce[i % nonce.len()];
}
let tag: Vec<u8> = (0..16)
.map(|i| {
ciphertext.get(i).copied().unwrap_or(0) ^ key.material()[i % key.material().len()]
})
.collect();
ciphertext.extend(tag);
let elapsed = start.elapsed();
{
let mut stats = self.stats.write().unwrap();
stats.bytes_encrypted += plaintext.len() as u64;
stats.encrypt_ops += 1;
let total_time = stats.avg_encrypt_time_us * (stats.encrypt_ops - 1) as f64;
stats.avg_encrypt_time_us =
(total_time + elapsed.as_micros() as f64) / stats.encrypt_ops as f64;
}
EncryptedRegion {
region_id,
ciphertext,
nonce,
key_id: key.key_id,
plaintext_size: plaintext.len(),
algorithm: self.config.algorithm,
encrypted_at: Instant::now(),
}
}
#[cfg(feature = "crypto")]
pub fn decrypt_region(&self, region: &EncryptedRegion) -> Result<Vec<u8>, String> {
let start = Instant::now();
let key = if region.key_id == self.active_key.read().unwrap().key_id {
self.active_key.read().unwrap().clone()
} else {
self.previous_keys
.read()
.unwrap()
.get(®ion.key_id)
.cloned()
.ok_or_else(|| format!("Key {} not found", region.key_id))?
};
let plaintext = match region.algorithm {
EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => {
let cipher = Aes256Gcm::new_from_slice(key.material())
.map_err(|e| format!("Invalid AES key: {}", e))?;
let aes_nonce = AesNonce::from_slice(®ion.nonce);
cipher
.decrypt(aes_nonce, region.ciphertext.as_ref())
.map_err(|_| {
"AES-GCM decryption failed: authentication tag mismatch".to_string()
})?
}
EncryptionAlgorithm::ChaCha20Poly1305 => {
let cipher = ChaCha20Poly1305::new_from_slice(key.material())
.map_err(|e| format!("Invalid ChaCha20 key: {}", e))?;
let cha_nonce = ChaNonce::from_slice(®ion.nonce);
cipher
.decrypt(cha_nonce, region.ciphertext.as_ref())
.map_err(|_| {
"ChaCha20-Poly1305 decryption failed: authentication tag mismatch"
.to_string()
})?
}
EncryptionAlgorithm::XChaCha20Poly1305 => {
let cipher = XChaCha20Poly1305::new_from_slice(key.material())
.map_err(|e| format!("Invalid XChaCha20 key: {}", e))?;
let x_nonce = XNonce::from_slice(®ion.nonce);
cipher
.decrypt(x_nonce, region.ciphertext.as_ref())
.map_err(|_| {
"XChaCha20-Poly1305 decryption failed: authentication tag mismatch"
.to_string()
})?
}
};
let elapsed = start.elapsed();
{
let mut stats = self.stats.write().unwrap();
stats.bytes_decrypted += plaintext.len() as u64;
stats.decrypt_ops += 1;
let total_time = stats.avg_decrypt_time_us * (stats.decrypt_ops - 1) as f64;
stats.avg_decrypt_time_us =
(total_time + elapsed.as_micros() as f64) / stats.decrypt_ops as f64;
}
Ok(plaintext)
}
#[cfg(not(feature = "crypto"))]
pub fn decrypt_region(&self, region: &EncryptedRegion) -> Result<Vec<u8>, String> {
let start = Instant::now();
let key = if region.key_id == self.active_key.read().unwrap().key_id {
self.active_key.read().unwrap().clone()
} else {
self.previous_keys
.read()
.unwrap()
.get(®ion.key_id)
.cloned()
.ok_or_else(|| format!("Key {} not found", region.key_id))?
};
if region.ciphertext.len() < 16 {
return Err("Ciphertext too short".to_string());
}
let (ciphertext, _tag) = region.ciphertext.split_at(region.ciphertext.len() - 16);
let mut plaintext = ciphertext.to_vec();
for (i, byte) in plaintext.iter_mut().enumerate() {
*byte ^= region.nonce[i % region.nonce.len()];
*byte ^= key.material()[i % key.material().len()];
}
let elapsed = start.elapsed();
{
let mut stats = self.stats.write().unwrap();
stats.bytes_decrypted += plaintext.len() as u64;
stats.decrypt_ops += 1;
let total_time = stats.avg_decrypt_time_us * (stats.decrypt_ops - 1) as f64;
stats.avg_decrypt_time_us =
(total_time + elapsed.as_micros() as f64) / stats.decrypt_ops as f64;
}
Ok(plaintext)
}
pub fn rotate_keys(&self) {
let mut active = self.active_key.write().unwrap();
let mut previous = self.previous_keys.write().unwrap();
let old_key = active.clone();
previous.insert(old_key.key_id, old_key);
let new_key_id = self.next_key_id.fetch_add(1, Ordering::Relaxed);
*active = EncryptionKey::new(new_key_id, self.config.algorithm);
*self.last_rotation.write().unwrap() = Instant::now();
self.stats.write().unwrap().key_rotations += 1;
while previous.len() > 10 {
if let Some(oldest_id) = previous.keys().min().copied() {
previous.remove(&oldest_id);
}
}
}
pub fn needs_rotation(&self) -> bool {
let last = *self.last_rotation.read().unwrap();
last.elapsed() >= self.config.key_rotation_interval
}
pub fn stats(&self) -> EncryptionStats {
self.stats.read().unwrap().clone()
}
pub fn current_key_id(&self) -> u64 {
self.active_key.read().unwrap().key_id
}
pub fn config(&self) -> &EncryptionConfig {
&self.config
}
}
impl fmt::Debug for MemoryEncryption {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MemoryEncryption")
.field("config", &self.config)
.field("current_key_id", &self.current_key_id())
.field("stats", &self.stats())
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AccessLevel {
Deny,
ReadOnly,
#[default]
ReadWrite,
Full,
}
#[derive(Debug, Clone)]
pub struct ResourceLimits {
pub max_memory_bytes: u64,
pub max_execution_time: Duration,
pub max_messages_per_sec: u32,
pub max_k2k_connections: u32,
pub max_checkpoint_size: u64,
pub max_queue_depth: u32,
}
impl Default for ResourceLimits {
fn default() -> Self {
Self {
max_memory_bytes: 1024 * 1024 * 1024, max_execution_time: Duration::from_secs(60),
max_messages_per_sec: 10000,
max_k2k_connections: 100,
max_checkpoint_size: 100 * 1024 * 1024, max_queue_depth: 4096,
}
}
}
impl ResourceLimits {
pub fn new() -> Self {
Self::default()
}
pub fn with_max_memory(mut self, bytes: u64) -> Self {
self.max_memory_bytes = bytes;
self
}
pub fn with_max_execution_time(mut self, duration: Duration) -> Self {
self.max_execution_time = duration;
self
}
pub fn with_max_messages_per_sec(mut self, count: u32) -> Self {
self.max_messages_per_sec = count;
self
}
pub fn with_max_k2k_connections(mut self, count: u32) -> Self {
self.max_k2k_connections = count;
self
}
pub fn restrictive() -> Self {
Self {
max_memory_bytes: 256 * 1024 * 1024, max_execution_time: Duration::from_secs(10),
max_messages_per_sec: 1000,
max_k2k_connections: 10,
max_checkpoint_size: 10 * 1024 * 1024, max_queue_depth: 256,
}
}
pub fn permissive() -> Self {
Self {
max_memory_bytes: 8 * 1024 * 1024 * 1024, max_execution_time: Duration::from_secs(3600),
max_messages_per_sec: 1_000_000,
max_k2k_connections: 1000,
max_checkpoint_size: 1024 * 1024 * 1024, max_queue_depth: 65536,
}
}
}
#[derive(Debug, Clone)]
pub struct SandboxPolicy {
pub limits: ResourceLimits,
pub allowed_k2k_destinations: HashSet<String>,
pub denied_k2k_destinations: HashSet<String>,
pub memory_access: HashMap<String, AccessLevel>,
pub can_checkpoint: bool,
pub can_migrate: bool,
pub can_spawn: bool,
pub can_access_host: bool,
pub allowed_syscalls: HashSet<String>,
}
impl Default for SandboxPolicy {
fn default() -> Self {
Self {
limits: ResourceLimits::default(),
allowed_k2k_destinations: HashSet::new(),
denied_k2k_destinations: HashSet::new(),
memory_access: HashMap::new(),
can_checkpoint: true,
can_migrate: true,
can_spawn: false,
can_access_host: false,
allowed_syscalls: HashSet::new(),
}
}
}
impl SandboxPolicy {
pub fn new() -> Self {
Self::default()
}
pub fn with_limits(mut self, limits: ResourceLimits) -> Self {
self.limits = limits;
self
}
pub fn with_memory_limit(mut self, bytes: u64) -> Self {
self.limits.max_memory_bytes = bytes;
self
}
pub fn with_execution_timeout(mut self, timeout: Duration) -> Self {
self.limits.max_execution_time = timeout;
self
}
pub fn allow_k2k_to(mut self, destinations: &[&str]) -> Self {
self.allowed_k2k_destinations
.extend(destinations.iter().map(|s| s.to_string()));
self
}
pub fn deny_k2k_to(mut self, destinations: &[&str]) -> Self {
self.denied_k2k_destinations
.extend(destinations.iter().map(|s| s.to_string()));
self
}
pub fn with_memory_access(mut self, region: &str, access: AccessLevel) -> Self {
self.memory_access.insert(region.to_string(), access);
self
}
pub fn with_checkpoint(mut self, enabled: bool) -> Self {
self.can_checkpoint = enabled;
self
}
pub fn with_migration(mut self, enabled: bool) -> Self {
self.can_migrate = enabled;
self
}
pub fn with_spawn(mut self, enabled: bool) -> Self {
self.can_spawn = enabled;
self
}
pub fn with_host_access(mut self, enabled: bool) -> Self {
self.can_access_host = enabled;
self
}
pub fn restrictive() -> Self {
Self {
limits: ResourceLimits::restrictive(),
allowed_k2k_destinations: HashSet::new(),
denied_k2k_destinations: HashSet::new(),
memory_access: HashMap::new(),
can_checkpoint: false,
can_migrate: false,
can_spawn: false,
can_access_host: false,
allowed_syscalls: HashSet::new(),
}
}
pub fn permissive() -> Self {
Self {
limits: ResourceLimits::permissive(),
allowed_k2k_destinations: HashSet::new(),
denied_k2k_destinations: HashSet::new(),
memory_access: HashMap::new(),
can_checkpoint: true,
can_migrate: true,
can_spawn: true,
can_access_host: true,
allowed_syscalls: HashSet::new(),
}
}
pub fn is_k2k_allowed(&self, destination: &str) -> bool {
if self.denied_k2k_destinations.contains(destination) {
return false;
}
if self.allowed_k2k_destinations.is_empty() {
return true;
}
self.allowed_k2k_destinations.contains(destination)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ViolationType {
MemoryLimitExceeded {
used: u64,
limit: u64,
},
ExecutionTimeExceeded {
elapsed: Duration,
limit: Duration,
},
MessageRateExceeded {
rate: u32,
limit: u32,
},
UnauthorizedK2K {
destination: String,
},
UnauthorizedMemoryAccess {
region: String,
requested: AccessLevel,
},
CheckpointNotAllowed,
MigrationNotAllowed,
SpawnNotAllowed,
HostAccessNotAllowed,
}
impl fmt::Display for ViolationType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MemoryLimitExceeded { used, limit } => {
write!(f, "Memory limit exceeded: {} > {} bytes", used, limit)
}
Self::ExecutionTimeExceeded { elapsed, limit } => {
write!(f, "Execution time exceeded: {:?} > {:?}", elapsed, limit)
}
Self::MessageRateExceeded { rate, limit } => {
write!(f, "Message rate exceeded: {} > {} msg/s", rate, limit)
}
Self::UnauthorizedK2K { destination } => {
write!(f, "Unauthorized K2K to: {}", destination)
}
Self::UnauthorizedMemoryAccess { region, requested } => {
write!(
f,
"Unauthorized {:?} access to region: {}",
requested, region
)
}
Self::CheckpointNotAllowed => write!(f, "Checkpointing not allowed"),
Self::MigrationNotAllowed => write!(f, "Migration not allowed"),
Self::SpawnNotAllowed => write!(f, "Spawning not allowed"),
Self::HostAccessNotAllowed => write!(f, "Host memory access not allowed"),
}
}
}
#[derive(Debug, Clone)]
pub struct SandboxViolation {
pub violation_type: ViolationType,
pub kernel_id: KernelId,
pub timestamp: Instant,
pub context: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct SandboxStats {
pub total_checks: u64,
pub violations_detected: u64,
pub operations_blocked: u64,
pub current_memory_usage: u64,
pub current_message_rate: u32,
}
pub struct KernelSandbox {
policy: SandboxPolicy,
kernel_id: Option<KernelId>,
stats: RwLock<SandboxStats>,
violations: RwLock<Vec<SandboxViolation>>,
start_time: RwLock<Option<Instant>>,
message_count: AtomicU64,
last_rate_check: RwLock<Instant>,
}
impl KernelSandbox {
pub fn new(policy: SandboxPolicy) -> Self {
Self {
policy,
kernel_id: None,
stats: RwLock::new(SandboxStats::default()),
violations: RwLock::new(Vec::new()),
start_time: RwLock::new(None),
message_count: AtomicU64::new(0),
last_rate_check: RwLock::new(Instant::now()),
}
}
pub fn apply_to_kernel(&mut self, kernel_id: KernelId) {
self.kernel_id = Some(kernel_id);
*self.start_time.write().unwrap() = Some(Instant::now());
}
pub fn check_memory(&self, bytes: u64) -> Result<(), SandboxViolation> {
self.stats.write().unwrap().total_checks += 1;
if bytes > self.policy.limits.max_memory_bytes {
let violation = SandboxViolation {
violation_type: ViolationType::MemoryLimitExceeded {
used: bytes,
limit: self.policy.limits.max_memory_bytes,
},
kernel_id: self
.kernel_id
.clone()
.unwrap_or_else(|| KernelId("unknown".to_string())),
timestamp: Instant::now(),
context: None,
};
self.record_violation(violation.clone());
return Err(violation);
}
self.stats.write().unwrap().current_memory_usage = bytes;
Ok(())
}
pub fn check_execution_time(&self) -> Result<(), SandboxViolation> {
self.stats.write().unwrap().total_checks += 1;
if let Some(start) = *self.start_time.read().unwrap() {
let elapsed = start.elapsed();
if elapsed > self.policy.limits.max_execution_time {
let violation = SandboxViolation {
violation_type: ViolationType::ExecutionTimeExceeded {
elapsed,
limit: self.policy.limits.max_execution_time,
},
kernel_id: self
.kernel_id
.clone()
.unwrap_or_else(|| KernelId("unknown".to_string())),
timestamp: Instant::now(),
context: None,
};
self.record_violation(violation.clone());
return Err(violation);
}
}
Ok(())
}
pub fn check_k2k(&self, destination: &str) -> Result<(), SandboxViolation> {
self.stats.write().unwrap().total_checks += 1;
if !self.policy.is_k2k_allowed(destination) {
let violation = SandboxViolation {
violation_type: ViolationType::UnauthorizedK2K {
destination: destination.to_string(),
},
kernel_id: self
.kernel_id
.clone()
.unwrap_or_else(|| KernelId("unknown".to_string())),
timestamp: Instant::now(),
context: None,
};
self.record_violation(violation.clone());
return Err(violation);
}
Ok(())
}
pub fn check_checkpoint(&self) -> Result<(), SandboxViolation> {
self.stats.write().unwrap().total_checks += 1;
if !self.policy.can_checkpoint {
let violation = SandboxViolation {
violation_type: ViolationType::CheckpointNotAllowed,
kernel_id: self
.kernel_id
.clone()
.unwrap_or_else(|| KernelId("unknown".to_string())),
timestamp: Instant::now(),
context: None,
};
self.record_violation(violation.clone());
return Err(violation);
}
Ok(())
}
pub fn check_migration(&self) -> Result<(), SandboxViolation> {
self.stats.write().unwrap().total_checks += 1;
if !self.policy.can_migrate {
let violation = SandboxViolation {
violation_type: ViolationType::MigrationNotAllowed,
kernel_id: self
.kernel_id
.clone()
.unwrap_or_else(|| KernelId("unknown".to_string())),
timestamp: Instant::now(),
context: None,
};
self.record_violation(violation.clone());
return Err(violation);
}
Ok(())
}
pub fn record_message(&self) -> Result<(), SandboxViolation> {
self.message_count.fetch_add(1, Ordering::Relaxed);
let mut last_check = self.last_rate_check.write().unwrap();
if last_check.elapsed() >= Duration::from_secs(1) {
let count = self.message_count.swap(0, Ordering::Relaxed) as u32;
*last_check = Instant::now();
self.stats.write().unwrap().current_message_rate = count;
if count > self.policy.limits.max_messages_per_sec {
let violation = SandboxViolation {
violation_type: ViolationType::MessageRateExceeded {
rate: count,
limit: self.policy.limits.max_messages_per_sec,
},
kernel_id: self
.kernel_id
.clone()
.unwrap_or_else(|| KernelId("unknown".to_string())),
timestamp: Instant::now(),
context: None,
};
self.record_violation(violation.clone());
return Err(violation);
}
}
Ok(())
}
fn record_violation(&self, violation: SandboxViolation) {
let mut stats = self.stats.write().unwrap();
stats.violations_detected += 1;
stats.operations_blocked += 1;
self.violations.write().unwrap().push(violation);
}
pub fn violations(&self) -> Vec<SandboxViolation> {
self.violations.read().unwrap().clone()
}
pub fn stats(&self) -> SandboxStats {
self.stats.read().unwrap().clone()
}
pub fn policy(&self) -> &SandboxPolicy {
&self.policy
}
pub fn reset(&self) {
*self.stats.write().unwrap() = SandboxStats::default();
self.violations.write().unwrap().clear();
self.message_count.store(0, Ordering::Relaxed);
}
}
impl fmt::Debug for KernelSandbox {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("KernelSandbox")
.field("policy", &self.policy)
.field("kernel_id", &self.kernel_id)
.field("stats", &self.stats())
.field("violations_count", &self.violations.read().unwrap().len())
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ComplianceStandard {
SOC2,
GDPR,
HIPAA,
PCIDSS,
ISO27001,
FedRAMP,
NIST,
}
impl fmt::Display for ComplianceStandard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::SOC2 => write!(f, "SOC 2 Type II"),
Self::GDPR => write!(f, "GDPR"),
Self::HIPAA => write!(f, "HIPAA"),
Self::PCIDSS => write!(f, "PCI DSS"),
Self::ISO27001 => write!(f, "ISO 27001"),
Self::FedRAMP => write!(f, "FedRAMP"),
Self::NIST => write!(f, "NIST CSF"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ReportFormat {
#[default]
Json,
Html,
Markdown,
Pdf,
Csv,
}
#[derive(Debug, Clone)]
pub enum ComplianceStatus {
Compliant,
PartiallyCompliant {
notes: Vec<String>,
},
NonCompliant {
reasons: Vec<String>,
},
NotApplicable,
}
impl ComplianceStatus {
pub fn is_compliant(&self) -> bool {
matches!(self, Self::Compliant | Self::NotApplicable)
}
}
#[derive(Debug, Clone)]
pub struct ComplianceCheck {
pub id: String,
pub name: String,
pub standard: ComplianceStandard,
pub description: String,
pub status: ComplianceStatus,
pub evidence: Vec<String>,
pub recommendations: Vec<String>,
pub checked_at: SystemTime,
}
#[derive(Debug, Clone)]
pub struct ComplianceSummary {
pub total_checks: usize,
pub compliant: usize,
pub partially_compliant: usize,
pub non_compliant: usize,
pub not_applicable: usize,
pub compliance_percentage: f64,
}
#[derive(Debug, Clone)]
pub struct ComplianceReport {
pub id: String,
pub title: String,
pub standards: Vec<ComplianceStandard>,
pub checks: Vec<ComplianceCheck>,
pub summary: ComplianceSummary,
pub generated_at: SystemTime,
pub period_start: SystemTime,
pub period_end: SystemTime,
pub metadata: HashMap<String, String>,
}
impl ComplianceReport {
pub fn export(&self, format: ReportFormat) -> String {
match format {
ReportFormat::Json => self.to_json(),
ReportFormat::Html => self.to_html(),
ReportFormat::Markdown => self.to_markdown(),
ReportFormat::Pdf => self.to_markdown(), ReportFormat::Csv => self.to_csv(),
}
}
fn to_json(&self) -> String {
let mut json = String::new();
json.push_str("{\n");
json.push_str(&format!(" \"id\": \"{}\",\n", self.id));
json.push_str(&format!(" \"title\": \"{}\",\n", self.title));
json.push_str(&format!(
" \"standards\": [{}],\n",
self.standards
.iter()
.map(|s| format!("\"{}\"", s))
.collect::<Vec<_>>()
.join(", ")
));
json.push_str(" \"summary\": {\n");
json.push_str(&format!(
" \"total_checks\": {},\n",
self.summary.total_checks
));
json.push_str(&format!(" \"compliant\": {},\n", self.summary.compliant));
json.push_str(&format!(
" \"partially_compliant\": {},\n",
self.summary.partially_compliant
));
json.push_str(&format!(
" \"non_compliant\": {},\n",
self.summary.non_compliant
));
json.push_str(&format!(
" \"compliance_percentage\": {:.1}\n",
self.summary.compliance_percentage
));
json.push_str(" },\n");
json.push_str(&format!(" \"checks_count\": {}\n", self.checks.len()));
json.push_str("}\n");
json
}
fn to_html(&self) -> String {
let mut html = String::new();
html.push_str("<!DOCTYPE html>\n<html>\n<head>\n");
html.push_str(&format!("<title>{}</title>\n", self.title));
html.push_str("<style>body { font-family: sans-serif; } .compliant { color: green; } .non-compliant { color: red; }</style>\n");
html.push_str("</head>\n<body>\n");
html.push_str(&format!("<h1>{}</h1>\n", self.title));
html.push_str(&format!("<p>Report ID: {}</p>\n", self.id));
html.push_str("<h2>Summary</h2>\n");
html.push_str("<table>\n");
html.push_str(&format!(
"<tr><td>Total Checks</td><td>{}</td></tr>\n",
self.summary.total_checks
));
html.push_str(&format!(
"<tr><td>Compliant</td><td class=\"compliant\">{}</td></tr>\n",
self.summary.compliant
));
html.push_str(&format!(
"<tr><td>Non-Compliant</td><td class=\"non-compliant\">{}</td></tr>\n",
self.summary.non_compliant
));
html.push_str(&format!(
"<tr><td>Compliance</td><td>{:.1}%</td></tr>\n",
self.summary.compliance_percentage
));
html.push_str("</table>\n");
html.push_str("<h2>Checks</h2>\n");
for check in &self.checks {
html.push_str(&format!("<h3>{}</h3>\n", check.name));
html.push_str(&format!("<p>{}</p>\n", check.description));
}
html.push_str("</body>\n</html>\n");
html
}
fn to_markdown(&self) -> String {
let mut md = String::new();
md.push_str(&format!("# {}\n\n", self.title));
md.push_str(&format!("**Report ID:** {}\n\n", self.id));
md.push_str("## Summary\n\n");
md.push_str("| Metric | Value |\n");
md.push_str("|--------|-------|\n");
md.push_str(&format!(
"| Total Checks | {} |\n",
self.summary.total_checks
));
md.push_str(&format!("| Compliant | {} |\n", self.summary.compliant));
md.push_str(&format!(
"| Partially Compliant | {} |\n",
self.summary.partially_compliant
));
md.push_str(&format!(
"| Non-Compliant | {} |\n",
self.summary.non_compliant
));
md.push_str(&format!(
"| Compliance | {:.1}% |\n",
self.summary.compliance_percentage
));
md.push_str("\n## Detailed Checks\n\n");
for check in &self.checks {
let status_icon = match &check.status {
ComplianceStatus::Compliant => "✅",
ComplianceStatus::PartiallyCompliant { .. } => "⚠️",
ComplianceStatus::NonCompliant { .. } => "❌",
ComplianceStatus::NotApplicable => "➖",
};
md.push_str(&format!("### {} {}\n\n", status_icon, check.name));
md.push_str(&format!("{}\n\n", check.description));
}
md
}
fn to_csv(&self) -> String {
let mut csv = String::new();
csv.push_str("ID,Name,Standard,Status,Description\n");
for check in &self.checks {
let status = match &check.status {
ComplianceStatus::Compliant => "Compliant",
ComplianceStatus::PartiallyCompliant { .. } => "Partially Compliant",
ComplianceStatus::NonCompliant { .. } => "Non-Compliant",
ComplianceStatus::NotApplicable => "N/A",
};
csv.push_str(&format!(
"\"{}\",\"{}\",\"{}\",\"{}\",\"{}\"\n",
check.id, check.name, check.standard, status, check.description
));
}
csv
}
}
pub struct ComplianceReporter {
standards: HashSet<ComplianceStandard>,
organization: String,
metadata: HashMap<String, String>,
custom_checks: Vec<Box<dyn Fn() -> ComplianceCheck + Send + Sync>>,
}
impl ComplianceReporter {
pub fn new() -> Self {
Self {
standards: HashSet::new(),
organization: "Unknown".to_string(),
metadata: HashMap::new(),
custom_checks: Vec::new(),
}
}
pub fn with_standard(mut self, standard: ComplianceStandard) -> Self {
self.standards.insert(standard);
self
}
pub fn with_organization(mut self, org: &str) -> Self {
self.organization = org.to_string();
self
}
pub fn with_metadata(mut self, key: &str, value: &str) -> Self {
self.metadata.insert(key.to_string(), value.to_string());
self
}
pub fn generate_report(&self, _format: ReportFormat) -> ComplianceReport {
let mut checks = Vec::new();
let now = SystemTime::now();
for standard in &self.standards {
checks.extend(self.generate_standard_checks(*standard));
}
for check_fn in &self.custom_checks {
checks.push(check_fn());
}
let total = checks.len();
let compliant = checks
.iter()
.filter(|c| matches!(c.status, ComplianceStatus::Compliant))
.count();
let partial = checks
.iter()
.filter(|c| matches!(c.status, ComplianceStatus::PartiallyCompliant { .. }))
.count();
let non_compliant = checks
.iter()
.filter(|c| matches!(c.status, ComplianceStatus::NonCompliant { .. }))
.count();
let na = checks
.iter()
.filter(|c| matches!(c.status, ComplianceStatus::NotApplicable))
.count();
let applicable = total - na;
let compliance_pct = if applicable > 0 {
((compliant as f64 + partial as f64 * 0.5) / applicable as f64) * 100.0
} else {
100.0
};
ComplianceReport {
id: format!(
"RPT-{}",
now.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs()
),
title: format!("{} Compliance Report", self.organization),
standards: self.standards.iter().copied().collect(),
checks,
summary: ComplianceSummary {
total_checks: total,
compliant,
partially_compliant: partial,
non_compliant,
not_applicable: na,
compliance_percentage: compliance_pct,
},
generated_at: now,
period_start: now - Duration::from_secs(30 * 24 * 60 * 60), period_end: now,
metadata: self.metadata.clone(),
}
}
fn generate_standard_checks(&self, standard: ComplianceStandard) -> Vec<ComplianceCheck> {
let now = SystemTime::now();
match standard {
ComplianceStandard::SOC2 => vec![
ComplianceCheck {
id: "SOC2-CC1.1".to_string(),
name: "Control Environment".to_string(),
standard,
description:
"The entity demonstrates commitment to integrity and ethical values."
.to_string(),
status: ComplianceStatus::Compliant,
evidence: vec![
"Audit logging enabled".to_string(),
"Access controls implemented".to_string(),
],
recommendations: vec![],
checked_at: now,
},
ComplianceCheck {
id: "SOC2-CC6.1".to_string(),
name: "Logical Access Controls".to_string(),
standard,
description:
"Logical access security software, infrastructure, and architectures."
.to_string(),
status: ComplianceStatus::Compliant,
evidence: vec![
"Kernel sandboxing available".to_string(),
"Memory encryption available".to_string(),
],
recommendations: vec![],
checked_at: now,
},
ComplianceCheck {
id: "SOC2-CC7.2".to_string(),
name: "System Monitoring".to_string(),
standard,
description: "System components are monitored and anomalies are identified."
.to_string(),
status: ComplianceStatus::Compliant,
evidence: vec![
"Health monitoring enabled".to_string(),
"GPU memory dashboard available".to_string(),
],
recommendations: vec![],
checked_at: now,
},
],
ComplianceStandard::GDPR => vec![
ComplianceCheck {
id: "GDPR-32".to_string(),
name: "Security of Processing".to_string(),
standard,
description: "Implement appropriate technical and organizational measures."
.to_string(),
status: ComplianceStatus::Compliant,
evidence: vec!["Memory encryption available".to_string()],
recommendations: vec!["Consider enabling encryption by default".to_string()],
checked_at: now,
},
ComplianceCheck {
id: "GDPR-33".to_string(),
name: "Breach Notification".to_string(),
standard,
description: "Notify supervisory authority of personal data breach."
.to_string(),
status: ComplianceStatus::PartiallyCompliant {
notes: vec!["Audit logging available but breach detection not automated"
.to_string()],
},
evidence: vec!["Audit logging enabled".to_string()],
recommendations: vec!["Add automated breach detection".to_string()],
checked_at: now,
},
],
ComplianceStandard::HIPAA => vec![
ComplianceCheck {
id: "HIPAA-164.312(a)".to_string(),
name: "Access Control".to_string(),
standard,
description: "Implement technical policies for electronic PHI access."
.to_string(),
status: ComplianceStatus::Compliant,
evidence: vec![
"Kernel sandboxing available".to_string(),
"Access levels configurable".to_string(),
],
recommendations: vec![],
checked_at: now,
},
ComplianceCheck {
id: "HIPAA-164.312(e)".to_string(),
name: "Transmission Security".to_string(),
standard,
description: "Implement security measures for ePHI transmission.".to_string(),
status: ComplianceStatus::Compliant,
evidence: vec!["Memory encryption for data at rest".to_string()],
recommendations: vec!["Implement TLS for network K2K".to_string()],
checked_at: now,
},
],
ComplianceStandard::PCIDSS => vec![
ComplianceCheck {
id: "PCI-3.4".to_string(),
name: "Render PAN Unreadable".to_string(),
standard,
description: "Render PAN unreadable anywhere it is stored.".to_string(),
status: ComplianceStatus::Compliant,
evidence: vec!["AES-256-GCM encryption available".to_string()],
recommendations: vec![],
checked_at: now,
},
ComplianceCheck {
id: "PCI-10.1".to_string(),
name: "Audit Trails".to_string(),
standard,
description: "Implement audit trails to link access to individual users."
.to_string(),
status: ComplianceStatus::Compliant,
evidence: vec!["Comprehensive audit logging".to_string()],
recommendations: vec![],
checked_at: now,
},
],
ComplianceStandard::ISO27001 => vec![ComplianceCheck {
id: "ISO-A.10.1".to_string(),
name: "Cryptographic Controls".to_string(),
standard,
description: "Policy on use of cryptographic controls.".to_string(),
status: ComplianceStatus::Compliant,
evidence: vec!["Multiple encryption algorithms supported".to_string()],
recommendations: vec![],
checked_at: now,
}],
ComplianceStandard::FedRAMP => vec![ComplianceCheck {
id: "FedRAMP-SC-28".to_string(),
name: "Protection of Information at Rest".to_string(),
standard,
description: "Protect confidentiality and integrity of information at rest."
.to_string(),
status: ComplianceStatus::Compliant,
evidence: vec!["FIPS-compliant algorithms available".to_string()],
recommendations: vec![],
checked_at: now,
}],
ComplianceStandard::NIST => vec![
ComplianceCheck {
id: "NIST-PR.DS-1".to_string(),
name: "Data-at-rest Protection".to_string(),
standard,
description: "Data-at-rest is protected.".to_string(),
status: ComplianceStatus::Compliant,
evidence: vec!["Memory encryption module".to_string()],
recommendations: vec![],
checked_at: now,
},
ComplianceCheck {
id: "NIST-DE.CM-1".to_string(),
name: "Network Monitoring".to_string(),
standard,
description: "The network is monitored to detect cybersecurity events."
.to_string(),
status: ComplianceStatus::Compliant,
evidence: vec![
"Observability context".to_string(),
"GPU profiler integration".to_string(),
],
recommendations: vec![],
checked_at: now,
},
],
}
}
}
impl Default for ComplianceReporter {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for ComplianceReporter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ComplianceReporter")
.field("standards", &self.standards)
.field("organization", &self.organization)
.field("metadata", &self.metadata)
.field("custom_checks_count", &self.custom_checks.len())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encryption_config_builder() {
let config = EncryptionConfig::new()
.with_algorithm(EncryptionAlgorithm::ChaCha20Poly1305)
.with_key_rotation_interval(Duration::from_secs(7200))
.with_control_block_encryption(false);
assert_eq!(config.algorithm, EncryptionAlgorithm::ChaCha20Poly1305);
assert_eq!(config.key_rotation_interval, Duration::from_secs(7200));
assert!(!config.encrypt_control_blocks);
}
#[test]
fn test_encryption_key_creation() {
let key = EncryptionKey::new(1, EncryptionAlgorithm::Aes256Gcm);
assert_eq!(key.key_id, 1);
assert_eq!(key.key_size(), 32);
assert!(!key.is_expired());
}
#[test]
fn test_encrypt_decrypt_roundtrip() {
let encryption = MemoryEncryption::new(EncryptionConfig::default());
let plaintext = b"Hello, GPU World!";
let encrypted = encryption.encrypt_region(plaintext);
assert_ne!(encrypted.ciphertext[..plaintext.len()], plaintext[..]);
let decrypted = encryption.decrypt_region(&encrypted).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_key_rotation() {
let encryption = MemoryEncryption::new(EncryptionConfig::default());
let initial_key_id = encryption.current_key_id();
encryption.rotate_keys();
assert_eq!(encryption.current_key_id(), initial_key_id + 1);
let stats = encryption.stats();
assert_eq!(stats.key_rotations, 1);
}
#[test]
fn test_decrypt_with_old_key() {
let encryption = MemoryEncryption::new(EncryptionConfig::default());
let plaintext = b"Secret data";
let encrypted = encryption.encrypt_region(plaintext);
let old_key_id = encrypted.key_id;
encryption.rotate_keys();
assert_ne!(encryption.current_key_id(), old_key_id);
let decrypted = encryption.decrypt_region(&encrypted).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_encryption_stats() {
let encryption = MemoryEncryption::new(EncryptionConfig::default());
let data = vec![0u8; 1024];
for _ in 0..10 {
let encrypted = encryption.encrypt_region(&data);
let _ = encryption.decrypt_region(&encrypted);
}
let stats = encryption.stats();
assert_eq!(stats.encrypt_ops, 10);
assert_eq!(stats.decrypt_ops, 10);
assert_eq!(stats.bytes_encrypted, 10240);
}
#[test]
fn test_resource_limits_builder() {
let limits = ResourceLimits::new()
.with_max_memory(512 * 1024 * 1024)
.with_max_execution_time(Duration::from_secs(30));
assert_eq!(limits.max_memory_bytes, 512 * 1024 * 1024);
assert_eq!(limits.max_execution_time, Duration::from_secs(30));
}
#[test]
fn test_sandbox_policy_k2k() {
let policy = SandboxPolicy::new()
.allow_k2k_to(&["trusted_kernel", "another_trusted"])
.deny_k2k_to(&["malicious_kernel"]);
assert!(policy.is_k2k_allowed("trusted_kernel"));
assert!(policy.is_k2k_allowed("another_trusted"));
assert!(!policy.is_k2k_allowed("malicious_kernel"));
assert!(!policy.is_k2k_allowed("unknown_kernel")); }
#[test]
fn test_sandbox_memory_check() {
let policy = SandboxPolicy::new().with_memory_limit(1024);
let sandbox = KernelSandbox::new(policy);
assert!(sandbox.check_memory(512).is_ok());
let result = sandbox.check_memory(2048);
assert!(result.is_err());
if let Err(violation) = result {
assert!(matches!(
violation.violation_type,
ViolationType::MemoryLimitExceeded { .. }
));
}
}
#[test]
fn test_sandbox_k2k_check() {
let policy = SandboxPolicy::new().deny_k2k_to(&["blocked"]);
let sandbox = KernelSandbox::new(policy);
assert!(sandbox.check_k2k("allowed_dest").is_ok());
assert!(sandbox.check_k2k("blocked").is_err());
}
#[test]
fn test_sandbox_checkpoint_check() {
let policy = SandboxPolicy::restrictive();
let sandbox = KernelSandbox::new(policy);
assert!(sandbox.check_checkpoint().is_err());
let permissive = SandboxPolicy::permissive();
let sandbox2 = KernelSandbox::new(permissive);
assert!(sandbox2.check_checkpoint().is_ok());
}
#[test]
fn test_sandbox_stats() {
let policy = SandboxPolicy::new().with_memory_limit(1024);
let sandbox = KernelSandbox::new(policy);
let _ = sandbox.check_memory(512);
let _ = sandbox.check_memory(2048); let _ = sandbox.check_k2k("dest");
let stats = sandbox.stats();
assert_eq!(stats.total_checks, 3);
assert_eq!(stats.violations_detected, 1);
}
#[test]
fn test_sandbox_violations_list() {
let policy = SandboxPolicy::restrictive();
let sandbox = KernelSandbox::new(policy);
let _ = sandbox.check_checkpoint();
let _ = sandbox.check_migration();
let violations = sandbox.violations();
assert_eq!(violations.len(), 2);
}
#[test]
fn test_compliance_reporter_creation() {
let reporter = ComplianceReporter::new()
.with_standard(ComplianceStandard::SOC2)
.with_standard(ComplianceStandard::GDPR)
.with_organization("Test Org");
assert_eq!(reporter.standards.len(), 2);
assert!(reporter.standards.contains(&ComplianceStandard::SOC2));
assert!(reporter.standards.contains(&ComplianceStandard::GDPR));
}
#[test]
fn test_generate_soc2_report() {
let reporter = ComplianceReporter::new()
.with_standard(ComplianceStandard::SOC2)
.with_organization("Acme Corp");
let report = reporter.generate_report(ReportFormat::Json);
assert!(!report.checks.is_empty());
assert!(report.summary.total_checks > 0);
assert!(report.title.contains("Acme Corp"));
}
#[test]
fn test_report_json_export() {
let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::HIPAA);
let report = reporter.generate_report(ReportFormat::Json);
let json = report.export(ReportFormat::Json);
assert!(json.contains("\"id\""));
assert!(json.contains("\"summary\""));
}
#[test]
fn test_report_markdown_export() {
let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::NIST);
let report = reporter.generate_report(ReportFormat::Markdown);
let md = report.export(ReportFormat::Markdown);
assert!(md.contains("# "));
assert!(md.contains("## Summary"));
assert!(md.contains("| Metric | Value |"));
}
#[test]
fn test_report_html_export() {
let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::PCIDSS);
let report = reporter.generate_report(ReportFormat::Html);
let html = report.export(ReportFormat::Html);
assert!(html.contains("<!DOCTYPE html>"));
assert!(html.contains("<h1>"));
}
#[test]
fn test_report_csv_export() {
let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::ISO27001);
let report = reporter.generate_report(ReportFormat::Csv);
let csv = report.export(ReportFormat::Csv);
assert!(csv.contains("ID,Name,Standard,Status,Description"));
}
#[test]
fn test_compliance_summary_calculation() {
let reporter = ComplianceReporter::new()
.with_standard(ComplianceStandard::SOC2)
.with_standard(ComplianceStandard::GDPR)
.with_standard(ComplianceStandard::HIPAA);
let report = reporter.generate_report(ReportFormat::Json);
let sum = report.summary.compliant
+ report.summary.partially_compliant
+ report.summary.non_compliant
+ report.summary.not_applicable;
assert_eq!(sum, report.summary.total_checks);
}
#[test]
fn test_compliance_status_is_compliant() {
assert!(ComplianceStatus::Compliant.is_compliant());
assert!(ComplianceStatus::NotApplicable.is_compliant());
assert!(!ComplianceStatus::NonCompliant { reasons: vec![] }.is_compliant());
assert!(!ComplianceStatus::PartiallyCompliant { notes: vec![] }.is_compliant());
}
#[test]
fn test_all_standards() {
let reporter = ComplianceReporter::new()
.with_standard(ComplianceStandard::SOC2)
.with_standard(ComplianceStandard::GDPR)
.with_standard(ComplianceStandard::HIPAA)
.with_standard(ComplianceStandard::PCIDSS)
.with_standard(ComplianceStandard::ISO27001)
.with_standard(ComplianceStandard::FedRAMP)
.with_standard(ComplianceStandard::NIST);
let report = reporter.generate_report(ReportFormat::Json);
assert_eq!(report.standards.len(), 7);
}
}