feat(sandbox): implement cross-platform support with Windows fallback
- Add conditional compilation for Unix-specific gaol sandbox functionality - Implement Windows-compatible SandboxExecutor with no-op sandboxing - Update ProfileBuilder to handle both Unix and Windows platforms - Add platform-specific dependency declaration for gaol crate - Modify agent and claude commands to use appropriate sandbox implementation - Update test modules with conditional compilation for Unix-only tests - Ensure graceful degradation on Windows with logging warnings This change enables the application to build and run on Windows while maintaining full sandbox security on Unix-like systems.
This commit is contained in:
@@ -50,6 +50,9 @@ zstd = "0.13"
|
|||||||
uuid = { version = "1.6", features = ["v4", "serde"] }
|
uuid = { version = "1.6", features = ["v4", "serde"] }
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
gaol = "0.2"
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
cocoa = "0.26"
|
cocoa = "0.26"
|
||||||
objc = "0.2"
|
objc = "0.2"
|
||||||
|
@@ -996,12 +996,20 @@ pub async fn execute_agent(
|
|||||||
Ok(build_result) => {
|
Ok(build_result) => {
|
||||||
|
|
||||||
// Create the enhanced sandbox executor
|
// Create the enhanced sandbox executor
|
||||||
|
#[cfg(unix)]
|
||||||
let executor = crate::sandbox::executor::SandboxExecutor::new_with_serialization(
|
let executor = crate::sandbox::executor::SandboxExecutor::new_with_serialization(
|
||||||
build_result.profile,
|
build_result.profile,
|
||||||
project_path_buf.clone(),
|
project_path_buf.clone(),
|
||||||
build_result.serialized
|
build_result.serialized
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
let executor = crate::sandbox::executor::SandboxExecutor::new_with_serialization(
|
||||||
|
(),
|
||||||
|
project_path_buf.clone(),
|
||||||
|
build_result.serialized
|
||||||
|
);
|
||||||
|
|
||||||
// Prepare the sandboxed command
|
// Prepare the sandboxed command
|
||||||
let args = vec![
|
let args = vec![
|
||||||
"-p", &task,
|
"-p", &task,
|
||||||
|
@@ -1021,7 +1021,14 @@ fn create_sandboxed_claude_command(app: &AppHandle, project_path: &str) -> Resul
|
|||||||
|
|
||||||
// Use the helper function to create sandboxed command
|
// Use the helper function to create sandboxed command
|
||||||
let claude_path = find_claude_binary(app)?;
|
let claude_path = find_claude_binary(app)?;
|
||||||
Ok(create_sandboxed_command(&claude_path, &[], &project_path_buf, profile, project_path_buf.clone()))
|
#[cfg(unix)]
|
||||||
|
return Ok(create_sandboxed_command(&claude_path, &[], &project_path_buf, profile, project_path_buf.clone()));
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
{
|
||||||
|
log::warn!("Sandboxing not supported on Windows, using regular command");
|
||||||
|
Ok(create_command_with_env(&claude_path))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::error!("Failed to build sandbox profile: {}, falling back to non-sandboxed", e);
|
log::error!("Failed to build sandbox profile: {}, falling back to non-sandboxed", e);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
#[cfg(unix)]
|
||||||
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods, Command as GaolCommand, Sandbox, SandboxMethods};
|
use gaol::sandbox::{ChildSandbox, ChildSandboxMethods, Command as GaolCommand, Sandbox, SandboxMethods};
|
||||||
use log::{info, warn, error, debug};
|
use log::{info, warn, error, debug};
|
||||||
use std::env;
|
use std::env;
|
||||||
@@ -8,11 +9,13 @@ use tokio::process::Command;
|
|||||||
|
|
||||||
/// Sandbox executor for running commands in a sandboxed environment
|
/// Sandbox executor for running commands in a sandboxed environment
|
||||||
pub struct SandboxExecutor {
|
pub struct SandboxExecutor {
|
||||||
|
#[cfg(unix)]
|
||||||
profile: gaol::profile::Profile,
|
profile: gaol::profile::Profile,
|
||||||
project_path: PathBuf,
|
project_path: PathBuf,
|
||||||
serialized_profile: Option<SerializedProfile>,
|
serialized_profile: Option<SerializedProfile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
impl SandboxExecutor {
|
impl SandboxExecutor {
|
||||||
/// Create a new sandbox executor with the given profile
|
/// Create a new sandbox executor with the given profile
|
||||||
pub fn new(profile: gaol::profile::Profile, project_path: PathBuf) -> Self {
|
pub fn new(profile: gaol::profile::Profile, project_path: PathBuf) -> Self {
|
||||||
@@ -267,12 +270,76 @@ impl SandboxExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Windows implementation - no sandboxing
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
impl SandboxExecutor {
|
||||||
|
/// Create a new sandbox executor (no-op on Windows)
|
||||||
|
pub fn new(_profile: (), project_path: PathBuf) -> Self {
|
||||||
|
Self {
|
||||||
|
project_path,
|
||||||
|
serialized_profile: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new sandbox executor with serialized profile (no-op on Windows)
|
||||||
|
pub fn new_with_serialization(
|
||||||
|
_profile: (),
|
||||||
|
project_path: PathBuf,
|
||||||
|
serialized_profile: SerializedProfile
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
project_path,
|
||||||
|
serialized_profile: Some(serialized_profile),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute a command in the sandbox (Windows - no sandboxing)
|
||||||
|
pub fn execute_sandboxed_spawn(&self, command: &str, args: &[&str], cwd: &Path) -> Result<std::process::Child> {
|
||||||
|
info!("Executing command without sandbox on Windows: {} {:?}", command, args);
|
||||||
|
|
||||||
|
std::process::Command::new(command)
|
||||||
|
.args(args)
|
||||||
|
.current_dir(cwd)
|
||||||
|
.stdin(Stdio::piped())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped())
|
||||||
|
.spawn()
|
||||||
|
.context("Failed to spawn process")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepare a sandboxed tokio Command (Windows - no sandboxing)
|
||||||
|
pub fn prepare_sandboxed_command(&self, command: &str, args: &[&str], cwd: &Path) -> Command {
|
||||||
|
info!("Preparing command without sandbox on Windows: {} {:?}", command, args);
|
||||||
|
|
||||||
|
let mut cmd = Command::new(command);
|
||||||
|
cmd.args(args)
|
||||||
|
.current_dir(cwd)
|
||||||
|
.stdin(Stdio::null())
|
||||||
|
.stdout(Stdio::piped())
|
||||||
|
.stderr(Stdio::piped());
|
||||||
|
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract sandbox rules (no-op on Windows)
|
||||||
|
fn extract_sandbox_rules(&self) -> Result<SerializedProfile> {
|
||||||
|
Ok(SerializedProfile { operations: vec![] })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Activate sandbox in child process (no-op on Windows)
|
||||||
|
pub fn activate_sandbox_in_child() -> Result<()> {
|
||||||
|
debug!("Sandbox activation skipped on Windows");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if the current process should activate sandbox
|
/// Check if the current process should activate sandbox
|
||||||
pub fn should_activate_sandbox() -> bool {
|
pub fn should_activate_sandbox() -> bool {
|
||||||
env::var("GAOL_SANDBOX_ACTIVE").unwrap_or_default() == "1"
|
env::var("GAOL_SANDBOX_ACTIVE").unwrap_or_default() == "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to create a sandboxed tokio Command
|
/// Helper to create a sandboxed tokio Command
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn create_sandboxed_command(
|
pub fn create_sandboxed_command(
|
||||||
command: &str,
|
command: &str,
|
||||||
args: &[&str],
|
args: &[&str],
|
||||||
@@ -300,6 +367,7 @@ pub enum SerializedOperation {
|
|||||||
SystemInfoRead,
|
SystemInfoRead,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
fn deserialize_profile(serialized: SerializedProfile, project_path: &Path) -> Result<gaol::profile::Profile> {
|
fn deserialize_profile(serialized: SerializedProfile, project_path: &Path) -> Result<gaol::profile::Profile> {
|
||||||
let mut operations = Vec::new();
|
let mut operations = Vec::new();
|
||||||
|
|
||||||
@@ -366,6 +434,7 @@ fn deserialize_profile(serialized: SerializedProfile, project_path: &Path) -> Re
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
fn create_minimal_profile(project_path: PathBuf) -> Result<gaol::profile::Profile> {
|
fn create_minimal_profile(project_path: PathBuf) -> Result<gaol::profile::Profile> {
|
||||||
let operations = vec![
|
let operations = vec![
|
||||||
gaol::profile::Operation::FileReadAll(
|
gaol::profile::Operation::FileReadAll(
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
|
#[cfg(unix)]
|
||||||
use gaol::profile::{AddressPattern, Operation, OperationSupport, PathPattern, Profile};
|
use gaol::profile::{AddressPattern, Operation, OperationSupport, PathPattern, Profile};
|
||||||
use log::{debug, info, warn};
|
use log::{debug, info, warn};
|
||||||
use rusqlite::{params, Connection};
|
use rusqlite::{params, Connection};
|
||||||
@@ -33,7 +34,10 @@ pub struct SandboxRule {
|
|||||||
|
|
||||||
/// Result of building a profile
|
/// Result of building a profile
|
||||||
pub struct ProfileBuildResult {
|
pub struct ProfileBuildResult {
|
||||||
|
#[cfg(unix)]
|
||||||
pub profile: Profile,
|
pub profile: Profile,
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub profile: (), // Placeholder for Windows
|
||||||
pub serialized: SerializedProfile,
|
pub serialized: SerializedProfile,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +64,10 @@ impl ProfileBuilder {
|
|||||||
// If sandbox is completely disabled, return an empty profile
|
// If sandbox is completely disabled, return an empty profile
|
||||||
if !sandbox_enabled {
|
if !sandbox_enabled {
|
||||||
return Ok(ProfileBuildResult {
|
return Ok(ProfileBuildResult {
|
||||||
|
#[cfg(unix)]
|
||||||
profile: Profile::new(vec![]).map_err(|_| anyhow::anyhow!("Failed to create empty profile"))?,
|
profile: Profile::new(vec![]).map_err(|_| anyhow::anyhow!("Failed to create empty profile"))?,
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
profile: (),
|
||||||
serialized: SerializedProfile { operations: vec![] },
|
serialized: SerializedProfile { operations: vec![] },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -112,13 +119,23 @@ impl ProfileBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build a gaol Profile from database rules
|
/// Build a gaol Profile from database rules
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn build_profile(&self, rules: Vec<SandboxRule>) -> Result<Profile> {
|
pub fn build_profile(&self, rules: Vec<SandboxRule>) -> Result<Profile> {
|
||||||
let result = self.build_profile_with_serialization(rules)?;
|
let result = self.build_profile_with_serialization(rules)?;
|
||||||
Ok(result.profile)
|
Ok(result.profile)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build a gaol Profile from database rules (Windows stub)
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub fn build_profile(&self, _rules: Vec<SandboxRule>) -> Result<()> {
|
||||||
|
warn!("Sandbox profiles are not supported on Windows");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Build a gaol Profile from database rules and return serialized operations
|
/// Build a gaol Profile from database rules and return serialized operations
|
||||||
pub fn build_profile_with_serialization(&self, rules: Vec<SandboxRule>) -> Result<ProfileBuildResult> {
|
pub fn build_profile_with_serialization(&self, rules: Vec<SandboxRule>) -> Result<ProfileBuildResult> {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
let mut operations = Vec::new();
|
let mut operations = Vec::new();
|
||||||
let mut serialized_operations = Vec::new();
|
let mut serialized_operations = Vec::new();
|
||||||
|
|
||||||
@@ -177,7 +194,32 @@ impl ProfileBuilder {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
{
|
||||||
|
// On Windows, we just create a serialized profile without actual sandboxing
|
||||||
|
let mut serialized_operations = Vec::new();
|
||||||
|
|
||||||
|
for rule in rules {
|
||||||
|
if !rule.enabled {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(Some(serialized)) = self.build_serialized_operation(&rule) {
|
||||||
|
serialized_operations.push(serialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ProfileBuildResult {
|
||||||
|
profile: (),
|
||||||
|
serialized: SerializedProfile {
|
||||||
|
operations: serialized_operations,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Build a gaol Operation from a database rule
|
/// Build a gaol Operation from a database rule
|
||||||
|
#[cfg(unix)]
|
||||||
fn build_operation(&self, rule: &SandboxRule) -> Result<Option<Operation>> {
|
fn build_operation(&self, rule: &SandboxRule) -> Result<Option<Operation>> {
|
||||||
match self.build_operation_with_serialization(rule) {
|
match self.build_operation_with_serialization(rule) {
|
||||||
Ok(Some((op, _))) => Ok(Some(op)),
|
Ok(Some((op, _))) => Ok(Some(op)),
|
||||||
@@ -187,6 +229,7 @@ impl ProfileBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build a gaol Operation and its serialized form from a database rule
|
/// Build a gaol Operation and its serialized form from a database rule
|
||||||
|
#[cfg(unix)]
|
||||||
fn build_operation_with_serialization(&self, rule: &SandboxRule) -> Result<Option<(Operation, SerializedOperation)>> {
|
fn build_operation_with_serialization(&self, rule: &SandboxRule) -> Result<Option<(Operation, SerializedOperation)>> {
|
||||||
match rule.operation_type.as_str() {
|
match rule.operation_type.as_str() {
|
||||||
"file_read_all" => {
|
"file_read_all" => {
|
||||||
@@ -218,12 +261,14 @@ impl ProfileBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build a PathPattern from pattern type and value
|
/// Build a PathPattern from pattern type and value
|
||||||
|
#[cfg(unix)]
|
||||||
fn build_path_pattern(&self, pattern_type: &str, pattern_value: &str) -> Result<PathPattern> {
|
fn build_path_pattern(&self, pattern_type: &str, pattern_value: &str) -> Result<PathPattern> {
|
||||||
let (pattern, _, _) = self.build_path_pattern_with_info(pattern_type, pattern_value)?;
|
let (pattern, _, _) = self.build_path_pattern_with_info(pattern_type, pattern_value)?;
|
||||||
Ok(pattern)
|
Ok(pattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build a PathPattern and return additional info for serialization
|
/// Build a PathPattern and return additional info for serialization
|
||||||
|
#[cfg(unix)]
|
||||||
fn build_path_pattern_with_info(&self, pattern_type: &str, pattern_value: &str) -> Result<(PathPattern, PathBuf, bool)> {
|
fn build_path_pattern_with_info(&self, pattern_type: &str, pattern_value: &str) -> Result<(PathPattern, PathBuf, bool)> {
|
||||||
// Replace template variables
|
// Replace template variables
|
||||||
let expanded_value = pattern_value
|
let expanded_value = pattern_value
|
||||||
@@ -240,12 +285,14 @@ impl ProfileBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build an AddressPattern from pattern type and value
|
/// Build an AddressPattern from pattern type and value
|
||||||
|
#[cfg(unix)]
|
||||||
fn build_address_pattern(&self, pattern_type: &str, pattern_value: &str) -> Result<AddressPattern> {
|
fn build_address_pattern(&self, pattern_type: &str, pattern_value: &str) -> Result<AddressPattern> {
|
||||||
let (pattern, _) = self.build_address_pattern_with_serialization(pattern_type, pattern_value)?;
|
let (pattern, _) = self.build_address_pattern_with_serialization(pattern_type, pattern_value)?;
|
||||||
Ok(pattern)
|
Ok(pattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build an AddressPattern and its serialized form
|
/// Build an AddressPattern and its serialized form
|
||||||
|
#[cfg(unix)]
|
||||||
fn build_address_pattern_with_serialization(&self, pattern_type: &str, pattern_value: &str) -> Result<(AddressPattern, SerializedOperation)> {
|
fn build_address_pattern_with_serialization(&self, pattern_type: &str, pattern_value: &str) -> Result<(AddressPattern, SerializedOperation)> {
|
||||||
match pattern_type {
|
match pattern_type {
|
||||||
"all" => Ok((
|
"all" => Ok((
|
||||||
@@ -282,6 +329,59 @@ impl ProfileBuilder {
|
|||||||
// If no platform support specified, assume it's supported
|
// If no platform support specified, assume it's supported
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build only the serialized operation (for Windows)
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
fn build_serialized_operation(&self, rule: &SandboxRule) -> Result<Option<SerializedOperation>> {
|
||||||
|
let pattern_value = self.expand_pattern_value(&rule.pattern_value);
|
||||||
|
|
||||||
|
match rule.operation_type.as_str() {
|
||||||
|
"file_read_all" => {
|
||||||
|
let (path, is_subpath) = self.parse_path_pattern(&rule.pattern_type, &pattern_value)?;
|
||||||
|
Ok(Some(SerializedOperation::FileReadAll { path, is_subpath }))
|
||||||
|
}
|
||||||
|
"file_read_metadata" => {
|
||||||
|
let (path, is_subpath) = self.parse_path_pattern(&rule.pattern_type, &pattern_value)?;
|
||||||
|
Ok(Some(SerializedOperation::FileReadMetadata { path, is_subpath }))
|
||||||
|
}
|
||||||
|
"network_outbound" => {
|
||||||
|
Ok(Some(SerializedOperation::NetworkOutbound { pattern: pattern_value }))
|
||||||
|
}
|
||||||
|
"network_tcp" => {
|
||||||
|
let port = pattern_value.parse::<u16>()
|
||||||
|
.context("Invalid TCP port")?;
|
||||||
|
Ok(Some(SerializedOperation::NetworkTcp { port }))
|
||||||
|
}
|
||||||
|
"network_local_socket" => {
|
||||||
|
let path = PathBuf::from(pattern_value);
|
||||||
|
Ok(Some(SerializedOperation::NetworkLocalSocket { path }))
|
||||||
|
}
|
||||||
|
"system_info_read" => {
|
||||||
|
Ok(Some(SerializedOperation::SystemInfoRead))
|
||||||
|
}
|
||||||
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method to expand pattern values (Windows version)
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
fn expand_pattern_value(&self, pattern_value: &str) -> String {
|
||||||
|
pattern_value
|
||||||
|
.replace("{{PROJECT_PATH}}", &self.project_path.to_string_lossy())
|
||||||
|
.replace("{{HOME}}", &self.home_dir.to_string_lossy())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method to parse path patterns (Windows version)
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
fn parse_path_pattern(&self, pattern_type: &str, pattern_value: &str) -> Result<(PathBuf, bool)> {
|
||||||
|
let path = PathBuf::from(pattern_value);
|
||||||
|
|
||||||
|
match pattern_type {
|
||||||
|
"literal" => Ok((path, false)),
|
||||||
|
"subpath" => Ok((path, true)),
|
||||||
|
_ => Err(anyhow::anyhow!("Unknown path pattern type: {}", pattern_type))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load a sandbox profile by ID
|
/// Load a sandbox profile by ID
|
||||||
@@ -351,6 +451,7 @@ pub fn load_profile_rules(conn: &Connection, profile_id: i64) -> Result<Vec<Sand
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get or create the gaol Profile for execution
|
/// Get or create the gaol Profile for execution
|
||||||
|
#[cfg(unix)]
|
||||||
pub fn get_gaol_profile(conn: &Connection, profile_id: Option<i64>, project_path: PathBuf) -> Result<Profile> {
|
pub fn get_gaol_profile(conn: &Connection, profile_id: Option<i64>, project_path: PathBuf) -> Result<Profile> {
|
||||||
// Load the profile
|
// Load the profile
|
||||||
let profile = if let Some(id) = profile_id {
|
let profile = if let Some(id) = profile_id {
|
||||||
@@ -369,3 +470,10 @@ pub fn get_gaol_profile(conn: &Connection, profile_id: Option<i64>, project_path
|
|||||||
let builder = ProfileBuilder::new(project_path)?;
|
let builder = ProfileBuilder::new(project_path)?;
|
||||||
builder.build_profile(rules)
|
builder.build_profile(rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get or create the gaol Profile for execution (Windows stub)
|
||||||
|
#[cfg(not(unix))]
|
||||||
|
pub fn get_gaol_profile(_conn: &Connection, _profile_id: Option<i64>, _project_path: PathBuf) -> Result<()> {
|
||||||
|
warn!("Sandbox profiles are not supported on Windows");
|
||||||
|
Ok(())
|
||||||
|
}
|
@@ -2,8 +2,16 @@
|
|||||||
//!
|
//!
|
||||||
//! This test suite validates the sandboxing capabilities across different platforms,
|
//! This test suite validates the sandboxing capabilities across different platforms,
|
||||||
//! ensuring that security policies are correctly enforced.
|
//! ensuring that security policies are correctly enforced.
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod common;
|
pub mod common;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
pub mod unit;
|
pub mod unit;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
pub mod integration;
|
pub mod integration;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
pub mod e2e;
|
pub mod e2e;
|
@@ -2,7 +2,9 @@
|
|||||||
//!
|
//!
|
||||||
//! This file integrates all the sandbox test modules and provides
|
//! This file integrates all the sandbox test modules and provides
|
||||||
//! a central location for running the comprehensive test suite.
|
//! a central location for running the comprehensive test suite.
|
||||||
#[path = "sandbox/mod.rs"]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
mod sandbox;
|
mod sandbox;
|
||||||
|
|
||||||
// Re-export test modules to make them discoverable
|
// Re-export test modules to make them discoverable
|
||||||
|
Reference in New Issue
Block a user