AI Sandbox - Cross-platform AI Tool Sandbox Security
A Rust crate providing cross-platform sandbox isolation for AI agent tools.
Features
-
Multi-platform sandboxing:
- Linux: Bubblewrap + Seccomp + Landlock
- macOS: Seatbelt (sandbox-exec)
- Windows: Restricted Token
- FreeBSD: Capsicum
- OpenBSD: pledge
-
Process hardening: Pre-main() security hardening
-
Execution policy engine: Rule-based command execution control
-
Network policy: Fine-grained network access control
-
Filesystem policy: Read-only, workspace-only, or full access
Platform Support
| Platform |
Sandbox Type |
Status |
| Linux |
Bubblewrap/Seccomp/Landlock |
✅ |
| macOS |
Seatbelt (sandbox-exec) |
✅ |
| Windows |
Restricted Token |
✅ |
| FreeBSD |
Capsicum |
✅ |
| OpenBSD |
pledge |
✅ |
Installation
[dependencies]
ai-sandbox = "0.1.4"
Testing
./test.sh
cargo check
cargo test
cargo run --example demo
cargo build --release
Creating Your Own Sandbox Application
Step 1: Add Dependency
[dependencies]
ai-sandbox = "0.1.4"
Step 2: Basic Usage Example
Create a file examples/sandbox_app.rs:
use ai_sandbox::{
SandboxManager,
SandboxPolicy,
SandboxCommand,
FileSystemSandboxPolicy,
NetworkSandboxPolicy,
get_platform_sandbox,
};
use std::ffi::OsString;
use std::collections::HashMap;
use std::path::PathBuf;
use std::process::{Command, Stdio};
fn main() {
let sandbox_type = get_platform_sandbox(false);
println!("Using sandbox type: {:?}", sandbox_type);
let manager = SandboxManager::new();
let policy = SandboxPolicy::ReadOnly {
file_system: FileSystemSandboxPolicy::ReadOnly,
network_access: NetworkSandboxPolicy::NoAccess,
};
let command = SandboxCommand {
program: OsString::from("ls"),
args: vec!["-la".to_string(), "/etc".to_string()],
cwd: PathBuf::from("/tmp"),
env: HashMap::new(),
};
match manager.create_exec_request(command, policy) {
Ok(request) => {
println!("Sandbox policy applied: {:?}", request.sandbox_policy);
println!("Command: {:?}", request.command);
}
Err(e) => {
eprintln!("Sandbox error: {}", e);
}
}
}
Step 3: Run Your Example
cargo run --example sandbox_app
Advanced Usage
Custom Sandbox Policy
use ai_sandbox::{SandboxPolicy, NetworkSandboxPolicy};
use std::path::PathBuf;
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![
PathBuf::from("/tmp"),
PathBuf::from("/home/user/workspace"),
],
network_access: NetworkSandboxPolicy::Localhost, };
Execution Policy (Command Allow/Deny)
use ai_sandbox::{Policy, Decision};
let mut exec_policy = Policy::new();
exec_policy.add_prefix_rule(
&["rm".to_string(), "-rf".to_string()],
Decision::Deny,
Some("Deleting files recursively is not allowed".to_string()),
).unwrap();
exec_policy.add_prefix_rule(
&["curl".to_string()],
Decision::Deny,
Some("Network downloads are restricted".to_string()),
).unwrap();
exec_policy.add_prefix_rule(
&["wget".to_string()],
Decision::Deny,
Some("Network downloads are restricted".to_string()),
).unwrap();
exec_policy.add_prefix_rule(
&["ls".to_string()],
Decision::Allow,
None,
).unwrap();
exec_policy.add_prefix_rule(
&["cat".to_string()],
Decision::Allow,
None,
).unwrap();
let result = exec_policy.check(&["rm".to_string(), "-rf".to_string(), "/".to_string()]);
match result {
Some(m) => {
if m.decision == Decision::Deny {
println!("Command denied: {}", m.justification.as_deref().unwrap_or("No reason"));
}
}
None => println!("Command allowed (no matching rule)"),
}
Process Hardening
use ai_sandbox::process_hardening::pre_main_hardening;
fn init() {
pre_main_hardening();
}
#[cfg_attr(not(test), ctor::ctor)]
fn init() {
pre_main_hardening();
}
Platform-Specific Features
Linux with Bubblewrap
use ai_sandbox::linux_sandbox::{find_system_bwrap_in_path, system_bwrap_warning};
if let Some(warning) = system_bwrap_warning() {
eprintln!("Warning: {}", warning);
}
let bwrap_path = find_system_bwrap_in_path();
Linux with Landlock
use ai_sandbox::linux_sandbox::landlock::{is_landlock_available, get_landlock_version};
if is_landlock_available() {
let version = get_landlock_version().unwrap_or(0);
println!("Landlock version: {}", version);
}
macOS Seatbelt
use ai_sandbox::sandboxing::seatbelt::{create_seatbelt_policy, proxy_loopback_ports_from_env};
use std::collections::HashMap;
let policy = create_seatbelt_policy(&sandbox_policy);
println!("Seatbelt policy:\n{}", policy);
let mut env = HashMap::new();
env.insert("HTTP_PROXY".to_string(), "http://localhost:8080".to_string());
let ports = proxy_loopback_ports_from_env(&env);
Complete AI Agent Example
use ai_sandbox::{
SandboxManager, SandboxPolicy, SandboxCommand, SandboxType,
FileSystemSandboxPolicy, NetworkSandboxPolicy, Policy, Decision,
};
use std::collections::HashMap;
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::Command;
struct AgentSandbox {
manager: SandboxManager,
exec_policy: Policy,
}
impl AgentSandbox {
fn new() -> Self {
let mut exec_policy = Policy::new();
exec_policy.add_prefix_rule(&["rm".to_string()], Decision::Deny, None).unwrap();
exec_policy.add_prefix_rule(&["mkfs".to_string()], Decision::Deny, None).unwrap();
exec_policy.add_prefix_rule(&["dd".to_string()], Decision::Deny, None).unwrap();
Self {
manager: SandboxManager::new(),
exec_policy,
}
}
fn execute(&self, program: &str, args: Vec<String>, cwd: PathBuf) -> Result<(), String> {
let command = SandboxCommand {
program: OsString::from(program),
args,
cwd: cwd.clone(),
env: HashMap::new(),
};
let mut full_command = vec![program.to_string()];
full_command.extend(command.args.clone());
if let Some(m) = self.exec_policy.check(&full_command) {
if m.decision == Decision::Deny {
return Err(format!("Command denied: {:?}", m.justification));
}
}
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![cwd],
network_access: NetworkSandboxPolicy::Localhost,
};
let request = self.manager
.create_exec_request(command, policy)
.map_err(|e| e.to_string())?;
println!("Executing: {:?}", request.command);
Ok(())
}
}
fn main() {
let sandbox = AgentSandbox::new();
sandbox.execute("ls", vec!["-la".to_string()], PathBuf::from("/tmp")).unwrap();
let result = sandbox.execute("rm", vec!["-rf".to_string(), "/".to_string()], PathBuf::from("/tmp"));
assert!(result.is_err());
}
Platform-Specific Notes
Linux
Requires bubblewrap (bwrap) installed on the system. On Ubuntu/Debian:
apt install bubblewrap
macOS
Uses the native sandbox-exec command (available in /usr/bin/sandbox-exec).
Windows
Uses Windows Restricted Token API. Requires Windows 10 version 1709 or later.
FreeBSD
Uses Capsicum framework for capability-mode sandboxing.
OpenBSD
Uses the pledge() system call for system call filtering.
License
MIT