1use crate::error::SandboxError;
2use crate::policy::SandboxPolicy;
3use std::process::Command;
4
5#[derive(Debug, Clone)]
6pub struct SandboxOutput {
7 pub stdout: Vec<u8>,
8 pub stderr: Vec<u8>,
9 pub exit: i32,
10}
11
12pub fn run_sandboxed(
13 command: &str,
14 args: &[&str],
15 policy: &SandboxPolicy,
16) -> Result<SandboxOutput, SandboxError> {
17 if let Err(e) = policy.validate() {
18 return Err(SandboxError::PolicyViolation(e));
19 }
20 if policy.disabled {
21 return run_unsandboxed(command, args);
22 }
23 #[cfg(target_os = "linux")]
24 {
25 crate::linux::run(command, args, policy)
26 }
27 #[cfg(target_os = "macos")]
28 {
29 crate::macos::run(command, args, policy)
30 }
31 #[cfg(not(any(target_os = "linux", target_os = "macos")))]
32 {
33 let _ = (command, args);
34 Err(SandboxError::Unsupported)
35 }
36}
37
38fn run_unsandboxed(command: &str, args: &[&str]) -> Result<SandboxOutput, SandboxError> {
39 let out = Command::new(command).args(args).output()?;
40 Ok(SandboxOutput {
41 stdout: out.stdout,
42 stderr: out.stderr,
43 exit: out.status.code().unwrap_or(-1),
44 })
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn rejects_relative_paths_in_policy() {
53 let p = SandboxPolicy::builder().allow_read("rel").build();
54 let r = run_sandboxed("/bin/true", &[], &p);
55 assert!(matches!(r, Err(SandboxError::PolicyViolation(_))));
56 }
57
58 #[test]
59 fn disabled_policy_runs_unsandboxed() {
60 let mut p = SandboxPolicy::deny_all();
61 p.disabled = true;
62 let r = run_sandboxed("/bin/echo", &["hi"], &p).unwrap();
63 assert_eq!(r.exit, 0);
64 assert!(String::from_utf8_lossy(&r.stdout).contains("hi"));
65 }
66}