Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion codex-cli/scripts/run_in_container.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@ quoted_args=""
for arg in "$@"; do
quoted_args+=" $(printf '%q' "$arg")"
done
docker exec -it "$CONTAINER_NAME" bash -c "cd \"/app$WORK_DIR\" && codex --full-auto ${quoted_args}"
docker exec -it "$CONTAINER_NAME" bash -c "cd \"/app$WORK_DIR\" && codex --sandbox workspace-write --ask-for-approval on-request ${quoted_args}"
13 changes: 8 additions & 5 deletions codex-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,22 @@ To test to see what happens when a command is run under the sandbox provided by

```
# macOS
codex sandbox macos [--full-auto] [--log-denials] [COMMAND]...
codex sandbox macos [--log-denials] [COMMAND]...

# Linux
codex sandbox linux [--full-auto] [COMMAND]...
codex sandbox linux [COMMAND]...

# Windows
codex sandbox windows [--full-auto] [COMMAND]...
codex sandbox windows [COMMAND]...

# Legacy aliases
codex debug seatbelt [--full-auto] [--log-denials] [COMMAND]...
codex debug landlock [--full-auto] [COMMAND]...
codex debug seatbelt [--log-denials] [COMMAND]...
codex debug landlock [COMMAND]...
```

To try a writable legacy sandbox mode with these commands, pass an explicit config override such
as `-c 'sandbox_mode="workspace-write"'`.

### Selecting a sandbox policy via `--sandbox`

The Rust CLI exposes a dedicated `--sandbox` (`-s`) flag that lets you pick the sandbox policy **without** having to reach for the generic `-c/--config` option:
Expand Down
133 changes: 92 additions & 41 deletions codex-rs/cli/src/debug_sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,12 @@ pub async fn run_command_under_seatbelt(
codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
let SeatbeltCommand {
full_auto,
allow_unix_sockets,
log_denials,
config_overrides,
command,
} = command;
run_command_under_sandbox(
full_auto,
command,
config_overrides,
codex_linux_sandbox_exe,
Expand All @@ -72,12 +70,10 @@ pub async fn run_command_under_landlock(
codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
let LandlockCommand {
full_auto,
config_overrides,
command,
} = command;
run_command_under_sandbox(
full_auto,
command,
config_overrides,
codex_linux_sandbox_exe,
Expand All @@ -93,12 +89,10 @@ pub async fn run_command_under_windows(
codex_linux_sandbox_exe: Option<PathBuf>,
) -> anyhow::Result<()> {
let WindowsCommand {
full_auto,
config_overrides,
command,
} = command;
run_command_under_sandbox(
full_auto,
command,
config_overrides,
codex_linux_sandbox_exe,
Expand All @@ -117,7 +111,6 @@ enum SandboxType {
}

async fn run_command_under_sandbox(
full_auto: bool,
command: Vec<String>,
config_overrides: CliConfigOverrides,
codex_linux_sandbox_exe: Option<PathBuf>,
Expand All @@ -131,7 +124,6 @@ async fn run_command_under_sandbox(
.parse_overrides()
.map_err(anyhow::Error::msg)?,
codex_linux_sandbox_exe,
full_auto,
)
.await?;

Expand Down Expand Up @@ -406,14 +398,6 @@ async fn run_command_under_windows_session(
std::process::exit(exit_code);
}

pub fn create_sandbox_mode(full_auto: bool) -> SandboxMode {
if full_auto {
SandboxMode::WorkspaceWrite
} else {
SandboxMode::ReadOnly
}
}

async fn spawn_debug_sandbox_child(
program: PathBuf,
args: Vec<String>,
Expand Down Expand Up @@ -583,12 +567,10 @@ mod windows_stdio_bridge {
async fn load_debug_sandbox_config(
cli_overrides: Vec<(String, TomlValue)>,
codex_linux_sandbox_exe: Option<PathBuf>,
full_auto: bool,
) -> anyhow::Result<Config> {
load_debug_sandbox_config_with_codex_home(
cli_overrides,
codex_linux_sandbox_exe,
full_auto,
/*codex_home*/ None,
)
.await
Expand All @@ -597,9 +579,14 @@ async fn load_debug_sandbox_config(
async fn load_debug_sandbox_config_with_codex_home(
cli_overrides: Vec<(String, TomlValue)>,
codex_linux_sandbox_exe: Option<PathBuf>,
full_auto: bool,
codex_home: Option<PathBuf>,
) -> anyhow::Result<Config> {
// For legacy configs, `codex sandbox` historically defaulted to read-only
// instead of inheriting ambient `sandbox_mode` settings from user/system
// config. Keep that behavior unless this invocation explicitly passes a
// legacy `sandbox_mode` CLI override, which is now the documented writable
// replacement for the removed `--full-auto` flag.
let uses_legacy_sandbox_mode_override = cli_overrides_use_legacy_sandbox_mode(&cli_overrides);
Comment thread
dylan-hurd-oai marked this conversation as resolved.
let config = build_debug_sandbox_config(
cli_overrides.clone(),
ConfigOverrides {
Expand All @@ -610,19 +597,14 @@ async fn load_debug_sandbox_config_with_codex_home(
)
.await?;

if config_uses_permission_profiles(&config) {
if full_auto {
anyhow::bail!(
"`codex sandbox --full-auto` is only supported for legacy `sandbox_mode` configs; choose a writable `[permissions]` profile instead"
);
}
if config_uses_permission_profiles(&config) || uses_legacy_sandbox_mode_override {
return Ok(config);
}

build_debug_sandbox_config(
cli_overrides,
ConfigOverrides {
sandbox_mode: Some(create_sandbox_mode(full_auto)),
sandbox_mode: Some(SandboxMode::ReadOnly),
codex_linux_sandbox_exe,
..Default::default()
},
Expand Down Expand Up @@ -656,9 +638,14 @@ fn config_uses_permission_profiles(config: &Config) -> bool {
.is_some()
}

fn cli_overrides_use_legacy_sandbox_mode(cli_overrides: &[(String, TomlValue)]) -> bool {
cli_overrides.iter().any(|(key, _)| key == "sandbox_mode")
}

#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use tempfile::TempDir;

fn escape_toml_path(path: &std::path::Path) -> String {
Expand Down Expand Up @@ -705,7 +692,7 @@ mod tests {
let legacy_config = build_debug_sandbox_config(
Vec::new(),
ConfigOverrides {
sandbox_mode: Some(create_sandbox_mode(/*full_auto*/ false)),
sandbox_mode: Some(SandboxMode::ReadOnly),
..Default::default()
},
Some(codex_home_path.clone()),
Expand All @@ -715,7 +702,6 @@ mod tests {
let config = load_debug_sandbox_config_with_codex_home(
Vec::new(),
/*codex_linux_sandbox_exe*/ None,
/*full_auto*/ false,
Some(codex_home_path),
)
.await?;
Expand All @@ -739,25 +725,90 @@ mod tests {
}

#[tokio::test]
async fn debug_sandbox_rejects_full_auto_for_permission_profiles() -> anyhow::Result<()> {
async fn debug_sandbox_honors_explicit_legacy_sandbox_mode() -> anyhow::Result<()> {
let codex_home = TempDir::new()?;
let sandbox_paths = TempDir::new()?;
let docs = sandbox_paths.path().join("docs");
let private = docs.join("private");
write_permissions_profile_config(&codex_home, &docs, &private)?;
let codex_home_path = codex_home.path().to_path_buf();
let cli_overrides = vec![(
"sandbox_mode".to_string(),
TomlValue::String("workspace-write".to_string()),
)];

let err = load_debug_sandbox_config_with_codex_home(
let workspace_write_config = build_debug_sandbox_config(
cli_overrides.clone(),
ConfigOverrides::default(),
Some(codex_home_path.clone()),
)
.await?;
let read_only_config = build_debug_sandbox_config(
Vec::new(),
ConfigOverrides {
sandbox_mode: Some(SandboxMode::ReadOnly),
..Default::default()
},
Some(codex_home_path.clone()),
)
.await?;

let config = load_debug_sandbox_config_with_codex_home(
cli_overrides,
/*codex_linux_sandbox_exe*/ None,
/*full_auto*/ true,
Some(codex_home.path().to_path_buf()),
Some(codex_home_path),
)
.await
.expect_err("full-auto should be rejected for active permission profiles");
.await?;

assert!(
err.to_string().contains("--full-auto"),
"unexpected error: {err}"
if cfg!(target_os = "windows") {
assert_eq!(
workspace_write_config
.permissions
.file_system_sandbox_policy(),
read_only_config.permissions.file_system_sandbox_policy(),
"workspace-write downgrades to read-only when the Windows sandbox is disabled"
);
} else {
assert_ne!(
workspace_write_config
.permissions
.file_system_sandbox_policy(),
read_only_config.permissions.file_system_sandbox_policy(),
"test fixture should distinguish explicit workspace-write from read-only"
);
}
assert_eq!(
config.permissions.file_system_sandbox_policy(),
workspace_write_config
.permissions
.file_system_sandbox_policy(),
);

Ok(())
}

#[tokio::test]
async fn debug_sandbox_defaults_legacy_configs_to_read_only() -> anyhow::Result<()> {
let codex_home = TempDir::new()?;
let codex_home_path = codex_home.path().to_path_buf();

let read_only_config = build_debug_sandbox_config(
Vec::new(),
ConfigOverrides {
sandbox_mode: Some(SandboxMode::ReadOnly),
..Default::default()
},
Some(codex_home_path.clone()),
)
.await?;

let config = load_debug_sandbox_config_with_codex_home(
Vec::new(),
/*codex_linux_sandbox_exe*/ None,
Some(codex_home_path),
)
.await?;

assert!(!config_uses_permission_profiles(&config));
assert_eq!(
config.permissions.file_system_sandbox_policy(),
read_only_config.permissions.file_system_sandbox_policy(),
);

Ok(())
Expand Down
12 changes: 0 additions & 12 deletions codex-rs/cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ pub use login::run_logout;

#[derive(Debug, Parser)]
pub struct SeatbeltCommand {
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
#[arg(long = "full-auto", default_value_t = false)]
pub full_auto: bool,

/// Allow the sandboxed command to bind/connect AF_UNIX sockets rooted at this path. Relative paths are resolved against the current directory. Repeat to allow multiple paths.
#[arg(long = "allow-unix-socket", value_parser = parse_allow_unix_socket_path)]
pub allow_unix_sockets: Vec<AbsolutePathBuf>,
Expand All @@ -48,10 +44,6 @@ fn parse_allow_unix_socket_path(raw: &str) -> Result<AbsolutePathBuf, String> {

#[derive(Debug, Parser)]
pub struct LandlockCommand {
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
#[arg(long = "full-auto", default_value_t = false)]
pub full_auto: bool,

#[clap(skip)]
pub config_overrides: CliConfigOverrides,

Expand All @@ -62,10 +54,6 @@ pub struct LandlockCommand {

#[derive(Debug, Parser)]
pub struct WindowsCommand {
/// Convenience alias for low-friction sandboxed automatic execution (network-disabled sandbox that can write to cwd and TMPDIR)
#[arg(long = "full-auto", default_value_t = false)]
pub full_auto: bool,

#[clap(skip)]
pub config_overrides: CliConfigOverrides,

Expand Down
Loading
Loading