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:
committed by
GitHub
Unverified
parent
857146b328
commit
5597925155
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ pub(crate) mod login;
|
||||
use clap::Parser;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_cli::CliConfigOverrides;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub use debug_sandbox::run_command_under_landlock;
|
||||
pub use debug_sandbox::run_command_under_seatbelt;
|
||||
@@ -19,12 +20,31 @@ pub use login::run_login_with_device_code;
|
||||
pub use login::run_login_with_device_code_fallback_to_browser;
|
||||
pub use login::run_logout;
|
||||
|
||||
// TODO: Deduplicate these shared sandbox options if we remove the explicit
|
||||
// `codex sandbox <os>` platform subcommands.
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct SeatbeltCommand {
|
||||
/// Named permissions profile to apply from the active configuration stack.
|
||||
#[arg(long = "permissions-profile", value_name = "NAME")]
|
||||
pub permissions_profile: Option<String>,
|
||||
|
||||
/// Working directory used for profile resolution and command execution.
|
||||
#[arg(
|
||||
short = 'C',
|
||||
long = "cd",
|
||||
value_name = "DIR",
|
||||
requires = "permissions_profile"
|
||||
)]
|
||||
pub cwd: Option<PathBuf>,
|
||||
|
||||
/// Include managed requirements while resolving an explicit permissions profile.
|
||||
#[arg(
|
||||
long = "include-managed-config",
|
||||
default_value_t = false,
|
||||
requires = "permissions_profile"
|
||||
)]
|
||||
pub include_managed_config: 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>,
|
||||
@@ -52,6 +72,23 @@ pub struct LandlockCommand {
|
||||
#[arg(long = "permissions-profile", value_name = "NAME")]
|
||||
pub permissions_profile: Option<String>,
|
||||
|
||||
/// Working directory used for profile resolution and command execution.
|
||||
#[arg(
|
||||
short = 'C',
|
||||
long = "cd",
|
||||
value_name = "DIR",
|
||||
requires = "permissions_profile"
|
||||
)]
|
||||
pub cwd: Option<PathBuf>,
|
||||
|
||||
/// Include managed requirements while resolving an explicit permissions profile.
|
||||
#[arg(
|
||||
long = "include-managed-config",
|
||||
default_value_t = false,
|
||||
requires = "permissions_profile"
|
||||
)]
|
||||
pub include_managed_config: bool,
|
||||
|
||||
#[clap(skip)]
|
||||
pub config_overrides: CliConfigOverrides,
|
||||
|
||||
@@ -66,6 +103,23 @@ pub struct WindowsCommand {
|
||||
#[arg(long = "permissions-profile", value_name = "NAME")]
|
||||
pub permissions_profile: Option<String>,
|
||||
|
||||
/// Working directory used for profile resolution and command execution.
|
||||
#[arg(
|
||||
short = 'C',
|
||||
long = "cd",
|
||||
value_name = "DIR",
|
||||
requires = "permissions_profile"
|
||||
)]
|
||||
pub cwd: Option<PathBuf>,
|
||||
|
||||
/// Include managed requirements while resolving an explicit permissions profile.
|
||||
#[arg(
|
||||
long = "include-managed-config",
|
||||
default_value_t = false,
|
||||
requires = "permissions_profile"
|
||||
)]
|
||||
pub include_managed_config: bool,
|
||||
|
||||
#[clap(skip)]
|
||||
pub config_overrides: CliConfigOverrides,
|
||||
|
||||
|
||||
@@ -1946,6 +1946,14 @@ mod tests {
|
||||
assert_eq!(command.command, vec!["echo"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sandbox_macos_rejects_explicit_profile_controls_without_profile() {
|
||||
let err = MultitoolCli::try_parse_from(["codex", "sandbox", "macos", "-C", "/tmp"])
|
||||
.expect_err("parse should fail");
|
||||
|
||||
assert_eq!(err.kind(), clap::error::ErrorKind::MissingRequiredArgument);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn plugin_marketplace_remove_parses_under_plugin() {
|
||||
let cli =
|
||||
|
||||
@@ -92,41 +92,46 @@ pub async fn load_config_layers_state(
|
||||
cloud_requirements: CloudRequirementsLoader,
|
||||
thread_config_loader: &dyn ThreadConfigLoader,
|
||||
) -> io::Result<ConfigLayerStack> {
|
||||
let ignore_managed_requirements = overrides.ignore_managed_requirements;
|
||||
let ignore_user_config = overrides.ignore_user_config;
|
||||
let ignore_user_and_project_exec_policy_rules =
|
||||
overrides.ignore_user_and_project_exec_policy_rules;
|
||||
let mut config_requirements_toml = ConfigRequirementsWithSources::default();
|
||||
|
||||
if let Some(requirements) = cloud_requirements.get().await.map_err(io::Error::other)? {
|
||||
merge_requirements_with_remote_sandbox_config(
|
||||
if !ignore_managed_requirements {
|
||||
if let Some(requirements) = cloud_requirements.get().await.map_err(io::Error::other)? {
|
||||
merge_requirements_with_remote_sandbox_config(
|
||||
&mut config_requirements_toml,
|
||||
RequirementSource::CloudRequirements,
|
||||
requirements,
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
macos::load_managed_admin_requirements_toml(
|
||||
&mut config_requirements_toml,
|
||||
RequirementSource::CloudRequirements,
|
||||
requirements,
|
||||
);
|
||||
overrides
|
||||
.macos_managed_config_requirements_base64
|
||||
.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Honor the system requirements.toml location.
|
||||
let requirements_toml_file = system_requirements_toml_file_with_overrides(&overrides)?;
|
||||
load_requirements_toml(fs, &mut config_requirements_toml, &requirements_toml_file).await?;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
macos::load_managed_admin_requirements_toml(
|
||||
&mut config_requirements_toml,
|
||||
overrides
|
||||
.macos_managed_config_requirements_base64
|
||||
.as_deref(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Honor the system requirements.toml location.
|
||||
let requirements_toml_file = system_requirements_toml_file_with_overrides(&overrides)?;
|
||||
load_requirements_toml(fs, &mut config_requirements_toml, &requirements_toml_file).await?;
|
||||
|
||||
// Make a best-effort to support the legacy `managed_config.toml` as a
|
||||
// requirements specification.
|
||||
let loaded_config_layers =
|
||||
layer_io::load_config_layers_internal(fs, codex_home, overrides.clone()).await?;
|
||||
load_requirements_from_legacy_scheme(
|
||||
&mut config_requirements_toml,
|
||||
loaded_config_layers.clone(),
|
||||
)
|
||||
.await?;
|
||||
if !ignore_managed_requirements {
|
||||
load_requirements_from_legacy_scheme(
|
||||
&mut config_requirements_toml,
|
||||
loaded_config_layers.clone(),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let thread_config_context = ThreadConfigContext {
|
||||
thread_id: None,
|
||||
|
||||
@@ -20,6 +20,7 @@ pub struct LoaderOverrides {
|
||||
pub managed_config_path: Option<PathBuf>,
|
||||
pub system_config_path: Option<PathBuf>,
|
||||
pub system_requirements_path: Option<PathBuf>,
|
||||
pub ignore_managed_requirements: bool,
|
||||
pub ignore_user_config: bool,
|
||||
pub ignore_user_and_project_exec_policy_rules: bool,
|
||||
//TODO(gt): Add a macos_ prefix to this field and remove the target_os check.
|
||||
@@ -38,6 +39,7 @@ impl LoaderOverrides {
|
||||
managed_config_path: Some(base.join("managed_config.toml")),
|
||||
system_config_path: Some(base.join("config.toml")),
|
||||
system_requirements_path: Some(base.join("requirements.toml")),
|
||||
ignore_managed_requirements: false,
|
||||
ignore_user_config: false,
|
||||
ignore_user_and_project_exec_policy_rules: false,
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
@@ -1084,6 +1084,58 @@ async fn load_config_layers_includes_cloud_requirements() -> anyhow::Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_config_layers_can_ignore_managed_requirements() -> anyhow::Result<()> {
|
||||
let tmp = tempdir()?;
|
||||
let codex_home = tmp.path().join("home");
|
||||
tokio::fs::create_dir_all(&codex_home).await?;
|
||||
let cwd = AbsolutePathBuf::from_absolute_path(tmp.path())?;
|
||||
|
||||
let managed_config_path = tmp.path().join("managed_config.toml");
|
||||
tokio::fs::write(&managed_config_path, "approval_policy = \"never\"\n").await?;
|
||||
let system_requirements_path = tmp.path().join("requirements.toml");
|
||||
tokio::fs::write(
|
||||
&system_requirements_path,
|
||||
"allowed_sandbox_modes = [\"read-only\"]\n",
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut overrides = LoaderOverrides::with_managed_config_path_for_tests(managed_config_path);
|
||||
overrides.system_requirements_path = Some(system_requirements_path);
|
||||
overrides.ignore_managed_requirements = true;
|
||||
|
||||
let cloud_requirements = CloudRequirementsLoader::new(async {
|
||||
Ok(Some(ConfigRequirementsToml {
|
||||
allowed_approval_policies: Some(vec![AskForApproval::Never]),
|
||||
..Default::default()
|
||||
}))
|
||||
});
|
||||
|
||||
let mut config = ConfigBuilder::default()
|
||||
.codex_home(codex_home)
|
||||
.fallback_cwd(Some(cwd.to_path_buf()))
|
||||
.loader_overrides(overrides)
|
||||
.cloud_requirements(cloud_requirements)
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
assert!(
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.can_set(&AskForApproval::OnRequest)
|
||||
.is_ok(),
|
||||
"ignoring managed requirements should leave on-request approval allowed"
|
||||
);
|
||||
config
|
||||
.permissions
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("ignoring managed requirements should allow setting on-request approval");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn load_config_layers_includes_cloud_hook_requirements() -> anyhow::Result<()> {
|
||||
let tmp = tempdir()?;
|
||||
|
||||
Reference in New Issue
Block a user