use std::{
fmt::{Display, Formatter},
path::{Path, PathBuf},
};
use clap::Parser;
use serde::{Deserialize, Serialize};
#[derive(Debug, Parser, Clone, Default)]
pub struct SandboxArguments {
#[clap(long, action, help_heading = "Sandbox arguments")]
pub sandbox: bool,
#[clap(long, action, help_heading = "Sandbox arguments")]
pub allow_network: bool,
#[clap(long, help_heading = "Sandbox arguments")]
pub allow_read: Vec<PathBuf>,
#[clap(long, help_heading = "Sandbox arguments")]
pub allow_read_execute: Vec<PathBuf>,
#[clap(long, help_heading = "Sandbox arguments")]
pub allow_read_write: Vec<PathBuf>,
#[clap(long, action, help_heading = "Sandbox arguments")]
pub overwrite_default_sandbox_config: bool,
}
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct SandboxConfiguration {
allow_network: bool,
read: Vec<PathBuf>,
read_execute: Vec<PathBuf>,
read_write: Vec<PathBuf>,
}
impl Display for SandboxConfiguration {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{} Sandbox Configuration", console::Emoji("🛡️", " "))?;
writeln!(
f,
"Network Access: {}",
if self.allow_network {
console::Emoji("✅", " ")
} else {
console::Emoji("❌", " ")
}
)?;
writeln!(f, "\n{} Read-only paths:", console::Emoji("📁 ", ""))?;
for path in &self.read {
writeln!(f, " - {}", path.display())?;
}
writeln!(f, "\n{} Read-execute paths:", console::Emoji("📂 ", ""))?;
for path in &self.read_execute {
writeln!(f, " - {}", path.display())?;
}
writeln!(f, "\n{} Read-write paths:", console::Emoji("📝 ", ""))?;
for path in &self.read_write {
writeln!(f, " - {}", path.display())?;
}
Ok(())
}
}
impl SandboxConfiguration {
pub fn for_macos() -> Self {
let read_execute = vec!["/bin/", "/usr/bin/"]
.into_iter()
.map(Into::into)
.collect();
let mut read_write = Vec::new();
read_write.push("/tmp".into());
read_write.push("/var/tmp".into());
let temp_folder = std::env::var("TMPDIR").ok();
if let Some(temp_folder) = temp_folder {
read_write.push(temp_folder.into());
}
Self {
allow_network: false,
read: vec!["/".into()],
read_execute,
read_write,
}
}
pub fn for_linux() -> Self {
let read_execute = vec![
"/bin/",
"/usr/bin/",
"/lib64",
"/usr/lib64",
"/lib",
"/usr/lib",
]
.into_iter()
.map(Into::into)
.collect();
let mut read_write: Vec<PathBuf> = vec![
"/tmp", "/var/tmp",
]
.into_iter()
.map(Into::into)
.collect();
let temp_folder = std::env::var("TMPDIR").ok();
if let Some(temp_folder) = temp_folder {
read_write.push(temp_folder.into());
}
Self {
allow_network: false,
read: vec!["/".into()],
read_execute,
read_write,
}
}
pub fn with_cwd(&self, cwd: &Path) -> Self {
let mut read_execute = self.read_execute.clone();
if let Some(parent) = cwd.parent() {
read_execute.push(parent.to_path_buf());
}
let mut read_write = self.read_write.clone();
if let Some(parent) = cwd.parent() {
read_write.push(parent.to_path_buf());
}
Self {
allow_network: self.allow_network,
read: self.read.clone(),
read_execute,
read_write,
}
}
pub fn to_args(&self) -> Vec<String> {
let mut args = Vec::new();
if self.allow_network {
args.push("--network".to_string());
}
for path in &self.read {
args.push("--fs-read".to_string());
args.push(path.to_string_lossy().to_string());
}
for path in &self.read_execute {
args.push("--fs-exec-and-read".to_string());
args.push(path.to_string_lossy().to_string());
}
for path in &self.read_write {
args.push("--fs-write-and-read".to_string());
args.push(path.to_string_lossy().to_string());
}
args
}
}
impl From<SandboxArguments> for Option<SandboxConfiguration> {
fn from(args: SandboxArguments) -> Self {
if !args.sandbox {
return None;
}
let mut result = if !args.overwrite_default_sandbox_config {
#[cfg(target_os = "linux")]
let default = SandboxConfiguration::for_linux();
#[cfg(target_os = "macos")]
let default = SandboxConfiguration::for_macos();
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
let default = SandboxConfiguration::default();
default
} else {
SandboxConfiguration::default()
};
for path in args.allow_read {
result.read.push(path);
}
for path in args.allow_read_execute {
result.read_execute.push(path);
}
for path in args.allow_read_write {
result.read_write.push(path);
}
result.allow_network = args.allow_network;
Some(result)
}
}