mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
permissions: remove core legacy policy round trips (#19394)
## Why Several execution paths still converted profile-backed permissions into `SandboxPolicy` and then rebuilt runtime permissions from that legacy shape. Those round trips are unnecessary after the preceding PRs and can lose split filesystem semantics. Core approval and escalation should carry the resolved profile directly. ## What Changed - Removes `sandbox_policy` from `ResolvedPermissionProfile`; the resolved permission object now carries the canonical `PermissionProfile` directly. - Updates exec-policy fallback, shell/unified-exec interception, escalation reruns, and related tests to pass profiles instead of legacy policies. - Removes legacy additional-permission merge helpers that built an effective `SandboxPolicy` before rebuilding runtime permissions. - Keeps legacy projections only at compatibility boundaries that still require `SandboxPolicy`, not in core permission computation. ## Verification - `cargo test -p codex-core direct_write_roots` - `cargo test -p codex-core runtime_roots_to_legacy_projection` - `cargo test -p codex-app-server requested_permissions_trust_project_uses_permission_profile_intent` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19394). * #19737 * #19736 * #19735 * #19734 * #19395 * __->__ #19394
This commit is contained in:
@@ -514,15 +514,15 @@ impl ShellHandler {
|
||||
emitter.begin(event_ctx).await;
|
||||
|
||||
let file_system_sandbox_policy = turn.file_system_sandbox_policy();
|
||||
let sandbox_policy = turn.sandbox_policy();
|
||||
let exec_approval_requirement = session
|
||||
.services
|
||||
.exec_policy
|
||||
.create_exec_approval_requirement_for_command(ExecApprovalRequest {
|
||||
command: &exec_params.command,
|
||||
approval_policy: turn.approval_policy.value(),
|
||||
sandbox_policy: &sandbox_policy,
|
||||
permission_profile: turn.permission_profile(),
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
sandbox_cwd: turn.cwd.as_path(),
|
||||
sandbox_permissions: if effective_additional_permissions.permissions_preapproved {
|
||||
codex_protocol::models::SandboxPermissions::UseDefault
|
||||
} else {
|
||||
|
||||
@@ -34,7 +34,6 @@ use codex_protocol::exec_output::ExecToolCallOutput;
|
||||
use codex_protocol::exec_output::StreamOutput;
|
||||
use codex_protocol::models::AdditionalPermissionProfile;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::models::SandboxEnforcement;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
@@ -63,6 +62,7 @@ use codex_shell_escalation::ShellCommandExecutor;
|
||||
use codex_shell_escalation::Stopwatch;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -206,9 +206,9 @@ pub(super) async fn try_run_zsh_fork(
|
||||
call_id: ctx.call_id.clone(),
|
||||
tool_name: GuardianCommandSource::Shell,
|
||||
approval_policy: ctx.turn.approval_policy.value(),
|
||||
sandbox_policy: command_executor.sandbox_policy.clone(),
|
||||
permission_profile: command_executor.permission_profile.clone(),
|
||||
file_system_sandbox_policy: command_executor.file_system_sandbox_policy.clone(),
|
||||
network_sandbox_policy: command_executor.network_sandbox_policy,
|
||||
sandbox_policy_cwd: command_executor.sandbox_policy_cwd.clone(),
|
||||
sandbox_permissions: req.sandbox_permissions,
|
||||
approval_sandbox_permissions,
|
||||
prompt_permissions: req.additional_permissions.clone(),
|
||||
@@ -268,7 +268,7 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
|
||||
network: exec_request.network.clone(),
|
||||
windows_sandbox_level: exec_request.windows_sandbox_level,
|
||||
arg0: exec_request.arg0.clone(),
|
||||
sandbox_policy_cwd: ctx.turn.cwd.clone(),
|
||||
sandbox_policy_cwd: exec_request.windows_sandbox_policy_cwd.clone(),
|
||||
codex_linux_sandbox_exe: ctx.turn.codex_linux_sandbox_exe.clone(),
|
||||
use_legacy_landlock: ctx.turn.features.use_legacy_landlock(),
|
||||
};
|
||||
@@ -279,9 +279,9 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
|
||||
call_id: ctx.call_id.clone(),
|
||||
tool_name: GuardianCommandSource::UnifiedExec,
|
||||
approval_policy: ctx.turn.approval_policy.value(),
|
||||
sandbox_policy: exec_request.sandbox_policy.clone(),
|
||||
permission_profile: exec_request.permission_profile.clone(),
|
||||
file_system_sandbox_policy: exec_request.file_system_sandbox_policy.clone(),
|
||||
network_sandbox_policy: exec_request.network_sandbox_policy,
|
||||
sandbox_policy_cwd: exec_request.windows_sandbox_policy_cwd.clone(),
|
||||
sandbox_permissions: req.sandbox_permissions,
|
||||
approval_sandbox_permissions: approval_sandbox_permissions(
|
||||
req.sandbox_permissions,
|
||||
@@ -314,9 +314,9 @@ struct CoreShellActionProvider {
|
||||
call_id: String,
|
||||
tool_name: GuardianCommandSource,
|
||||
approval_policy: AskForApproval,
|
||||
sandbox_policy: SandboxPolicy,
|
||||
permission_profile: PermissionProfile,
|
||||
file_system_sandbox_policy: FileSystemSandboxPolicy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy,
|
||||
sandbox_policy_cwd: AbsolutePathBuf,
|
||||
sandbox_permissions: SandboxPermissions,
|
||||
approval_sandbox_permissions: SandboxPermissions,
|
||||
prompt_permissions: Option<AdditionalPermissionProfile>,
|
||||
@@ -366,9 +366,7 @@ impl CoreShellActionProvider {
|
||||
|
||||
fn shell_request_escalation_execution(
|
||||
sandbox_permissions: SandboxPermissions,
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy,
|
||||
permission_profile: &PermissionProfile,
|
||||
additional_permissions: Option<&AdditionalPermissionProfile>,
|
||||
) -> EscalationExecution {
|
||||
match sandbox_permissions {
|
||||
@@ -381,15 +379,7 @@ impl CoreShellActionProvider {
|
||||
EscalationExecution::Permissions(
|
||||
EscalationPermissions::ResolvedPermissionProfile(
|
||||
ResolvedPermissionProfile {
|
||||
permission_profile:
|
||||
PermissionProfile::from_runtime_permissions_with_enforcement(
|
||||
SandboxEnforcement::from_legacy_sandbox_policy(
|
||||
sandbox_policy,
|
||||
),
|
||||
file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
),
|
||||
sandbox_policy: sandbox_policy.clone(),
|
||||
permission_profile: permission_profile.clone(),
|
||||
},
|
||||
),
|
||||
)
|
||||
@@ -608,8 +598,9 @@ impl EscalationPolicy for CoreShellActionProvider {
|
||||
argv,
|
||||
InterceptedExecPolicyContext {
|
||||
approval_policy: self.approval_policy,
|
||||
sandbox_policy: &self.sandbox_policy,
|
||||
permission_profile: self.permission_profile.clone(),
|
||||
file_system_sandbox_policy: &self.file_system_sandbox_policy,
|
||||
sandbox_cwd: self.sandbox_policy_cwd.as_path(),
|
||||
sandbox_permissions: self.approval_sandbox_permissions,
|
||||
enable_shell_wrapper_parsing:
|
||||
ENABLE_INTERCEPTED_EXEC_POLICY_SHELL_WRAPPER_PARSING,
|
||||
@@ -632,9 +623,7 @@ impl EscalationPolicy for CoreShellActionProvider {
|
||||
DecisionSource::PrefixRule => EscalationExecution::Unsandboxed,
|
||||
DecisionSource::UnmatchedCommandFallback => Self::shell_request_escalation_execution(
|
||||
self.sandbox_permissions,
|
||||
&self.sandbox_policy,
|
||||
&self.file_system_sandbox_policy,
|
||||
self.network_sandbox_policy,
|
||||
&self.permission_profile,
|
||||
self.prompt_permissions.as_ref(),
|
||||
),
|
||||
};
|
||||
@@ -660,8 +649,9 @@ fn evaluate_intercepted_exec_policy(
|
||||
) -> Evaluation {
|
||||
let InterceptedExecPolicyContext {
|
||||
approval_policy,
|
||||
sandbox_policy,
|
||||
permission_profile,
|
||||
file_system_sandbox_policy,
|
||||
sandbox_cwd,
|
||||
sandbox_permissions,
|
||||
enable_shell_wrapper_parsing,
|
||||
} = context;
|
||||
@@ -685,8 +675,9 @@ fn evaluate_intercepted_exec_policy(
|
||||
let fallback = |cmd: &[String]| {
|
||||
crate::exec_policy::render_decision_for_unmatched_command(
|
||||
approval_policy,
|
||||
sandbox_policy,
|
||||
&permission_profile,
|
||||
file_system_sandbox_policy,
|
||||
sandbox_cwd,
|
||||
cmd,
|
||||
sandbox_permissions,
|
||||
used_complex_parsing,
|
||||
@@ -702,11 +693,12 @@ fn evaluate_intercepted_exec_policy(
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone)]
|
||||
struct InterceptedExecPolicyContext<'a> {
|
||||
approval_policy: AskForApproval,
|
||||
sandbox_policy: &'a SandboxPolicy,
|
||||
permission_profile: PermissionProfile,
|
||||
file_system_sandbox_policy: &'a FileSystemSandboxPolicy,
|
||||
sandbox_cwd: &'a Path,
|
||||
sandbox_permissions: SandboxPermissions,
|
||||
enable_shell_wrapper_parsing: bool,
|
||||
}
|
||||
|
||||
@@ -66,6 +66,14 @@ fn read_only_file_system_sandbox_policy() -> FileSystemSandboxPolicy {
|
||||
}])
|
||||
}
|
||||
|
||||
fn permission_profile_from_sandbox_policy(sandbox_policy: &SandboxPolicy) -> PermissionProfile {
|
||||
PermissionProfile::from_legacy_sandbox_policy(sandbox_policy)
|
||||
}
|
||||
|
||||
fn test_sandbox_cwd() -> AbsolutePathBuf {
|
||||
AbsolutePathBuf::try_from(host_absolute_path(&["workspace"])).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execve_prompt_rejection_keeps_prefix_rules_on_rules_flag() {
|
||||
assert_eq!(
|
||||
@@ -266,12 +274,6 @@ fn shell_request_escalation_execution_is_explicit() {
|
||||
)),
|
||||
..Default::default()
|
||||
};
|
||||
let sandbox_policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![AbsolutePathBuf::from_absolute_path("/tmp/original/output").unwrap()],
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
};
|
||||
let file_system_sandbox_policy = FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Path {
|
||||
@@ -287,13 +289,15 @@ fn shell_request_escalation_execution_is_explicit() {
|
||||
},
|
||||
]);
|
||||
let network_sandbox_policy = NetworkSandboxPolicy::Restricted;
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions(
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
CoreShellActionProvider::shell_request_escalation_execution(
|
||||
crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
&sandbox_policy,
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
&permission_profile,
|
||||
/*additional_permissions*/ None,
|
||||
),
|
||||
EscalationExecution::TurnDefault,
|
||||
@@ -301,9 +305,7 @@ fn shell_request_escalation_execution_is_explicit() {
|
||||
assert_eq!(
|
||||
CoreShellActionProvider::shell_request_escalation_execution(
|
||||
crate::sandboxing::SandboxPermissions::RequireEscalated,
|
||||
&sandbox_policy,
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
&permission_profile,
|
||||
/*additional_permissions*/ None,
|
||||
),
|
||||
EscalationExecution::Unsandboxed,
|
||||
@@ -311,19 +313,11 @@ fn shell_request_escalation_execution_is_explicit() {
|
||||
assert_eq!(
|
||||
CoreShellActionProvider::shell_request_escalation_execution(
|
||||
crate::sandboxing::SandboxPermissions::WithAdditionalPermissions,
|
||||
&sandbox_policy,
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
&permission_profile,
|
||||
Some(&requested_permissions),
|
||||
),
|
||||
EscalationExecution::Permissions(EscalationPermissions::ResolvedPermissionProfile(
|
||||
ResolvedPermissionProfile {
|
||||
permission_profile: PermissionProfile::from_runtime_permissions(
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
),
|
||||
sandbox_policy,
|
||||
},
|
||||
ResolvedPermissionProfile { permission_profile },
|
||||
)),
|
||||
);
|
||||
}
|
||||
@@ -395,8 +389,6 @@ async fn execve_permission_request_hook_short_circuits_prompt() -> anyhow::Resul
|
||||
&read_only_file_system_sandbox_policy(),
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
);
|
||||
let sandbox_policy = SandboxPolicy::new_read_only_policy();
|
||||
|
||||
let workdir = AbsolutePathBuf::try_from(std::env::current_dir()?)?;
|
||||
let target = std::env::temp_dir().join("execve-hook-short-circuit.txt");
|
||||
let target_str = target.display().to_string();
|
||||
@@ -410,9 +402,11 @@ async fn execve_permission_request_hook_short_circuits_prompt() -> anyhow::Resul
|
||||
call_id: "execve-hook-call".to_string(),
|
||||
tool_name: GuardianCommandSource::Shell,
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy,
|
||||
permission_profile: permission_profile_from_sandbox_policy(
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
),
|
||||
file_system_sandbox_policy: read_only_file_system_sandbox_policy(),
|
||||
network_sandbox_policy: NetworkSandboxPolicy::Restricted,
|
||||
sandbox_policy_cwd: workdir.clone(),
|
||||
sandbox_permissions: SandboxPermissions::RequireEscalated,
|
||||
approval_sandbox_permissions: SandboxPermissions::RequireEscalated,
|
||||
prompt_permissions: None,
|
||||
@@ -464,6 +458,7 @@ fn evaluate_intercepted_exec_policy_uses_wrapper_command_when_shell_wrapper_pars
|
||||
parser.parse("test.rules", policy_src).unwrap();
|
||||
let policy = parser.build();
|
||||
let program = AbsolutePathBuf::try_from(host_absolute_path(&["bin", "zsh"])).unwrap();
|
||||
let sandbox_cwd = test_sandbox_cwd();
|
||||
|
||||
let enable_intercepted_exec_policy_shell_wrapper_parsing = false;
|
||||
let evaluation = evaluate_intercepted_exec_policy(
|
||||
@@ -476,8 +471,11 @@ fn evaluate_intercepted_exec_policy_uses_wrapper_command_when_shell_wrapper_pars
|
||||
],
|
||||
InterceptedExecPolicyContext {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy: &SandboxPolicy::new_read_only_policy(),
|
||||
permission_profile: permission_profile_from_sandbox_policy(
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
),
|
||||
file_system_sandbox_policy: &read_only_file_system_sandbox_policy(),
|
||||
sandbox_cwd: sandbox_cwd.as_path(),
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
enable_shell_wrapper_parsing: enable_intercepted_exec_policy_shell_wrapper_parsing,
|
||||
},
|
||||
@@ -515,6 +513,7 @@ fn evaluate_intercepted_exec_policy_matches_inner_shell_commands_when_enabled()
|
||||
parser.parse("test.rules", policy_src).unwrap();
|
||||
let policy = parser.build();
|
||||
let program = AbsolutePathBuf::try_from(host_absolute_path(&["bin", "bash"])).unwrap();
|
||||
let sandbox_cwd = test_sandbox_cwd();
|
||||
|
||||
let enable_intercepted_exec_policy_shell_wrapper_parsing = true;
|
||||
let evaluation = evaluate_intercepted_exec_policy(
|
||||
@@ -527,8 +526,11 @@ fn evaluate_intercepted_exec_policy_matches_inner_shell_commands_when_enabled()
|
||||
],
|
||||
InterceptedExecPolicyContext {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy: &SandboxPolicy::new_read_only_policy(),
|
||||
permission_profile: permission_profile_from_sandbox_policy(
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
),
|
||||
file_system_sandbox_policy: &read_only_file_system_sandbox_policy(),
|
||||
sandbox_cwd: sandbox_cwd.as_path(),
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
enable_shell_wrapper_parsing: enable_intercepted_exec_policy_shell_wrapper_parsing,
|
||||
},
|
||||
@@ -562,6 +564,7 @@ host_executable(name = "git", paths = ["{git_path_literal}"])
|
||||
parser.parse("test.rules", &policy_src).unwrap();
|
||||
let policy = parser.build();
|
||||
let program = AbsolutePathBuf::try_from(git_path).unwrap();
|
||||
let sandbox_cwd = test_sandbox_cwd();
|
||||
|
||||
let evaluation = evaluate_intercepted_exec_policy(
|
||||
&policy,
|
||||
@@ -569,8 +572,11 @@ host_executable(name = "git", paths = ["{git_path_literal}"])
|
||||
&["git".to_string(), "status".to_string()],
|
||||
InterceptedExecPolicyContext {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy: &SandboxPolicy::new_read_only_policy(),
|
||||
permission_profile: permission_profile_from_sandbox_policy(
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
),
|
||||
file_system_sandbox_policy: &read_only_file_system_sandbox_policy(),
|
||||
sandbox_cwd: sandbox_cwd.as_path(),
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
enable_shell_wrapper_parsing: false,
|
||||
},
|
||||
@@ -602,6 +608,7 @@ fn intercepted_exec_policy_treats_preapproved_additional_permissions_as_default(
|
||||
let approval_policy = AskForApproval::OnRequest;
|
||||
let sandbox_policy = SandboxPolicy::new_workspace_write_policy();
|
||||
let file_system_sandbox_policy = read_only_file_system_sandbox_policy();
|
||||
let sandbox_cwd = test_sandbox_cwd();
|
||||
|
||||
let preapproved = evaluate_intercepted_exec_policy(
|
||||
&policy,
|
||||
@@ -609,8 +616,9 @@ fn intercepted_exec_policy_treats_preapproved_additional_permissions_as_default(
|
||||
&argv,
|
||||
InterceptedExecPolicyContext {
|
||||
approval_policy,
|
||||
sandbox_policy: &sandbox_policy,
|
||||
permission_profile: permission_profile_from_sandbox_policy(&sandbox_policy),
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
sandbox_cwd: sandbox_cwd.as_path(),
|
||||
sandbox_permissions: super::approval_sandbox_permissions(
|
||||
SandboxPermissions::WithAdditionalPermissions,
|
||||
/*additional_permissions_preapproved*/ true,
|
||||
@@ -624,8 +632,9 @@ fn intercepted_exec_policy_treats_preapproved_additional_permissions_as_default(
|
||||
&argv,
|
||||
InterceptedExecPolicyContext {
|
||||
approval_policy,
|
||||
sandbox_policy: &sandbox_policy,
|
||||
permission_profile: permission_profile_from_sandbox_policy(&sandbox_policy),
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
sandbox_cwd: sandbox_cwd.as_path(),
|
||||
sandbox_permissions: SandboxPermissions::WithAdditionalPermissions,
|
||||
enable_shell_wrapper_parsing: false,
|
||||
},
|
||||
@@ -650,6 +659,7 @@ host_executable(name = "git", paths = ["{allowed_git_literal}"])
|
||||
parser.parse("test.rules", &policy_src).unwrap();
|
||||
let policy = parser.build();
|
||||
let program = AbsolutePathBuf::try_from(other_git.clone()).unwrap();
|
||||
let sandbox_cwd = test_sandbox_cwd();
|
||||
|
||||
let evaluation = evaluate_intercepted_exec_policy(
|
||||
&policy,
|
||||
@@ -657,8 +667,11 @@ host_executable(name = "git", paths = ["{allowed_git_literal}"])
|
||||
&["git".to_string(), "status".to_string()],
|
||||
InterceptedExecPolicyContext {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy: &SandboxPolicy::new_read_only_policy(),
|
||||
permission_profile: permission_profile_from_sandbox_policy(
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
),
|
||||
file_system_sandbox_policy: &read_only_file_system_sandbox_policy(),
|
||||
sandbox_cwd: sandbox_cwd.as_path(),
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
enable_shell_wrapper_parsing: false,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user