Files
claudia/src-tauri/tests/sandbox/integration/process_isolation.rs
2025-06-19 19:24:01 +05:30

234 lines
7.5 KiB
Rust

//! Integration tests for process isolation in sandbox
use crate::sandbox::common::*;
use crate::skip_if_unsupported;
use claudia_lib::sandbox::executor::SandboxExecutor;
use gaol::profile::{Profile, Operation, PathPattern, AddressPattern};
use serial_test::serial;
use tempfile::TempDir;
/// Test that process spawning is always forbidden
#[test]
#[serial]
fn test_process_spawn_forbidden() {
skip_if_unsupported!();
// Create test project
let test_fs = TestFileSystem::new().expect("Failed to create test filesystem");
// Create profile with various permissions (process spawn should still be blocked)
let operations = vec![
Operation::FileReadAll(PathPattern::Subpath(test_fs.project_path.clone())),
Operation::NetworkOutbound(AddressPattern::All),
];
let profile = match Profile::new(operations) {
Ok(p) => p,
Err(_) => {
eprintln!("Failed to create profile - operation not supported");
return;
}
};
// Create test binary that tries to spawn a process
let test_code = test_code::spawn_process();
let binary_dir = TempDir::new().expect("Failed to create temp dir");
let binary_path = create_test_binary("test_spawn", test_code, binary_dir.path())
.expect("Failed to create test binary");
// Execute in sandbox
let executor = SandboxExecutor::new(profile, test_fs.project_path.clone());
match executor.execute_sandboxed_spawn(
&binary_path.to_string_lossy(),
&[],
&test_fs.project_path,
) {
Ok(mut child) => {
let status = child.wait().expect("Failed to wait for child");
// Process spawning might not be blocked on all platforms
if status.success() {
eprintln!("WARNING: Process spawning was not blocked");
// macOS sandbox might have limitations
if std::env::consts::OS != "linux" {
eprintln!("Process spawning might not be fully blocked on {}", std::env::consts::OS);
} else {
panic!("Process spawning should be blocked on Linux");
}
}
}
Err(e) => {
eprintln!("Sandbox execution failed: {} (may be expected in CI)", e);
}
}
}
/// Test that fork is blocked
#[test]
#[serial]
#[cfg(unix)]
fn test_fork_forbidden() {
skip_if_unsupported!();
// Create test project
let test_fs = TestFileSystem::new().expect("Failed to create test filesystem");
// Create minimal profile
let operations = vec![
Operation::FileReadAll(PathPattern::Subpath(test_fs.project_path.clone())),
];
let profile = match Profile::new(operations) {
Ok(p) => p,
Err(_) => {
eprintln!("Failed to create profile - operation not supported");
return;
}
};
// Create test binary that tries to fork
let test_code = test_code::fork_process();
let binary_dir = TempDir::new().expect("Failed to create temp dir");
let binary_path = create_test_binary_with_deps("test_fork", test_code, binary_dir.path(), &[("libc", "0.2")])
.expect("Failed to create test binary");
// Execute in sandbox
let executor = SandboxExecutor::new(profile, test_fs.project_path.clone());
match executor.execute_sandboxed_spawn(
&binary_path.to_string_lossy(),
&[],
&test_fs.project_path,
) {
Ok(mut child) => {
let status = child.wait().expect("Failed to wait for child");
// Fork might not be blocked on all platforms
if status.success() {
eprintln!("WARNING: Fork was not blocked (platform limitation)");
if std::env::consts::OS == "linux" {
panic!("Fork should be blocked on Linux");
}
}
}
Err(e) => {
eprintln!("Sandbox execution failed: {} (may be expected in CI)", e);
}
}
}
/// Test that exec is blocked
#[test]
#[serial]
#[cfg(unix)]
fn test_exec_forbidden() {
skip_if_unsupported!();
// Create test project
let test_fs = TestFileSystem::new().expect("Failed to create test filesystem");
// Create minimal profile
let operations = vec![
Operation::FileReadAll(PathPattern::Subpath(test_fs.project_path.clone())),
];
let profile = match Profile::new(operations) {
Ok(p) => p,
Err(_) => {
eprintln!("Failed to create profile - operation not supported");
return;
}
};
// Create test binary that tries to exec
let test_code = test_code::exec_process();
let binary_dir = TempDir::new().expect("Failed to create temp dir");
let binary_path = create_test_binary_with_deps("test_exec", test_code, binary_dir.path(), &[("libc", "0.2")])
.expect("Failed to create test binary");
// Execute in sandbox
let executor = SandboxExecutor::new(profile, test_fs.project_path.clone());
match executor.execute_sandboxed_spawn(
&binary_path.to_string_lossy(),
&[],
&test_fs.project_path,
) {
Ok(mut child) => {
let status = child.wait().expect("Failed to wait for child");
// Exec might not be blocked on all platforms
if status.success() {
eprintln!("WARNING: Exec was not blocked (platform limitation)");
if std::env::consts::OS == "linux" {
panic!("Exec should be blocked on Linux");
}
}
}
Err(e) => {
eprintln!("Sandbox execution failed: {} (may be expected in CI)", e);
}
}
}
/// Test thread creation is allowed
#[test]
#[serial]
fn test_thread_creation_allowed() {
skip_if_unsupported!();
// Create test project
let test_fs = TestFileSystem::new().expect("Failed to create test filesystem");
// Create minimal profile
let operations = vec![
Operation::FileReadAll(PathPattern::Subpath(test_fs.project_path.clone())),
];
let profile = match Profile::new(operations) {
Ok(p) => p,
Err(_) => {
eprintln!("Failed to create profile - operation not supported");
return;
}
};
// Create test binary that creates threads
let test_code = r#"
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
thread::sleep(Duration::from_millis(100));
42
});
match handle.join() {
Ok(value) => {
println!("SUCCESS: Thread returned {}", value);
}
Err(_) => {
eprintln!("FAILURE: Thread panicked");
std::process::exit(1);
}
}
}
"#;
let binary_dir = TempDir::new().expect("Failed to create temp dir");
let binary_path = create_test_binary("test_thread", test_code, binary_dir.path())
.expect("Failed to create test binary");
// Execute in sandbox
let executor = SandboxExecutor::new(profile, test_fs.project_path.clone());
match executor.execute_sandboxed_spawn(
&binary_path.to_string_lossy(),
&[],
&test_fs.project_path,
) {
Ok(mut child) => {
let status = child.wait().expect("Failed to wait for child");
assert!(status.success(), "Thread creation should be allowed");
}
Err(e) => {
eprintln!("Sandbox execution failed: {} (may be expected in CI)", e);
}
}
}