Skip to main content

tkr_sandbox/
exec.rs

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}