mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
feat(cli): add sandbox profile config controls (#20118)
## Why The explicit profile path from #20117 is meant for standalone testing, but it still inherited the shell cwd and all managed requirements implicitly. The pre-existing launcher path even called out that it did not support a separate cwd yet in [`debug_sandbox.rs`](https://github.com/openai/codex/blob/509453f688a30929432be866402d1ea46aa12169/codex-rs/cli/src/debug_sandbox.rs#L174-L179). For a standalone command, the useful default is to let the caller choose the project directory being tested and to avoid administrator-provided constraints unless the caller explicitly wants to test those too. ## What changed - Add explicit-profile-only `-C/--cd DIR`, and use that cwd for both profile resolution and command execution. - Add explicit-profile-only `--include-managed-config`. - Make explicit profile mode skip managed requirement sources by default, including cloud requirements, MDM requirements, `/etc/codex/requirements.toml`, and the legacy managed-config requirements projection. - Preserve all existing invocations outside the explicit-profile path. ## Stack 1. #20117 `sandbox-ui-profile` 2. #20118 `sandbox-ui-config` --> this PR Both PRs are additive. Replay JSON is intentionally deferred to a follow-up design pass. ## Tests ran - `cargo test -p codex-cli debug_sandbox` - `cargo test -p codex-cli sandbox_macos_` - `cargo test -p codex-core load_config_layers_can_ignore_managed_requirements` - `cargo test -p codex-core load_config_layers_includes_cloud_requirements` - macOS branch-binary smoke on the rebased top of stack: `-C` changed execution cwd, explicit profile mode omitted managed proxy env under `env -i`, and `--include-managed-config` restored it. - Linux devbox branch-binary smoke on the rebased top of stack: `-C` changed execution cwd for built-in and user-defined explicit profiles.
This commit is contained in:
@@ -6,6 +6,7 @@ mod seatbelt;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Stdio;
|
||||
|
||||
use codex_config::LoaderOverrides;
|
||||
use codex_core::config::Config;
|
||||
use codex_core::config::ConfigBuilder;
|
||||
use codex_core::config::ConfigOverrides;
|
||||
@@ -43,13 +44,23 @@ pub async fn run_command_under_seatbelt(
|
||||
) -> anyhow::Result<()> {
|
||||
let SeatbeltCommand {
|
||||
permissions_profile,
|
||||
cwd,
|
||||
include_managed_config,
|
||||
allow_unix_sockets,
|
||||
log_denials,
|
||||
config_overrides,
|
||||
command,
|
||||
} = command;
|
||||
let managed_requirements_mode = ManagedRequirementsMode::for_profile_invocation(
|
||||
&permissions_profile,
|
||||
include_managed_config,
|
||||
);
|
||||
run_command_under_sandbox(
|
||||
permissions_profile,
|
||||
DebugSandboxConfigOptions {
|
||||
permissions_profile,
|
||||
cwd,
|
||||
managed_requirements_mode,
|
||||
},
|
||||
command,
|
||||
config_overrides,
|
||||
codex_linux_sandbox_exe,
|
||||
@@ -74,11 +85,21 @@ pub async fn run_command_under_landlock(
|
||||
) -> anyhow::Result<()> {
|
||||
let LandlockCommand {
|
||||
permissions_profile,
|
||||
cwd,
|
||||
include_managed_config,
|
||||
config_overrides,
|
||||
command,
|
||||
} = command;
|
||||
let managed_requirements_mode = ManagedRequirementsMode::for_profile_invocation(
|
||||
&permissions_profile,
|
||||
include_managed_config,
|
||||
);
|
||||
run_command_under_sandbox(
|
||||
permissions_profile,
|
||||
DebugSandboxConfigOptions {
|
||||
permissions_profile,
|
||||
cwd,
|
||||
managed_requirements_mode,
|
||||
},
|
||||
command,
|
||||
config_overrides,
|
||||
codex_linux_sandbox_exe,
|
||||
@@ -95,11 +116,21 @@ pub async fn run_command_under_windows(
|
||||
) -> anyhow::Result<()> {
|
||||
let WindowsCommand {
|
||||
permissions_profile,
|
||||
cwd,
|
||||
include_managed_config,
|
||||
config_overrides,
|
||||
command,
|
||||
} = command;
|
||||
let managed_requirements_mode = ManagedRequirementsMode::for_profile_invocation(
|
||||
&permissions_profile,
|
||||
include_managed_config,
|
||||
);
|
||||
run_command_under_sandbox(
|
||||
permissions_profile,
|
||||
DebugSandboxConfigOptions {
|
||||
permissions_profile,
|
||||
cwd,
|
||||
managed_requirements_mode,
|
||||
},
|
||||
command,
|
||||
config_overrides,
|
||||
codex_linux_sandbox_exe,
|
||||
@@ -117,8 +148,34 @@ enum SandboxType {
|
||||
Windows,
|
||||
}
|
||||
|
||||
async fn run_command_under_sandbox(
|
||||
#[derive(Debug)]
|
||||
struct DebugSandboxConfigOptions {
|
||||
permissions_profile: Option<String>,
|
||||
cwd: Option<PathBuf>,
|
||||
managed_requirements_mode: ManagedRequirementsMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ManagedRequirementsMode {
|
||||
Include,
|
||||
Ignore,
|
||||
}
|
||||
|
||||
impl ManagedRequirementsMode {
|
||||
fn for_profile_invocation(
|
||||
permissions_profile: &Option<String>,
|
||||
include_managed_config: bool,
|
||||
) -> Self {
|
||||
if permissions_profile.is_some() && !include_managed_config {
|
||||
Self::Ignore
|
||||
} else {
|
||||
Self::Include
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_command_under_sandbox(
|
||||
config_options: DebugSandboxConfigOptions,
|
||||
command: Vec<String>,
|
||||
config_overrides: CliConfigOverrides,
|
||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
@@ -132,7 +189,7 @@ async fn run_command_under_sandbox(
|
||||
.parse_overrides()
|
||||
.map_err(anyhow::Error::msg)?,
|
||||
codex_linux_sandbox_exe,
|
||||
permissions_profile,
|
||||
config_options,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -571,12 +628,12 @@ mod windows_stdio_bridge {
|
||||
async fn load_debug_sandbox_config(
|
||||
cli_overrides: Vec<(String, TomlValue)>,
|
||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
permissions_profile: Option<String>,
|
||||
options: DebugSandboxConfigOptions,
|
||||
) -> anyhow::Result<Config> {
|
||||
load_debug_sandbox_config_with_codex_home(
|
||||
cli_overrides,
|
||||
codex_linux_sandbox_exe,
|
||||
permissions_profile,
|
||||
options,
|
||||
/*codex_home*/ None,
|
||||
)
|
||||
.await
|
||||
@@ -585,9 +642,15 @@ async fn load_debug_sandbox_config(
|
||||
async fn load_debug_sandbox_config_with_codex_home(
|
||||
mut cli_overrides: Vec<(String, TomlValue)>,
|
||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
||||
permissions_profile: Option<String>,
|
||||
options: DebugSandboxConfigOptions,
|
||||
codex_home: Option<PathBuf>,
|
||||
) -> anyhow::Result<Config> {
|
||||
let DebugSandboxConfigOptions {
|
||||
permissions_profile,
|
||||
cwd,
|
||||
managed_requirements_mode,
|
||||
} = options;
|
||||
|
||||
if let Some(permissions_profile) = permissions_profile {
|
||||
cli_overrides.push((
|
||||
"default_permissions".to_string(),
|
||||
@@ -604,10 +667,12 @@ async fn load_debug_sandbox_config_with_codex_home(
|
||||
let config = build_debug_sandbox_config(
|
||||
cli_overrides.clone(),
|
||||
ConfigOverrides {
|
||||
cwd: cwd.clone(),
|
||||
codex_linux_sandbox_exe: codex_linux_sandbox_exe.clone(),
|
||||
..Default::default()
|
||||
},
|
||||
codex_home.clone(),
|
||||
managed_requirements_mode,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -619,10 +684,12 @@ async fn load_debug_sandbox_config_with_codex_home(
|
||||
cli_overrides,
|
||||
ConfigOverrides {
|
||||
sandbox_mode: Some(SandboxMode::ReadOnly),
|
||||
cwd,
|
||||
codex_linux_sandbox_exe,
|
||||
..Default::default()
|
||||
},
|
||||
codex_home,
|
||||
managed_requirements_mode,
|
||||
)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
@@ -632,10 +699,17 @@ async fn build_debug_sandbox_config(
|
||||
cli_overrides: Vec<(String, TomlValue)>,
|
||||
harness_overrides: ConfigOverrides,
|
||||
codex_home: Option<PathBuf>,
|
||||
managed_requirements_mode: ManagedRequirementsMode,
|
||||
) -> std::io::Result<Config> {
|
||||
let mut builder = ConfigBuilder::default()
|
||||
.cli_overrides(cli_overrides)
|
||||
.harness_overrides(harness_overrides);
|
||||
if let ManagedRequirementsMode::Ignore = managed_requirements_mode {
|
||||
builder = builder.loader_overrides(LoaderOverrides {
|
||||
ignore_managed_requirements: true,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
if let Some(codex_home) = codex_home {
|
||||
builder = builder
|
||||
.codex_home(codex_home.clone())
|
||||
@@ -701,6 +775,7 @@ mod tests {
|
||||
Vec::new(),
|
||||
ConfigOverrides::default(),
|
||||
Some(codex_home_path.clone()),
|
||||
ManagedRequirementsMode::Include,
|
||||
)
|
||||
.await?;
|
||||
let legacy_config = build_debug_sandbox_config(
|
||||
@@ -710,13 +785,18 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
Some(codex_home_path.clone()),
|
||||
ManagedRequirementsMode::Include,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let config = load_debug_sandbox_config_with_codex_home(
|
||||
Vec::new(),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
/*permissions_profile*/ None,
|
||||
DebugSandboxConfigOptions {
|
||||
permissions_profile: None,
|
||||
cwd: None,
|
||||
managed_requirements_mode: ManagedRequirementsMode::Include,
|
||||
},
|
||||
Some(codex_home_path),
|
||||
)
|
||||
.await?;
|
||||
@@ -752,6 +832,7 @@ mod tests {
|
||||
cli_overrides.clone(),
|
||||
ConfigOverrides::default(),
|
||||
Some(codex_home_path.clone()),
|
||||
ManagedRequirementsMode::Include,
|
||||
)
|
||||
.await?;
|
||||
let read_only_config = build_debug_sandbox_config(
|
||||
@@ -761,13 +842,18 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
Some(codex_home_path.clone()),
|
||||
ManagedRequirementsMode::Include,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let config = load_debug_sandbox_config_with_codex_home(
|
||||
cli_overrides,
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
/*permissions_profile*/ None,
|
||||
DebugSandboxConfigOptions {
|
||||
permissions_profile: None,
|
||||
cwd: None,
|
||||
managed_requirements_mode: ManagedRequirementsMode::Include,
|
||||
},
|
||||
Some(codex_home_path),
|
||||
)
|
||||
.await?;
|
||||
@@ -811,13 +897,18 @@ mod tests {
|
||||
..Default::default()
|
||||
},
|
||||
Some(codex_home_path.clone()),
|
||||
ManagedRequirementsMode::Include,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let config = load_debug_sandbox_config_with_codex_home(
|
||||
Vec::new(),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
/*permissions_profile*/ None,
|
||||
DebugSandboxConfigOptions {
|
||||
permissions_profile: None,
|
||||
cwd: None,
|
||||
managed_requirements_mode: ManagedRequirementsMode::Include,
|
||||
},
|
||||
Some(codex_home_path),
|
||||
)
|
||||
.await?;
|
||||
@@ -838,7 +929,11 @@ mod tests {
|
||||
let config = load_debug_sandbox_config_with_codex_home(
|
||||
Vec::new(),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
Some(":workspace".to_string()),
|
||||
DebugSandboxConfigOptions {
|
||||
permissions_profile: Some(":workspace".to_string()),
|
||||
cwd: None,
|
||||
managed_requirements_mode: ManagedRequirementsMode::Ignore,
|
||||
},
|
||||
Some(codex_home.path().to_path_buf()),
|
||||
)
|
||||
.await?;
|
||||
@@ -867,7 +962,11 @@ mod tests {
|
||||
let config = load_debug_sandbox_config_with_codex_home(
|
||||
Vec::new(),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
Some(":workspace".to_string()),
|
||||
DebugSandboxConfigOptions {
|
||||
permissions_profile: Some(":workspace".to_string()),
|
||||
cwd: None,
|
||||
managed_requirements_mode: ManagedRequirementsMode::Ignore,
|
||||
},
|
||||
Some(codex_home.path().to_path_buf()),
|
||||
)
|
||||
.await?;
|
||||
@@ -892,7 +991,11 @@ mod tests {
|
||||
let config = load_debug_sandbox_config_with_codex_home(
|
||||
Vec::new(),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
Some("limited-read-test".to_string()),
|
||||
DebugSandboxConfigOptions {
|
||||
permissions_profile: Some("limited-read-test".to_string()),
|
||||
cwd: None,
|
||||
managed_requirements_mode: ManagedRequirementsMode::Ignore,
|
||||
},
|
||||
Some(codex_home.path().to_path_buf()),
|
||||
)
|
||||
.await?;
|
||||
@@ -904,6 +1007,7 @@ mod tests {
|
||||
)],
|
||||
ConfigOverrides::default(),
|
||||
Some(codex_home.path().to_path_buf()),
|
||||
ManagedRequirementsMode::Include,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -914,4 +1018,26 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn debug_sandbox_uses_explicit_profile_cwd() -> anyhow::Result<()> {
|
||||
let codex_home = TempDir::new()?;
|
||||
let cwd = TempDir::new()?;
|
||||
|
||||
let config = load_debug_sandbox_config_with_codex_home(
|
||||
Vec::new(),
|
||||
/*codex_linux_sandbox_exe*/ None,
|
||||
DebugSandboxConfigOptions {
|
||||
permissions_profile: Some(":workspace".to_string()),
|
||||
cwd: Some(cwd.path().to_path_buf()),
|
||||
managed_requirements_mode: ManagedRequirementsMode::Ignore,
|
||||
},
|
||||
Some(codex_home.path().to_path_buf()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert_eq!(config.cwd.as_path(), cwd.path());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user