feat: introduce Permissions (#11633)

## Why
We currently carry multiple permission-related concepts directly on
`Config` for shell/unified-exec behavior (`approval_policy`,
`sandbox_policy`, `network`, `shell_environment_policy`,
`windows_sandbox_mode`).

Consolidating these into one in-memory struct makes permission handling
easier to reason about and sets up the next step: supporting named
permission profiles (`[permissions.PROFILE_NAME]`) without changing
behavior now.

This change is mostly mechanical: it updates existing callsites to go
through `config.permissions`, but it does not yet refactor those
callsites to take a single `Permissions` value in places where multiple
permission fields are still threaded separately.

This PR intentionally **does not** change the on-disk `config.toml`
format yet and keeps compatibility with legacy config keys.

## What Changed
- Introduced `Permissions` in `core/src/config/mod.rs`.
- Added `Config::permissions` and moved effective runtime permission
fields under it:
  - `approval_policy`
  - `sandbox_policy`
  - `network`
  - `shell_environment_policy`
  - `windows_sandbox_mode`
- Updated config loading/building so these effective values are still
derived from the same existing config inputs and constraints.
- Updated Windows sandbox helpers/resolution to read/write via
`permissions`.
- Threaded the new field through all permission consumers across core
runtime, app-server, CLI/exec, TUI, and sandbox summary code.
- Updated affected tests to reference `config.permissions.*`.
- Renamed the struct/field from
`EffectivePermissions`/`effective_permissions` to
`Permissions`/`permissions` and aligned variable naming accordingly.

## Verification
- `just fix -p codex-core -p codex-tui -p codex-cli -p codex-app-server
-p codex-exec -p codex-utils-sandbox-summary`
- `cargo build -p codex-core -p codex-tui -p codex-cli -p
codex-app-server -p codex-exec -p codex-utils-sandbox-summary`
This commit is contained in:
Michael Bolin
2026-02-12 14:42:54 -08:00
committed by GitHub
Unverified
parent d7cb70ed26
commit a4cc1a4a85
30 changed files with 280 additions and 193 deletions
+1
View File
@@ -122,6 +122,7 @@ Rules:
}
if profile.read_only {
config
.permissions
.sandbox_policy
.set(SandboxPolicy::new_read_only_policy())
.map_err(|err| format!("sandbox_policy is invalid: {err}"))?;
+18 -18
View File
@@ -389,8 +389,8 @@ impl Codex {
personality: config.personality,
base_instructions,
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.approval_policy.clone(),
sandbox_policy: config.sandbox_policy.clone(),
approval_policy: config.permissions.approval_policy.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
cwd: config.cwd.clone(),
codex_home: config.codex_home.clone(),
@@ -932,7 +932,7 @@ impl Session {
sandbox_policy: session_configuration.sandbox_policy.get().clone(),
network,
windows_sandbox_level: session_configuration.windows_sandbox_level,
shell_environment_policy: per_turn_config.shell_environment_policy.clone(),
shell_environment_policy: per_turn_config.permissions.shell_environment_policy.clone(),
tools_config,
features: per_turn_config.features.clone(),
ghost_snapshot: per_turn_config.ghost_snapshot.clone(),
@@ -1099,7 +1099,7 @@ impl Session {
});
}
maybe_push_unstable_features_warning(&config, &mut post_session_configured_events);
if config.approval_policy.value() == AskForApproval::OnFailure {
if config.permissions.approval_policy.value() == AskForApproval::OnFailure {
post_session_configured_events.push(Event {
id: "".to_owned(),
msg: EventMsg::Warning(WarningEvent {
@@ -1142,8 +1142,8 @@ impl Session {
config.model_reasoning_summary,
config.model_context_window,
config.model_auto_compact_token_limit,
config.approval_policy.value(),
config.sandbox_policy.get().clone(),
config.permissions.approval_policy.value(),
config.permissions.sandbox_policy.get().clone(),
mcp_servers.keys().map(String::as_str).collect(),
config.active_profile.clone(),
);
@@ -1175,7 +1175,7 @@ impl Session {
session_configuration.thread_name = thread_name.clone();
let mut state = SessionState::new(session_configuration.clone());
let network_proxy =
match config.network.as_ref() {
match config.permissions.network.as_ref() {
Some(spec) => Some(spec.start_proxy().await.map_err(|err| {
anyhow::anyhow!("failed to start managed network proxy: {err}")
})?),
@@ -1697,7 +1697,7 @@ impl Session {
if sandbox_policy_changed {
let sandbox_state = SandboxState {
sandbox_policy: per_turn_config.sandbox_policy.get().clone(),
sandbox_policy: per_turn_config.permissions.sandbox_policy.get().clone(),
codex_linux_sandbox_exe: per_turn_config.codex_linux_sandbox_exe.clone(),
sandbox_cwd: per_turn_config.cwd.clone(),
use_linux_sandbox_bwrap: per_turn_config
@@ -6312,8 +6312,8 @@ mod tests {
.clone()
.unwrap_or_else(|| model_info.get_model_instructions(config.personality)),
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.approval_policy.clone(),
sandbox_policy: config.sandbox_policy.clone(),
approval_policy: config.permissions.approval_policy.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
cwd: config.cwd.clone(),
codex_home: config.codex_home.clone(),
@@ -6403,8 +6403,8 @@ mod tests {
.clone()
.unwrap_or_else(|| model_info.get_model_instructions(config.personality)),
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.approval_policy.clone(),
sandbox_policy: config.sandbox_policy.clone(),
approval_policy: config.permissions.approval_policy.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
cwd: config.cwd.clone(),
codex_home: config.codex_home.clone(),
@@ -6713,8 +6713,8 @@ mod tests {
.clone()
.unwrap_or_else(|| model_info.get_model_instructions(config.personality)),
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.approval_policy.clone(),
sandbox_policy: config.sandbox_policy.clone(),
approval_policy: config.permissions.approval_policy.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
cwd: config.cwd.clone(),
codex_home: config.codex_home.clone(),
@@ -6766,8 +6766,8 @@ mod tests {
.clone()
.unwrap_or_else(|| model_info.get_model_instructions(config.personality)),
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.approval_policy.clone(),
sandbox_policy: config.sandbox_policy.clone(),
approval_policy: config.permissions.approval_policy.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
cwd: config.cwd.clone(),
codex_home: config.codex_home.clone(),
@@ -6912,8 +6912,8 @@ mod tests {
.clone()
.unwrap_or_else(|| model_info.get_model_instructions(config.personality)),
compact_prompt: config.compact_prompt.clone(),
approval_policy: config.approval_policy.clone(),
sandbox_policy: config.sandbox_policy.clone(),
approval_policy: config.permissions.approval_policy.clone(),
sandbox_policy: config.permissions.sandbox_policy.clone(),
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
cwd: config.cwd.clone(),
codex_home: config.codex_home.clone(),
+84 -58
View File
@@ -117,6 +117,22 @@ pub(crate) fn test_config() -> Config {
.expect("load default test config")
}
/// Application configuration loaded from disk and merged with overrides.
#[derive(Debug, Clone, PartialEq)]
pub struct Permissions {
/// Approval policy for executing commands.
pub approval_policy: Constrained<AskForApproval>,
/// Effective sandbox policy used for shell/unified exec.
pub sandbox_policy: Constrained<SandboxPolicy>,
/// Effective network configuration applied to all spawned processes.
pub network: Option<NetworkProxySpec>,
/// Policy used to build process environments for shell/unified exec.
pub shell_environment_policy: ShellEnvironmentPolicy,
/// Effective Windows sandbox mode derived from `[windows].sandbox` or
/// legacy feature keys.
pub windows_sandbox_mode: Option<WindowsSandboxModeToml>,
}
/// Application configuration loaded from disk and merged with overrides.
#[derive(Debug, Clone, PartialEq)]
pub struct Config {
@@ -148,25 +164,18 @@ pub struct Config {
/// Optionally specify the personality of the model
pub personality: Option<Personality>,
/// Approval policy for executing commands.
pub approval_policy: Constrained<AskForApproval>,
pub sandbox_policy: Constrained<SandboxPolicy>,
/// Effective permission configuration for shell tool execution.
pub permissions: Permissions,
/// enforce_residency means web traffic cannot be routed outside of a
/// particular geography. HTTP clients should direct their requests
/// using backend-specific headers or URLs to enforce this.
pub enforce_residency: Constrained<Option<ResidencyRequirement>>,
/// Effective network configuration applied to all spawned processes.
pub network: Option<NetworkProxySpec>,
/// True if the user passed in an override or set a value in config.toml
/// for either of approval_policy or sandbox_mode.
pub did_user_set_custom_approval_policy_or_sandbox_mode: bool,
pub shell_environment_policy: ShellEnvironmentPolicy,
/// When `true`, `AgentReasoning` events emitted by the backend will be
/// suppressed from the frontend output. This can reduce visual noise when
/// users are only interested in the final agent responses.
@@ -345,10 +354,6 @@ pub struct Config {
/// Settings for ghost snapshots (used for undo).
pub ghost_snapshot: GhostSnapshotConfig,
/// Effective Windows sandbox mode derived from `[windows].sandbox` or
/// legacy feature keys.
pub windows_sandbox_mode: Option<WindowsSandboxModeToml>,
/// Centralized feature flags; source of truth for feature gating.
pub features: Features,
@@ -1726,12 +1731,15 @@ impl Config {
model_provider,
cwd: resolved_cwd,
startup_warnings,
approval_policy: constrained_approval_policy.value,
sandbox_policy: constrained_sandbox_policy.value,
permissions: Permissions {
approval_policy: constrained_approval_policy.value,
sandbox_policy: constrained_sandbox_policy.value,
network,
shell_environment_policy,
windows_sandbox_mode,
},
enforce_residency: enforce_residency.value,
network,
did_user_set_custom_approval_policy_or_sandbox_mode,
shell_environment_policy,
notify: cfg.notify,
user_instructions,
base_instructions,
@@ -1796,7 +1804,6 @@ impl Config {
web_search_mode: constrained_web_search_mode.value,
use_experimental_unified_exec_tool,
ghost_snapshot,
windows_sandbox_mode,
features,
suppress_unstable_features_warning: cfg
.suppress_unstable_features_warning
@@ -1902,28 +1909,28 @@ impl Config {
}
pub fn set_windows_sandbox_enabled(&mut self, value: bool) {
self.windows_sandbox_mode = if value {
self.permissions.windows_sandbox_mode = if value {
Some(WindowsSandboxModeToml::Unelevated)
} else if matches!(
self.windows_sandbox_mode,
self.permissions.windows_sandbox_mode,
Some(WindowsSandboxModeToml::Unelevated)
) {
None
} else {
self.windows_sandbox_mode
self.permissions.windows_sandbox_mode
};
}
pub fn set_windows_elevated_sandbox_enabled(&mut self, value: bool) {
self.windows_sandbox_mode = if value {
self.permissions.windows_sandbox_mode = if value {
Some(WindowsSandboxModeToml::Elevated)
} else if matches!(
self.windows_sandbox_mode,
self.permissions.windows_sandbox_mode,
Some(WindowsSandboxModeToml::Elevated)
) {
None
} else {
self.windows_sandbox_mode
self.permissions.windows_sandbox_mode
};
}
}
@@ -2366,12 +2373,12 @@ trust_level = "trusted"
let expected_backend = AbsolutePathBuf::try_from(backend).unwrap();
if cfg!(target_os = "windows") {
match config.sandbox_policy.get() {
match config.permissions.sandbox_policy.get() {
SandboxPolicy::ReadOnly { .. } => {}
other => panic!("expected read-only policy on Windows, got {other:?}"),
}
} else {
match config.sandbox_policy.get() {
match config.permissions.sandbox_policy.get() {
SandboxPolicy::WorkspaceWrite { writable_roots, .. } => {
assert_eq!(
writable_roots
@@ -2660,7 +2667,7 @@ profile = "project"
)?;
assert!(matches!(
config.sandbox_policy.get(),
config.permissions.sandbox_policy.get(),
&SandboxPolicy::DangerFullAccess
));
assert!(config.did_user_set_custom_approval_policy_or_sandbox_mode);
@@ -2698,12 +2705,12 @@ profile = "project"
if cfg!(target_os = "windows") {
assert!(matches!(
config.sandbox_policy.get(),
config.permissions.sandbox_policy.get(),
SandboxPolicy::ReadOnly { .. }
));
} else {
assert!(matches!(
config.sandbox_policy.get(),
config.permissions.sandbox_policy.get(),
SandboxPolicy::WorkspaceWrite { .. }
));
}
@@ -4019,12 +4026,15 @@ model_verbosity = "high"
model_auto_compact_token_limit: None,
model_provider_id: "openai".to_string(),
model_provider: fixture.openai_provider.clone(),
approval_policy: Constrained::allow_any(AskForApproval::Never),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
permissions: Permissions {
approval_policy: Constrained::allow_any(AskForApproval::Never),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
network: None,
shell_environment_policy: ShellEnvironmentPolicy::default(),
windows_sandbox_mode: None,
},
enforce_residency: Constrained::allow_any(None),
network: None,
did_user_set_custom_approval_policy_or_sandbox_mode: true,
shell_environment_policy: ShellEnvironmentPolicy::default(),
user_instructions: None,
notify: None,
cwd: fixture.cwd(),
@@ -4067,7 +4077,6 @@ model_verbosity = "high"
suppress_unstable_features_warning: false,
active_profile: Some("o3".to_string()),
active_project: ProjectConfig { trust_level: None },
windows_sandbox_mode: None,
windows_wsl_setup_acknowledged: false,
notices: Default::default(),
check_for_update_on_startup: true,
@@ -4126,12 +4135,15 @@ model_verbosity = "high"
model_auto_compact_token_limit: None,
model_provider_id: "openai-custom".to_string(),
model_provider: fixture.openai_custom_provider.clone(),
approval_policy: Constrained::allow_any(AskForApproval::UnlessTrusted),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
permissions: Permissions {
approval_policy: Constrained::allow_any(AskForApproval::UnlessTrusted),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
network: None,
shell_environment_policy: ShellEnvironmentPolicy::default(),
windows_sandbox_mode: None,
},
enforce_residency: Constrained::allow_any(None),
network: None,
did_user_set_custom_approval_policy_or_sandbox_mode: true,
shell_environment_policy: ShellEnvironmentPolicy::default(),
user_instructions: None,
notify: None,
cwd: fixture.cwd(),
@@ -4174,7 +4186,6 @@ model_verbosity = "high"
suppress_unstable_features_warning: false,
active_profile: Some("gpt3".to_string()),
active_project: ProjectConfig { trust_level: None },
windows_sandbox_mode: None,
windows_wsl_setup_acknowledged: false,
notices: Default::default(),
check_for_update_on_startup: true,
@@ -4231,12 +4242,15 @@ model_verbosity = "high"
model_auto_compact_token_limit: None,
model_provider_id: "openai".to_string(),
model_provider: fixture.openai_provider.clone(),
approval_policy: Constrained::allow_any(AskForApproval::OnFailure),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
permissions: Permissions {
approval_policy: Constrained::allow_any(AskForApproval::OnFailure),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
network: None,
shell_environment_policy: ShellEnvironmentPolicy::default(),
windows_sandbox_mode: None,
},
enforce_residency: Constrained::allow_any(None),
network: None,
did_user_set_custom_approval_policy_or_sandbox_mode: true,
shell_environment_policy: ShellEnvironmentPolicy::default(),
user_instructions: None,
notify: None,
cwd: fixture.cwd(),
@@ -4279,7 +4293,6 @@ model_verbosity = "high"
suppress_unstable_features_warning: false,
active_profile: Some("zdr".to_string()),
active_project: ProjectConfig { trust_level: None },
windows_sandbox_mode: None,
windows_wsl_setup_acknowledged: false,
notices: Default::default(),
check_for_update_on_startup: true,
@@ -4322,12 +4335,15 @@ model_verbosity = "high"
model_auto_compact_token_limit: None,
model_provider_id: "openai".to_string(),
model_provider: fixture.openai_provider.clone(),
approval_policy: Constrained::allow_any(AskForApproval::OnFailure),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
permissions: Permissions {
approval_policy: Constrained::allow_any(AskForApproval::OnFailure),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
network: None,
shell_environment_policy: ShellEnvironmentPolicy::default(),
windows_sandbox_mode: None,
},
enforce_residency: Constrained::allow_any(None),
network: None,
did_user_set_custom_approval_policy_or_sandbox_mode: true,
shell_environment_policy: ShellEnvironmentPolicy::default(),
user_instructions: None,
notify: None,
cwd: fixture.cwd(),
@@ -4370,7 +4386,6 @@ model_verbosity = "high"
suppress_unstable_features_warning: false,
active_profile: Some("gpt5".to_string()),
active_project: ProjectConfig { trust_level: None },
windows_sandbox_mode: None,
windows_wsl_setup_acknowledged: false,
notices: Default::default(),
check_for_update_on_startup: true,
@@ -4903,7 +4918,7 @@ mcp_oauth_callback_port = 5678
// Verify that untrusted projects get UnlessTrusted approval policy
assert_eq!(
config.approval_policy.value(),
config.permissions.approval_policy.value(),
AskForApproval::UnlessTrusted,
"Expected UnlessTrusted approval policy for untrusted project"
);
@@ -4911,13 +4926,16 @@ mcp_oauth_callback_port = 5678
// Verify that untrusted projects still get WorkspaceWrite sandbox (or ReadOnly on Windows)
if cfg!(target_os = "windows") {
assert!(
matches!(config.sandbox_policy.get(), SandboxPolicy::ReadOnly { .. }),
matches!(
config.permissions.sandbox_policy.get(),
SandboxPolicy::ReadOnly { .. }
),
"Expected ReadOnly on Windows"
);
} else {
assert!(
matches!(
config.sandbox_policy.get(),
config.permissions.sandbox_policy.get(),
SandboxPolicy::WorkspaceWrite { .. }
),
"Expected WorkspaceWrite sandbox for untrusted project"
@@ -4944,9 +4962,8 @@ mcp_oauth_callback_port = 5678
}))
.build()
.await?;
assert_eq!(
*config.sandbox_policy.get(),
*config.permissions.sandbox_policy.get(),
SandboxPolicy::new_read_only_policy()
);
Ok(())
@@ -4983,7 +5000,7 @@ mcp_oauth_callback_port = 5678
.build()
.await?;
assert_eq!(
*config.sandbox_policy.get(),
*config.permissions.sandbox_policy.get(),
SandboxPolicy::new_read_only_policy()
);
Ok(())
@@ -5015,7 +5032,10 @@ mcp_oauth_callback_port = 5678
assert_eq!(config.web_search_mode.value(), WebSearchMode::Cached);
assert_eq!(
resolve_web_search_mode_for_turn(&config.web_search_mode, config.sandbox_policy.get()),
resolve_web_search_mode_for_turn(
&config.web_search_mode,
config.permissions.sandbox_policy.get(),
),
WebSearchMode::Cached,
);
Ok(())
@@ -5049,7 +5069,10 @@ trust_level = "untrusted"
.build()
.await?;
assert_eq!(config.approval_policy.value(), AskForApproval::OnRequest);
assert_eq!(
config.permissions.approval_policy.value(),
AskForApproval::OnRequest
);
Ok(())
}
@@ -5074,7 +5097,10 @@ trust_level = "untrusted"
}))
.build()
.await?;
assert_eq!(config.approval_policy.value(), AskForApproval::OnRequest);
assert_eq!(
config.permissions.approval_policy.value(),
AskForApproval::OnRequest
);
Ok(())
}
}
+3 -1
View File
@@ -95,7 +95,8 @@ pub(in crate::memories) async fn run_global_memory_consolidation(
let consolidation_config = {
let mut consolidation_config = config.as_ref().clone();
consolidation_config.cwd = root.clone();
consolidation_config.approval_policy = Constrained::allow_only(AskForApproval::Never);
consolidation_config.permissions.approval_policy =
Constrained::allow_only(AskForApproval::Never);
let mut writable_roots = Vec::new();
match AbsolutePathBuf::from_absolute_path(consolidation_config.codex_home.clone()) {
Ok(codex_home) => writable_roots.push(codex_home),
@@ -112,6 +113,7 @@ pub(in crate::memories) async fn run_global_memory_consolidation(
exclude_slash_tmp: false,
};
if let Err(err) = consolidation_config
.permissions
.sandbox_policy
.set(consolidation_sandbox_policy)
{
+1 -1
View File
@@ -97,7 +97,7 @@ async fn start_review_conversation(
// Set explicit review rubric for the sub-agent
sub_agent_config.base_instructions = Some(crate::REVIEW_PROMPT.to_string());
sub_agent_config.approval_policy = Constrained::allow_only(AskForApproval::Never);
sub_agent_config.permissions.approval_policy = Constrained::allow_only(AskForApproval::Never);
let model = config
.review_model
+11 -6
View File
@@ -818,11 +818,12 @@ fn build_agent_shared_config(
config.model_reasoning_summary = turn.reasoning_summary;
config.developer_instructions = turn.developer_instructions.clone();
config.compact_prompt = turn.compact_prompt.clone();
config.shell_environment_policy = turn.shell_environment_policy.clone();
config.permissions.shell_environment_policy = turn.shell_environment_policy.clone();
config.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone();
config.cwd = turn.cwd.clone();
config.approval_policy = Constrained::allow_only(AskForApproval::Never);
config.permissions.approval_policy = Constrained::allow_only(AskForApproval::Never);
config
.permissions
.sandbox_policy
.set(turn.sandbox_policy.clone())
.map_err(|err| {
@@ -1717,8 +1718,8 @@ mod tests {
turn.cwd = temp_dir.path().to_path_buf();
turn.codex_linux_sandbox_exe = Some(PathBuf::from("/bin/echo"));
turn.sandbox_policy = pick_allowed_sandbox_policy(
&turn.config.sandbox_policy,
turn.config.sandbox_policy.get().clone(),
&turn.config.permissions.sandbox_policy,
turn.config.permissions.sandbox_policy.get().clone(),
);
let config = build_agent_spawn_config(&base_instructions, &turn, 0).expect("spawn config");
@@ -1730,14 +1731,16 @@ mod tests {
expected.model_reasoning_summary = turn.reasoning_summary;
expected.developer_instructions = turn.developer_instructions.clone();
expected.compact_prompt = turn.compact_prompt.clone();
expected.shell_environment_policy = turn.shell_environment_policy.clone();
expected.permissions.shell_environment_policy = turn.shell_environment_policy.clone();
expected.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone();
expected.cwd = turn.cwd.clone();
expected
.permissions
.approval_policy
.set(AskForApproval::Never)
.expect("approval policy set");
expected
.permissions
.sandbox_policy
.set(turn.sandbox_policy)
.expect("sandbox policy set");
@@ -1777,14 +1780,16 @@ mod tests {
expected.model_reasoning_summary = turn.reasoning_summary;
expected.developer_instructions = turn.developer_instructions.clone();
expected.compact_prompt = turn.compact_prompt.clone();
expected.shell_environment_policy = turn.shell_environment_policy.clone();
expected.permissions.shell_environment_policy = turn.shell_environment_policy.clone();
expected.codex_linux_sandbox_exe = turn.codex_linux_sandbox_exe.clone();
expected.cwd = turn.cwd.clone();
expected
.permissions
.approval_policy
.set(AskForApproval::Never)
.expect("approval policy set");
expected
.permissions
.sandbox_policy
.set(turn.sandbox_policy)
.expect("sandbox policy set");
+1 -1
View File
@@ -25,7 +25,7 @@ pub trait WindowsSandboxLevelExt {
impl WindowsSandboxLevelExt for WindowsSandboxLevel {
fn from_config(config: &Config) -> WindowsSandboxLevel {
match config.windows_sandbox_mode {
match config.permissions.windows_sandbox_mode {
Some(WindowsSandboxModeToml::Elevated) => WindowsSandboxLevel::Elevated,
Some(WindowsSandboxModeToml::Unelevated) => WindowsSandboxLevel::RestrictedToken,
None => Self::from_features(&config.features),