agcodex_cli/
debug_sandbox.rs

1use std::path::PathBuf;
2
3use agcodex_common::CliConfigOverrides;
4use agcodex_core::config::Config;
5use agcodex_core::config::ConfigOverrides;
6use agcodex_core::exec_env::create_env;
7use agcodex_core::landlock::spawn_command_under_linux_sandbox;
8use agcodex_core::seatbelt::spawn_command_under_seatbelt;
9use agcodex_core::spawn::StdioPolicy;
10use agcodex_protocol::config_types::SandboxMode;
11
12use crate::LandlockCommand;
13use crate::SeatbeltCommand;
14use crate::exit_status::handle_exit_status;
15
16pub async fn run_command_under_seatbelt(
17    command: SeatbeltCommand,
18    codex_linux_sandbox_exe: Option<PathBuf>,
19) -> anyhow::Result<()> {
20    let SeatbeltCommand {
21        full_auto,
22        config_overrides,
23        command,
24    } = command;
25    run_command_under_sandbox(
26        full_auto,
27        command,
28        config_overrides,
29        codex_linux_sandbox_exe,
30        SandboxType::Seatbelt,
31    )
32    .await
33}
34
35pub async fn run_command_under_landlock(
36    command: LandlockCommand,
37    codex_linux_sandbox_exe: Option<PathBuf>,
38) -> anyhow::Result<()> {
39    let LandlockCommand {
40        full_auto,
41        config_overrides,
42        command,
43    } = command;
44    run_command_under_sandbox(
45        full_auto,
46        command,
47        config_overrides,
48        codex_linux_sandbox_exe,
49        SandboxType::Landlock,
50    )
51    .await
52}
53
54enum SandboxType {
55    Seatbelt,
56    Landlock,
57}
58
59async fn run_command_under_sandbox(
60    full_auto: bool,
61    command: Vec<String>,
62    config_overrides: CliConfigOverrides,
63    codex_linux_sandbox_exe: Option<PathBuf>,
64    sandbox_type: SandboxType,
65) -> anyhow::Result<()> {
66    let sandbox_mode = create_sandbox_mode(full_auto);
67    let cwd = std::env::current_dir()?;
68    let config = Config::load_with_cli_overrides(
69        config_overrides
70            .parse_overrides()
71            .map_err(anyhow::Error::msg)?,
72        ConfigOverrides {
73            sandbox_mode: Some(sandbox_mode),
74            codex_linux_sandbox_exe,
75            ..Default::default()
76        },
77    )?;
78    let stdio_policy = StdioPolicy::Inherit;
79    let env = create_env(&config.shell_environment_policy);
80
81    let mut child = match sandbox_type {
82        SandboxType::Seatbelt => {
83            spawn_command_under_seatbelt(command, &config.sandbox_policy, cwd, stdio_policy, env)
84                .await?
85        }
86        SandboxType::Landlock => {
87            #[expect(clippy::expect_used)]
88            let codex_linux_sandbox_exe = config
89                .codex_linux_sandbox_exe
90                .expect("agcodex-linux-sandbox executable not found");
91            spawn_command_under_linux_sandbox(
92                codex_linux_sandbox_exe,
93                command,
94                &config.sandbox_policy,
95                cwd,
96                stdio_policy,
97                env,
98            )
99            .await?
100        }
101    };
102    let status = child.wait().await?;
103
104    handle_exit_status(status);
105}
106
107pub const fn create_sandbox_mode(full_auto: bool) -> SandboxMode {
108    if full_auto {
109        SandboxMode::WorkspaceWrite
110    } else {
111        SandboxMode::ReadOnly
112    }
113}