init: push source

This commit is contained in:
Mufeed VH
2025-06-19 19:24:01 +05:30
commit 8e76d016d4
136 changed files with 38177 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
//! Unit tests for SandboxExecutor
use claudia_lib::sandbox::executor::{SandboxExecutor, should_activate_sandbox};
use gaol::profile::{Profile, Operation, PathPattern, AddressPattern};
use std::env;
use std::path::PathBuf;
/// Create a simple test profile
fn create_test_profile(project_path: PathBuf) -> Profile {
let operations = vec![
Operation::FileReadAll(PathPattern::Subpath(project_path)),
Operation::NetworkOutbound(AddressPattern::All),
];
Profile::new(operations).expect("Failed to create test profile")
}
#[test]
fn test_executor_creation() {
let project_path = PathBuf::from("/test/project");
let profile = create_test_profile(project_path.clone());
let _executor = SandboxExecutor::new(profile, project_path);
// Executor should be created successfully
}
#[test]
fn test_should_activate_sandbox_env_var() {
// Test when env var is not set
env::remove_var("GAOL_SANDBOX_ACTIVE");
assert!(!should_activate_sandbox(), "Should not activate when env var is not set");
// Test when env var is set to "1"
env::set_var("GAOL_SANDBOX_ACTIVE", "1");
assert!(should_activate_sandbox(), "Should activate when env var is '1'");
// Test when env var is set to other value
env::set_var("GAOL_SANDBOX_ACTIVE", "0");
assert!(!should_activate_sandbox(), "Should not activate when env var is not '1'");
// Clean up
env::remove_var("GAOL_SANDBOX_ACTIVE");
}
#[test]
fn test_prepare_sandboxed_command() {
let project_path = PathBuf::from("/test/project");
let profile = create_test_profile(project_path.clone());
let executor = SandboxExecutor::new(profile, project_path.clone());
let _cmd = executor.prepare_sandboxed_command("echo", &["hello"], &project_path);
// The command should have sandbox environment variables set
// Note: We can't easily test Command internals, but we can verify it doesn't panic
}
#[test]
fn test_executor_with_empty_profile() {
let project_path = PathBuf::from("/test/project");
let profile = Profile::new(vec![]).expect("Failed to create empty profile");
let executor = SandboxExecutor::new(profile, project_path.clone());
let _cmd = executor.prepare_sandboxed_command("echo", &["test"], &project_path);
// Should handle empty profile gracefully
}
#[test]
fn test_executor_with_complex_profile() {
let project_path = PathBuf::from("/test/project");
let operations = vec![
Operation::FileReadAll(PathPattern::Subpath(project_path.clone())),
Operation::FileReadAll(PathPattern::Subpath(PathBuf::from("/usr/lib"))),
Operation::FileReadAll(PathPattern::Literal(PathBuf::from("/etc/hosts"))),
Operation::FileReadMetadata(PathPattern::Subpath(PathBuf::from("/"))),
Operation::NetworkOutbound(AddressPattern::All),
Operation::NetworkOutbound(AddressPattern::Tcp(443)),
Operation::SystemInfoRead,
];
// Only create profile with supported operations
let filtered_ops: Vec<_> = operations.into_iter()
.filter(|op| {
use gaol::profile::{OperationSupport, OperationSupportLevel};
matches!(op.support(), OperationSupportLevel::CanBeAllowed)
})
.collect();
if !filtered_ops.is_empty() {
let profile = Profile::new(filtered_ops).expect("Failed to create complex profile");
let executor = SandboxExecutor::new(profile, project_path.clone());
let _cmd = executor.prepare_sandboxed_command("echo", &["test"], &project_path);
}
}
#[test]
fn test_command_environment_setup() {
let project_path = PathBuf::from("/test/project");
let profile = create_test_profile(project_path.clone());
let executor = SandboxExecutor::new(profile, project_path.clone());
// Test with various arguments
let _cmd1 = executor.prepare_sandboxed_command("ls", &[], &project_path);
let _cmd2 = executor.prepare_sandboxed_command("cat", &["file.txt"], &project_path);
let _cmd3 = executor.prepare_sandboxed_command("grep", &["-r", "pattern", "."], &project_path);
// Commands should be prepared without panic
}
#[test]
#[cfg(unix)]
fn test_spawn_sandboxed_process() {
use crate::sandbox::common::is_sandboxing_supported;
if !is_sandboxing_supported() {
return;
}
let project_path = env::current_dir().unwrap_or_else(|_| PathBuf::from("/tmp"));
let profile = create_test_profile(project_path.clone());
let executor = SandboxExecutor::new(profile, project_path.clone());
// Try to spawn a simple command
let result = executor.execute_sandboxed_spawn("echo", &["sandbox test"], &project_path);
// On supported platforms, this should either succeed or fail gracefully
match result {
Ok(mut child) => {
// If spawned successfully, wait for it to complete
let _ = child.wait();
}
Err(e) => {
// Sandboxing might fail due to permissions or platform limitations
println!("Sandbox spawn failed (expected in some environments): {e}");
}
}
}

View File

@@ -0,0 +1,7 @@
//! Unit tests for sandbox components
#[cfg(test)]
mod profile_builder;
#[cfg(test)]
mod platform;
#[cfg(test)]
mod executor;

View File

@@ -0,0 +1,148 @@
//! Unit tests for platform capabilities
use claudia_lib::sandbox::platform::{get_platform_capabilities, is_sandboxing_available};
use std::env;
use pretty_assertions::assert_eq;
#[test]
fn test_sandboxing_availability() {
let is_available = is_sandboxing_available();
let expected = matches!(env::consts::OS, "linux" | "macos" | "freebsd");
assert_eq!(
is_available, expected,
"Sandboxing availability should match platform support"
);
}
#[test]
fn test_platform_capabilities_structure() {
let caps = get_platform_capabilities();
// Verify basic structure
assert_eq!(caps.os, env::consts::OS, "OS should match current platform");
assert!(!caps.operations.is_empty() || !caps.sandboxing_supported,
"Should have operations if sandboxing is supported");
assert!(!caps.notes.is_empty(), "Should have platform-specific notes");
}
#[test]
#[cfg(target_os = "linux")]
fn test_linux_capabilities() {
let caps = get_platform_capabilities();
assert_eq!(caps.os, "linux");
assert!(caps.sandboxing_supported);
// Verify Linux-specific capabilities
let file_read = caps.operations.iter()
.find(|op| op.operation == "file_read_all")
.expect("file_read_all should be present");
assert_eq!(file_read.support_level, "can_be_allowed");
let metadata_read = caps.operations.iter()
.find(|op| op.operation == "file_read_metadata")
.expect("file_read_metadata should be present");
assert_eq!(metadata_read.support_level, "cannot_be_precisely");
let network_all = caps.operations.iter()
.find(|op| op.operation == "network_outbound_all")
.expect("network_outbound_all should be present");
assert_eq!(network_all.support_level, "can_be_allowed");
let network_tcp = caps.operations.iter()
.find(|op| op.operation == "network_outbound_tcp")
.expect("network_outbound_tcp should be present");
assert_eq!(network_tcp.support_level, "cannot_be_precisely");
let system_info = caps.operations.iter()
.find(|op| op.operation == "system_info_read")
.expect("system_info_read should be present");
assert_eq!(system_info.support_level, "never");
}
#[test]
#[cfg(target_os = "macos")]
fn test_macos_capabilities() {
let caps = get_platform_capabilities();
assert_eq!(caps.os, "macos");
assert!(caps.sandboxing_supported);
// Verify macOS-specific capabilities
let file_read = caps.operations.iter()
.find(|op| op.operation == "file_read_all")
.expect("file_read_all should be present");
assert_eq!(file_read.support_level, "can_be_allowed");
let metadata_read = caps.operations.iter()
.find(|op| op.operation == "file_read_metadata")
.expect("file_read_metadata should be present");
assert_eq!(metadata_read.support_level, "can_be_allowed");
let network_tcp = caps.operations.iter()
.find(|op| op.operation == "network_outbound_tcp")
.expect("network_outbound_tcp should be present");
assert_eq!(network_tcp.support_level, "can_be_allowed");
let system_info = caps.operations.iter()
.find(|op| op.operation == "system_info_read")
.expect("system_info_read should be present");
assert_eq!(system_info.support_level, "can_be_allowed");
}
#[test]
#[cfg(target_os = "freebsd")]
fn test_freebsd_capabilities() {
let caps = get_platform_capabilities();
assert_eq!(caps.os, "freebsd");
assert!(caps.sandboxing_supported);
// Verify FreeBSD-specific capabilities
let file_read = caps.operations.iter()
.find(|op| op.operation == "file_read_all")
.expect("file_read_all should be present");
assert_eq!(file_read.support_level, "never");
let system_info = caps.operations.iter()
.find(|op| op.operation == "system_info_read")
.expect("system_info_read should be present");
assert_eq!(system_info.support_level, "always");
}
#[test]
#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "freebsd")))]
fn test_unsupported_platform_capabilities() {
let caps = get_platform_capabilities();
assert!(!caps.sandboxing_supported);
assert_eq!(caps.operations.len(), 0);
assert!(caps.notes.iter().any(|note| note.contains("not supported")));
}
#[test]
fn test_all_operations_have_descriptions() {
let caps = get_platform_capabilities();
for op in &caps.operations {
assert!(!op.description.is_empty(),
"Operation {} should have a description", op.operation);
assert!(!op.support_level.is_empty(),
"Operation {} should have a support level", op.operation);
}
}
#[test]
fn test_support_level_values() {
let caps = get_platform_capabilities();
let valid_levels = ["never", "can_be_allowed", "cannot_be_precisely", "always"];
for op in &caps.operations {
assert!(
valid_levels.contains(&op.support_level.as_str()),
"Operation {} has invalid support level: {}",
op.operation,
op.support_level
);
}
}

View File

@@ -0,0 +1,252 @@
//! Unit tests for ProfileBuilder
use claudia_lib::sandbox::profile::{ProfileBuilder, SandboxRule};
use std::path::PathBuf;
use test_case::test_case;
/// Helper to create a sandbox rule
fn make_rule(
operation_type: &str,
pattern_type: &str,
pattern_value: &str,
platforms: Option<&[&str]>,
) -> SandboxRule {
SandboxRule {
id: None,
profile_id: 0,
operation_type: operation_type.to_string(),
pattern_type: pattern_type.to_string(),
pattern_value: pattern_value.to_string(),
enabled: true,
platform_support: platforms.map(|p| {
serde_json::to_string(&p.iter().map(|s| s.to_string()).collect::<Vec<_>>())
.unwrap()
}),
created_at: String::new(),
}
}
#[test]
fn test_profile_builder_creation() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path.clone());
assert!(builder.is_ok(), "ProfileBuilder should be created successfully");
}
#[test]
fn test_empty_rules_creates_empty_profile() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path).unwrap();
let profile = builder.build_profile(vec![]);
assert!(profile.is_ok(), "Empty rules should create valid empty profile");
}
#[test]
fn test_file_read_rule_parsing() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path.clone()).unwrap();
let rules = vec![
make_rule("file_read_all", "literal", "/usr/lib/test.so", Some(&["linux", "macos"])),
make_rule("file_read_all", "subpath", "/usr/lib", Some(&["linux", "macos"])),
];
let _profile = builder.build_profile(rules);
// Profile creation might fail on unsupported platforms, but parsing should work
if std::env::consts::OS == "linux" || std::env::consts::OS == "macos" {
assert!(_profile.is_ok(), "File read rules should be parsed on supported platforms");
}
}
#[test]
fn test_network_rule_parsing() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path).unwrap();
let rules = vec![
make_rule("network_outbound", "all", "", Some(&["linux", "macos"])),
make_rule("network_outbound", "tcp", "8080", Some(&["macos"])),
make_rule("network_outbound", "local_socket", "/tmp/socket", Some(&["macos"])),
];
let _profile = builder.build_profile(rules);
if std::env::consts::OS == "linux" || std::env::consts::OS == "macos" {
assert!(_profile.is_ok(), "Network rules should be parsed on supported platforms");
}
}
#[test]
fn test_system_info_rule_parsing() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path).unwrap();
let rules = vec![
make_rule("system_info_read", "all", "", Some(&["macos"])),
];
let _profile = builder.build_profile(rules);
if std::env::consts::OS == "macos" {
assert!(_profile.is_ok(), "System info rule should be parsed on macOS");
}
}
#[test]
fn test_template_variable_replacement() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path.clone()).unwrap();
let rules = vec![
make_rule("file_read_all", "subpath", "{{PROJECT_PATH}}/src", Some(&["linux", "macos"])),
make_rule("file_read_all", "subpath", "{{HOME}}/.config", Some(&["linux", "macos"])),
];
let _profile = builder.build_profile(rules);
// We can't easily verify the exact paths without inspecting the Profile internals,
// but this test ensures template replacement doesn't panic
}
#[test]
fn test_disabled_rules_are_ignored() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path).unwrap();
let mut rule = make_rule("file_read_all", "subpath", "/usr/lib", Some(&["linux", "macos"]));
rule.enabled = false;
let profile = builder.build_profile(vec![rule]);
assert!(profile.is_ok(), "Disabled rules should be ignored");
}
#[test]
fn test_platform_filtering() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path).unwrap();
let current_os = std::env::consts::OS;
let other_os = if current_os == "linux" { "macos" } else { "linux" };
let rules = vec![
// Rule for current platform
make_rule("file_read_all", "subpath", "/test1", Some(&[current_os])),
// Rule for other platform
make_rule("file_read_all", "subpath", "/test2", Some(&[other_os])),
// Rule for both platforms
make_rule("file_read_all", "subpath", "/test3", Some(&["linux", "macos"])),
// Rule with no platform specification (should be included)
make_rule("file_read_all", "subpath", "/test4", None),
];
let _profile = builder.build_profile(rules);
// Rules for other platforms should be filtered out
}
#[test]
fn test_invalid_operation_type() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path).unwrap();
let rules = vec![
make_rule("invalid_operation", "subpath", "/test", Some(&["linux", "macos"])),
];
let _profile = builder.build_profile(rules);
assert!(_profile.is_ok(), "Invalid operations should be skipped");
}
#[test]
fn test_invalid_pattern_type() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path).unwrap();
let rules = vec![
make_rule("file_read_all", "invalid_pattern", "/test", Some(&["linux", "macos"])),
];
let _profile = builder.build_profile(rules);
// Should either skip the rule or fail gracefully
}
#[test]
fn test_invalid_tcp_port() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path).unwrap();
let rules = vec![
make_rule("network_outbound", "tcp", "not_a_number", Some(&["macos"])),
];
let _profile = builder.build_profile(rules);
// Should handle invalid port gracefully
}
#[test_case("file_read_all", "subpath", "/test" ; "file read operation")]
#[test_case("file_read_metadata", "literal", "/test/file" ; "metadata read operation")]
#[test_case("network_outbound", "all", "" ; "network all operation")]
#[test_case("system_info_read", "all", "" ; "system info operation")]
fn test_operation_support_level(operation_type: &str, pattern_type: &str, pattern_value: &str) {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path).unwrap();
let rule = make_rule(operation_type, pattern_type, pattern_value, None);
let rules = vec![rule];
match builder.build_profile(rules) {
Ok(_) => {
// Profile created successfully - operation is supported
println!("Operation {operation_type} is supported on this platform");
}
Err(e) => {
// Profile creation failed - likely due to unsupported operation
println!("Operation {operation_type} is not supported: {e}");
}
}
}
#[test]
fn test_complex_profile_with_multiple_rules() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path.clone()).unwrap();
let rules = vec![
// File operations
make_rule("file_read_all", "subpath", "{{PROJECT_PATH}}", Some(&["linux", "macos"])),
make_rule("file_read_all", "subpath", "/usr/lib", Some(&["linux", "macos"])),
make_rule("file_read_all", "literal", "/etc/hosts", Some(&["linux", "macos"])),
make_rule("file_read_metadata", "subpath", "/", Some(&["macos"])),
// Network operations
make_rule("network_outbound", "all", "", Some(&["linux", "macos"])),
make_rule("network_outbound", "tcp", "443", Some(&["macos"])),
make_rule("network_outbound", "tcp", "80", Some(&["macos"])),
// System info
make_rule("system_info_read", "all", "", Some(&["macos"])),
];
let _profile = builder.build_profile(rules);
if std::env::consts::OS == "linux" || std::env::consts::OS == "macos" {
assert!(_profile.is_ok(), "Complex profile should be created on supported platforms");
}
}
#[test]
fn test_rule_order_preservation() {
let project_path = PathBuf::from("/test/project");
let builder = ProfileBuilder::new(project_path).unwrap();
// Create rules with specific order
let rules = vec![
make_rule("file_read_all", "subpath", "/first", Some(&["linux", "macos"])),
make_rule("network_outbound", "all", "", Some(&["linux", "macos"])),
make_rule("file_read_all", "subpath", "/second", Some(&["linux", "macos"])),
];
let _profile = builder.build_profile(rules);
// Order should be preserved in the resulting profile
}