* fix(channels): execute tool calls in channel runtime (#302) * chore(fmt): align repo formatting with rustfmt 1.92
157 lines
5.2 KiB
Rust
157 lines
5.2 KiB
Rust
//! Auto-detection of available security features
|
|
|
|
use crate::config::{SandboxBackend, SecurityConfig};
|
|
use crate::security::traits::Sandbox;
|
|
use std::sync::Arc;
|
|
|
|
/// Create a sandbox based on auto-detection or explicit config
|
|
pub fn create_sandbox(config: &SecurityConfig) -> Arc<dyn Sandbox> {
|
|
let backend = &config.sandbox.backend;
|
|
|
|
// If explicitly disabled, return noop
|
|
if matches!(backend, SandboxBackend::None) || config.sandbox.enabled == Some(false) {
|
|
return Arc::new(super::traits::NoopSandbox);
|
|
}
|
|
|
|
// If specific backend requested, try that
|
|
match backend {
|
|
SandboxBackend::Landlock => {
|
|
#[cfg(feature = "sandbox-landlock")]
|
|
{
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
if let Ok(sandbox) = super::landlock::LandlockSandbox::new() {
|
|
return Arc::new(sandbox);
|
|
}
|
|
}
|
|
}
|
|
tracing::warn!(
|
|
"Landlock requested but not available, falling back to application-layer"
|
|
);
|
|
Arc::new(super::traits::NoopSandbox)
|
|
}
|
|
SandboxBackend::Firejail => {
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
if let Ok(sandbox) = super::firejail::FirejailSandbox::new() {
|
|
return Arc::new(sandbox);
|
|
}
|
|
}
|
|
tracing::warn!(
|
|
"Firejail requested but not available, falling back to application-layer"
|
|
);
|
|
Arc::new(super::traits::NoopSandbox)
|
|
}
|
|
SandboxBackend::Bubblewrap => {
|
|
#[cfg(feature = "sandbox-bubblewrap")]
|
|
{
|
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
|
{
|
|
if let Ok(sandbox) = super::bubblewrap::BubblewrapSandbox::new() {
|
|
return Arc::new(sandbox);
|
|
}
|
|
}
|
|
}
|
|
tracing::warn!(
|
|
"Bubblewrap requested but not available, falling back to application-layer"
|
|
);
|
|
Arc::new(super::traits::NoopSandbox)
|
|
}
|
|
SandboxBackend::Docker => {
|
|
if let Ok(sandbox) = super::docker::DockerSandbox::new() {
|
|
return Arc::new(sandbox);
|
|
}
|
|
tracing::warn!("Docker requested but not available, falling back to application-layer");
|
|
Arc::new(super::traits::NoopSandbox)
|
|
}
|
|
SandboxBackend::Auto | SandboxBackend::None => {
|
|
// Auto-detect best available
|
|
detect_best_sandbox()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Auto-detect the best available sandbox
|
|
fn detect_best_sandbox() -> Arc<dyn Sandbox> {
|
|
#[cfg(target_os = "linux")]
|
|
{
|
|
// Try Landlock first (native, no dependencies)
|
|
#[cfg(feature = "sandbox-landlock")]
|
|
{
|
|
if let Ok(sandbox) = super::landlock::LandlockSandbox::probe() {
|
|
tracing::info!("Landlock sandbox enabled (Linux kernel 5.13+)");
|
|
return Arc::new(sandbox);
|
|
}
|
|
}
|
|
|
|
// Try Firejail second (user-space tool)
|
|
if let Ok(sandbox) = super::firejail::FirejailSandbox::probe() {
|
|
tracing::info!("Firejail sandbox enabled");
|
|
return Arc::new(sandbox);
|
|
}
|
|
}
|
|
|
|
#[cfg(target_os = "macos")]
|
|
{
|
|
// Try Bubblewrap on macOS
|
|
#[cfg(feature = "sandbox-bubblewrap")]
|
|
{
|
|
if let Ok(sandbox) = super::bubblewrap::BubblewrapSandbox::probe() {
|
|
tracing::info!("Bubblewrap sandbox enabled");
|
|
return Arc::new(sandbox);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Docker is heavy but works everywhere if docker is installed
|
|
if let Ok(sandbox) = super::docker::DockerSandbox::probe() {
|
|
tracing::info!("Docker sandbox enabled");
|
|
return Arc::new(sandbox);
|
|
}
|
|
|
|
// Fallback: application-layer security only
|
|
tracing::info!("No sandbox backend available, using application-layer security");
|
|
Arc::new(super::traits::NoopSandbox)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::config::{SandboxConfig, SecurityConfig};
|
|
|
|
#[test]
|
|
fn detect_best_sandbox_returns_something() {
|
|
let sandbox = detect_best_sandbox();
|
|
// Should always return at least NoopSandbox
|
|
assert!(sandbox.is_available());
|
|
}
|
|
|
|
#[test]
|
|
fn explicit_none_returns_noop() {
|
|
let config = SecurityConfig {
|
|
sandbox: SandboxConfig {
|
|
enabled: Some(false),
|
|
backend: SandboxBackend::None,
|
|
firejail_args: Vec::new(),
|
|
},
|
|
..Default::default()
|
|
};
|
|
let sandbox = create_sandbox(&config);
|
|
assert_eq!(sandbox.name(), "none");
|
|
}
|
|
|
|
#[test]
|
|
fn auto_mode_detects_something() {
|
|
let config = SecurityConfig {
|
|
sandbox: SandboxConfig {
|
|
enabled: None, // Auto-detect
|
|
backend: SandboxBackend::Auto,
|
|
firejail_args: Vec::new(),
|
|
},
|
|
..Default::default()
|
|
};
|
|
let sandbox = create_sandbox(&config);
|
|
// Should return some sandbox (at least NoopSandbox)
|
|
assert!(sandbox.is_available());
|
|
}
|
|
}
|