#[cfg(all(feature = "sandbox-macos", target_os = "macos"))]
pub mod macos;
#[cfg(all(feature = "sandbox-linux", target_os = "linux"))]
pub mod linux;
#[cfg(all(feature = "sandbox-windows", target_os = "windows"))]
pub mod windows;
use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::error::SandboxError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum AccessMode {
ReadOnly,
ReadWrite,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AllowedPath {
pub path: PathBuf,
pub mode: AccessMode,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NetworkRule {
pub domain: String,
pub ports: Vec<u16>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SandboxPolicy {
pub allowed_paths: Vec<AllowedPath>,
pub allow_network: bool,
#[serde(default)]
pub network_rules: Vec<NetworkRule>,
pub allow_process_spawn: bool,
pub env: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct WrappedCommand {
pub program: OsString,
pub args: Vec<OsString>,
}
pub struct SandboxPolicyBuilder {
policy: SandboxPolicy,
}
impl SandboxPolicyBuilder {
pub fn new() -> Self {
Self {
policy: SandboxPolicy {
allowed_paths: Vec::new(),
allow_network: false,
network_rules: Vec::new(),
allow_process_spawn: false,
env: HashMap::new(),
},
}
}
pub fn allow_read(mut self, path: impl Into<PathBuf>) -> Self {
self.policy
.allowed_paths
.push(AllowedPath { path: path.into(), mode: AccessMode::ReadOnly });
self
}
pub fn allow_read_write(mut self, path: impl Into<PathBuf>) -> Self {
self.policy
.allowed_paths
.push(AllowedPath { path: path.into(), mode: AccessMode::ReadWrite });
self
}
pub fn allow_network(mut self) -> Self {
self.policy.allow_network = true;
self
}
pub fn allow_domain(mut self, domain: impl Into<String>, ports: &[u16]) -> Self {
self.policy
.network_rules
.push(NetworkRule { domain: domain.into(), ports: ports.to_vec() });
self
}
pub fn allow_process_spawn(mut self) -> Self {
self.policy.allow_process_spawn = true;
self
}
pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.policy.env.insert(key.into(), value.into());
self
}
pub fn build(self) -> SandboxPolicy {
self.policy
}
}
impl Default for SandboxPolicyBuilder {
fn default() -> Self {
Self::new()
}
}
pub trait SandboxEnforcer: Send + Sync {
fn name(&self) -> &str;
fn probe(&self) -> Result<(), SandboxError>;
fn wrap_command(
&self,
program: &OsStr,
args: &[OsString],
policy: &SandboxPolicy,
) -> Result<WrappedCommand, SandboxError>;
fn configure_command(
&self,
_cmd: &mut tokio::process::Command,
_policy: &SandboxPolicy,
) -> Result<(), SandboxError> {
Ok(())
}
}
pub fn get_enforcer() -> Result<Box<dyn SandboxEnforcer>, SandboxError> {
#[cfg(all(feature = "sandbox-macos", target_os = "macos"))]
{
let enforcer = macos::MacOsEnforcer::new();
enforcer.probe()?;
return Ok(Box::new(enforcer));
}
#[cfg(all(feature = "sandbox-linux", target_os = "linux"))]
{
let enforcer = linux::LinuxEnforcer::new();
enforcer.probe()?;
return Ok(Box::new(enforcer));
}
#[cfg(all(feature = "sandbox-windows", target_os = "windows"))]
{
let enforcer = windows::WindowsEnforcer::new();
enforcer.probe()?;
return Ok(Box::new(enforcer));
}
#[allow(unreachable_code)]
Err(SandboxError::EnforcerUnavailable {
enforcer: "none".to_string(),
message: "no sandbox feature flag is enabled for this platform".to_string(),
})
}