agcodex_cli/
debug_sandbox.rs1use 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}