use std::path::{Path, PathBuf};
use crate::config::SandboxConfig;
#[derive(Debug, Clone)]
pub struct SandboxPolicy {
pub project_dir: PathBuf,
pub allowed_write_paths: Vec<PathBuf>,
pub forbidden_paths: Vec<PathBuf>,
pub allow_network: bool,
}
impl SandboxPolicy {
pub fn from_config(config: &SandboxConfig, project_dir: &Path) -> Self {
let home = std::env::var_os("HOME").map(PathBuf::from);
let expand = |s: &String| expand_path(s.as_str(), &home, project_dir);
Self {
project_dir: project_dir.to_path_buf(),
allowed_write_paths: config.allowed_write_paths.iter().map(expand).collect(),
forbidden_paths: config.forbidden_paths.iter().map(expand).collect(),
allow_network: config.allow_network,
}
}
}
fn expand_path(raw: &str, home: &Option<PathBuf>, project_dir: &Path) -> PathBuf {
if let Some(rest) = raw.strip_prefix("~/")
&& let Some(home) = home
{
return home.join(rest);
}
if raw == "~"
&& let Some(home) = home
{
return home.clone();
}
let p = PathBuf::from(raw);
if p.is_absolute() {
p
} else {
project_dir.join(p)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn expands_tilde_to_home() {
let home = Some(PathBuf::from("/Users/alice"));
let project = PathBuf::from("/work/repo");
assert_eq!(
expand_path("~/.cache/agent-code", &home, &project),
PathBuf::from("/Users/alice/.cache/agent-code")
);
assert_eq!(
expand_path("~", &home, &project),
PathBuf::from("/Users/alice")
);
}
#[test]
fn absolute_paths_unchanged() {
let home = Some(PathBuf::from("/Users/alice"));
let project = PathBuf::from("/work/repo");
assert_eq!(expand_path("/tmp", &home, &project), PathBuf::from("/tmp"));
}
#[test]
fn relative_paths_join_project_dir() {
let home = Some(PathBuf::from("/Users/alice"));
let project = PathBuf::from("/work/repo");
assert_eq!(
expand_path("target", &home, &project),
PathBuf::from("/work/repo/target")
);
}
#[test]
fn missing_home_leaves_tilde() {
let home: Option<PathBuf> = None;
let project = PathBuf::from("/work/repo");
assert_eq!(
expand_path("~/foo", &home, &project),
PathBuf::from("/work/repo/~/foo")
);
}
#[test]
fn from_config_resolves_paths() {
unsafe { std::env::set_var("HOME", "/Users/test") };
let cfg = SandboxConfig {
enabled: true,
strategy: "seatbelt".to_string(),
allowed_write_paths: vec!["/tmp".to_string(), "~/.cache/agent-code".to_string()],
forbidden_paths: vec!["~/.ssh".to_string()],
allow_network: false,
};
let policy = SandboxPolicy::from_config(&cfg, Path::new("/work/repo"));
assert_eq!(policy.project_dir, PathBuf::from("/work/repo"));
assert_eq!(
policy.allowed_write_paths,
vec![
PathBuf::from("/tmp"),
PathBuf::from("/Users/test/.cache/agent-code"),
]
);
assert_eq!(
policy.forbidden_paths,
vec![PathBuf::from("/Users/test/.ssh")]
);
assert!(!policy.allow_network);
}
}