* fix(channels): execute tool calls in channel runtime (#302) * chore(fmt): align repo formatting with rustfmt 1.92
128 lines
3.6 KiB
Rust
128 lines
3.6 KiB
Rust
//! Firejail sandbox (Linux user-space sandboxing)
|
|
//!
|
|
//! Firejail is a SUID sandbox program that Linux applications use to sandbox themselves.
|
|
|
|
use crate::security::traits::Sandbox;
|
|
use std::process::Command;
|
|
|
|
/// Firejail sandbox backend for Linux
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct FirejailSandbox;
|
|
|
|
impl FirejailSandbox {
|
|
/// Create a new Firejail sandbox
|
|
pub fn new() -> std::io::Result<Self> {
|
|
if Self::is_installed() {
|
|
Ok(Self)
|
|
} else {
|
|
Err(std::io::Error::new(
|
|
std::io::ErrorKind::NotFound,
|
|
"Firejail not found. Install with: sudo apt install firejail",
|
|
))
|
|
}
|
|
}
|
|
|
|
/// Probe if Firejail is available (for auto-detection)
|
|
pub fn probe() -> std::io::Result<Self> {
|
|
Self::new()
|
|
}
|
|
|
|
/// Check if firejail is installed
|
|
fn is_installed() -> bool {
|
|
Command::new("firejail")
|
|
.arg("--version")
|
|
.output()
|
|
.map(|o| o.status.success())
|
|
.unwrap_or(false)
|
|
}
|
|
}
|
|
|
|
impl Sandbox for FirejailSandbox {
|
|
fn wrap_command(&self, cmd: &mut Command) -> std::io::Result<()> {
|
|
// Prepend firejail to the command
|
|
let program = cmd.get_program().to_string_lossy().to_string();
|
|
let args: Vec<String> = cmd
|
|
.get_args()
|
|
.map(|s| s.to_string_lossy().to_string())
|
|
.collect();
|
|
|
|
// Build firejail wrapper with security flags
|
|
let mut firejail_cmd = Command::new("firejail");
|
|
firejail_cmd.args([
|
|
"--private=home", // New home directory
|
|
"--private-dev", // Minimal /dev
|
|
"--nosound", // No audio
|
|
"--no3d", // No 3D acceleration
|
|
"--novideo", // No video devices
|
|
"--nowheel", // No input devices
|
|
"--notv", // No TV devices
|
|
"--noprofile", // Skip profile loading
|
|
"--quiet", // Suppress warnings
|
|
]);
|
|
|
|
// Add the original command
|
|
firejail_cmd.arg(&program);
|
|
firejail_cmd.args(&args);
|
|
|
|
// Replace the command
|
|
*cmd = firejail_cmd;
|
|
Ok(())
|
|
}
|
|
|
|
fn is_available(&self) -> bool {
|
|
Self::is_installed()
|
|
}
|
|
|
|
fn name(&self) -> &str {
|
|
"firejail"
|
|
}
|
|
|
|
fn description(&self) -> &str {
|
|
"Linux user-space sandbox (requires firejail to be installed)"
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn firejail_sandbox_name() {
|
|
assert_eq!(FirejailSandbox.name(), "firejail");
|
|
}
|
|
|
|
#[test]
|
|
fn firejail_description_mentions_dependency() {
|
|
let desc = FirejailSandbox.description();
|
|
assert!(desc.contains("firejail"));
|
|
}
|
|
|
|
#[test]
|
|
fn firejail_new_fails_if_not_installed() {
|
|
// This will fail unless firejail is actually installed
|
|
let result = FirejailSandbox::new();
|
|
match result {
|
|
Ok(_) => println!("Firejail is installed"),
|
|
Err(e) => assert!(
|
|
e.kind() == std::io::ErrorKind::NotFound
|
|
|| e.kind() == std::io::ErrorKind::Unsupported
|
|
),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn firejail_wrap_command_prepends_firejail() {
|
|
let sandbox = FirejailSandbox;
|
|
let mut cmd = Command::new("echo");
|
|
cmd.arg("test");
|
|
|
|
// Note: wrap_command will fail if firejail isn't installed,
|
|
// but we can still test the logic structure
|
|
let _ = sandbox.wrap_command(&mut cmd);
|
|
|
|
// After wrapping, the program should be firejail
|
|
if sandbox.is_available() {
|
|
assert_eq!(cmd.get_program().to_string_lossy(), "firejail");
|
|
}
|
|
}
|
|
}
|