mobius 0.15.5

A small, modular Rust framework for building coding agents
Documentation
use std::path::Path;

use tokio::process::Command;

use super::super::Invocation;
use super::super::LocalSandbox;
use super::super::NetworkAccess;
use super::super::WorkspaceAccess;
use super::append_invocation;
use crate::Error;
use crate::Result;
use crate::backend::sandbox::MACOS_COMMAND_WRAPPER;
use crate::backend::sandbox::MACOS_SEATBELT_BASE_POLICY;
use crate::backend::sandbox::MACOS_SEATBELT_NETWORK_POLICY;

const SEATBELT_POLICY_SUFFIX: &str = r#"
(allow file-read*)
(allow file-write*
  (subpath (param "TEMP_ROOT"))
  (subpath (param "WRITABLE_ROOT")))
"#;

pub(crate) fn protected_full_access_command(
    sandbox: &LocalSandbox,
    invocation: &Invocation<'_>,
) -> Result<Command> {
    let executable = Path::new("/usr/bin/sandbox-exec");
    if !executable.is_file() {
        return Err(Error::Sandbox(
            "/usr/bin/sandbox-exec is unavailable".into(),
        ));
    }
    let mut policy = String::from(
        "(version 1)\n(allow default)\n\
         (deny signal (require-not (target same-sandbox)))\n\
         (deny process-info* (require-not (target same-sandbox)))\n\
         (deny mach-task-name (require-not (target same-sandbox)))\n",
    );
    for (index, denied) in sandbox.denied_reads.iter().enumerate() {
        let parameter = format!("DENIED_READ_{index}");
        policy.push_str(&format!(
            "\n(deny file-read* file-write*\n  (literal (param \"{parameter}\")){}\n)",
            if denied.directory {
                format!("\n  (subpath (param \"{parameter}\"))")
            } else {
                String::new()
            }
        ));
    }
    let mut command = Command::new(executable);
    command.arg("-p").arg(policy);
    for (index, denied) in sandbox.denied_reads.iter().enumerate() {
        let path = denied
            .path
            .to_str()
            .ok_or_else(|| Error::Sandbox("sandbox path is not UTF-8".into()))?;
        command.arg(format!("-DDENIED_READ_{index}={path}"));
    }
    command.args([
        "--",
        "/bin/bash",
        "--noprofile",
        "--norc",
        "-c",
        MACOS_COMMAND_WRAPPER,
        "mobius-command",
    ]);
    append_invocation(&mut command, invocation, sandbox.isolated_home);
    Ok(command)
}

pub(crate) fn sandboxed_command(
    sandbox: &LocalSandbox,
    invocation: &Invocation<'_>,
    network_access: NetworkAccess,
    workspace_access: WorkspaceAccess,
) -> Result<Command> {
    let executable = Path::new("/usr/bin/sandbox-exec");
    if !executable.is_file() {
        return Err(Error::Sandbox(
            "/usr/bin/sandbox-exec is unavailable".into(),
        ));
    }
    let temp = std::fs::canonicalize(sandbox.temp.path())?;
    let mut command = Command::new(executable);
    let mut policy = format!("{MACOS_SEATBELT_BASE_POLICY}{SEATBELT_POLICY_SUFFIX}");
    for index in 0..sandbox.workspace_roots.len() {
        let parameter = format!("WORKSPACE_ROOT_{index}");
        policy.push_str(&format!(
            "\n(allow file-write*\n  (literal (param \"{parameter}\"))\n  (subpath (param \"{parameter}\")))"
        ));
    }
    for (index, denied) in sandbox.denied_reads.iter().enumerate() {
        let parameter = format!("DENIED_READ_{index}");
        policy.push_str(&format!(
            "\n(deny file-read*\n  (literal (param \"{parameter}\")){}\n)",
            if denied.directory {
                format!("\n  (subpath (param \"{parameter}\"))")
            } else {
                String::new()
            }
        ));
    }
    if network_access == NetworkAccess::Allowed {
        policy.push_str("\n(allow network-outbound)\n(allow network-inbound)\n");
        policy.push_str(MACOS_SEATBELT_NETWORK_POLICY);
    }
    if workspace_access == WorkspaceAccess::ReadOnly {
        policy.push_str(
            r#"
(deny file-write*
  (literal (param "WRITABLE_ROOT"))
  (subpath (param "WRITABLE_ROOT")))"#,
        );
        for index in 0..sandbox.workspace_roots.len() {
            let parameter = format!("WORKSPACE_ROOT_{index}");
            policy.push_str(&format!(
                "\n(deny file-write*\n  (literal (param \"{parameter}\"))\n  (subpath (param \"{parameter}\")))"
            ));
        }
    }
    command.arg("-p").arg(policy);
    for (name, path) in [("WRITABLE_ROOT", sandbox.root.clone()), ("TEMP_ROOT", temp)] {
        let path = path
            .to_str()
            .ok_or_else(|| Error::Sandbox("sandbox path is not UTF-8".into()))?;
        command.arg(format!("-D{name}={path}"));
    }
    for (index, root) in sandbox.workspace_roots.iter().enumerate() {
        let path = root
            .path
            .to_str()
            .ok_or_else(|| Error::Sandbox("sandbox path is not UTF-8".into()))?;
        command.arg(format!("-DWORKSPACE_ROOT_{index}={path}"));
    }
    for (index, denied) in sandbox.denied_reads.iter().enumerate() {
        let path = denied
            .path
            .to_str()
            .ok_or_else(|| Error::Sandbox("sandbox path is not UTF-8".into()))?;
        command.arg(format!("-DDENIED_READ_{index}={path}"));
    }
    command.args([
        "--",
        "/bin/bash",
        "--noprofile",
        "--norc",
        "-c",
        MACOS_COMMAND_WRAPPER,
        "mobius-command",
    ]);
    append_invocation(&mut command, invocation, sandbox.isolated_home);
    Ok(command)
}