diff --git a/codex-rs/app-server/src/request_processors/thread_summary.rs b/codex-rs/app-server/src/request_processors/thread_summary.rs index f467074828..4303ba537c 100644 --- a/codex-rs/app-server/src/request_processors/thread_summary.rs +++ b/codex-rs/app-server/src/request_processors/thread_summary.rs @@ -179,8 +179,11 @@ pub(crate) fn thread_response_sandbox_policy( permission_profile: &codex_protocol::models::PermissionProfile, cwd: &Path, ) -> codex_app_server_protocol::SandboxPolicy { + let file_system_policy = permission_profile.file_system_sandbox_policy(); let sandbox_policy = codex_sandboxing::compatibility_sandbox_policy_for_permission_profile( permission_profile, + &file_system_policy, + permission_profile.network_sandbox_policy(), cwd, ); sandbox_policy.into() diff --git a/codex-rs/core/src/codex_thread.rs b/codex-rs/core/src/codex_thread.rs index b3f9cb0e2e..5b4e92380e 100644 --- a/codex-rs/core/src/codex_thread.rs +++ b/codex-rs/core/src/codex_thread.rs @@ -115,8 +115,11 @@ impl TryStartTurnIfIdleError { impl ThreadConfigSnapshot { pub fn sandbox_policy(&self) -> SandboxPolicy { + let file_system_sandbox_policy = self.permission_profile.file_system_sandbox_policy(); codex_sandboxing::compatibility_sandbox_policy_for_permission_profile( &self.permission_profile, + &file_system_sandbox_policy, + self.permission_profile.network_sandbox_policy(), self.cwd.as_path(), ) } diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index fd74fcf2d2..1f06eb8668 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -449,7 +449,13 @@ impl Permissions { /// Legacy compatibility projection derived from the canonical profile. pub fn legacy_sandbox_policy(&self, cwd: &Path) -> SandboxPolicy { let permission_profile = self.materialized_permission_profile(); - compatibility_sandbox_policy_for_permission_profile(&permission_profile, cwd) + let file_system_sandbox_policy = permission_profile.file_system_sandbox_policy(); + compatibility_sandbox_policy_for_permission_profile( + &permission_profile, + &file_system_sandbox_policy, + permission_profile.network_sandbox_policy(), + cwd, + ) } /// Check whether a legacy sandbox policy can be applied to this permission diff --git a/codex-rs/core/src/exec.rs b/codex-rs/core/src/exec.rs index 8e87528df0..e050552246 100644 --- a/codex-rs/core/src/exec.rs +++ b/codex-rs/core/src/exec.rs @@ -31,18 +31,19 @@ use codex_protocol::error::SandboxErr; use codex_protocol::exec_output::ExecToolCallOutput; use codex_protocol::exec_output::StreamOutput; use codex_protocol::models::PermissionProfile; +use codex_protocol::permissions::FileSystemSandboxKind; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::Event; use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::ExecCommandOutputDeltaEvent; use codex_protocol::protocol::ExecOutputStream; +use codex_protocol::protocol::SandboxPolicy; use codex_sandboxing::SandboxCommand; use codex_sandboxing::SandboxManager; use codex_sandboxing::SandboxTransformRequest; use codex_sandboxing::SandboxType; use codex_sandboxing::SandboxablePreference; -use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_pty::DEFAULT_OUTPUT_BYTES_CAP; use codex_utils_pty::process_group::kill_child_process_group; @@ -418,17 +419,22 @@ pub fn build_exec_request( exec_req.windows_sandbox_level, exec_req.network.is_some(), ); + let sandbox_policy = exec_req.compatibility_sandbox_policy(); exec_req.windows_sandbox_filesystem_overrides = if use_windows_elevated_backend { resolve_windows_elevated_filesystem_overrides( exec_req.sandbox, - &exec_req.permission_profile, + &sandbox_policy, + &exec_req.file_system_sandbox_policy, + exec_req.network_sandbox_policy, sandbox_cwd, use_windows_elevated_backend, ) } else { resolve_windows_restricted_token_filesystem_overrides( exec_req.sandbox, - &exec_req.permission_profile, + &sandbox_policy, + &exec_req.file_system_sandbox_policy, + exec_req.network_sandbox_policy, sandbox_cwd, exec_req.windows_sandbox_level, ) @@ -998,28 +1004,34 @@ async fn exec( } #[cfg_attr(not(target_os = "windows"), allow(dead_code))] -fn permission_profile_supports_windows_restricted_token_sandbox( - permission_profile: &PermissionProfile, +fn should_use_windows_restricted_token_sandbox( + sandbox: SandboxType, + sandbox_policy: &SandboxPolicy, + file_system_sandbox_policy: &FileSystemSandboxPolicy, ) -> bool { - match permission_profile { - PermissionProfile::Managed { file_system, .. } => { - !file_system.to_sandbox_policy().has_full_disk_write_access() - } - PermissionProfile::Disabled | PermissionProfile::External { .. } => false, - } + sandbox == SandboxType::WindowsRestrictedToken + && file_system_sandbox_policy.kind == FileSystemSandboxKind::Restricted + && !matches!( + sandbox_policy, + SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } + ) } #[cfg_attr(not(test), allow(dead_code))] pub(crate) fn unsupported_windows_restricted_token_sandbox_reason( sandbox: SandboxType, - permission_profile: &PermissionProfile, + sandbox_policy: &SandboxPolicy, + file_system_sandbox_policy: &FileSystemSandboxPolicy, + network_sandbox_policy: NetworkSandboxPolicy, sandbox_policy_cwd: &AbsolutePathBuf, windows_sandbox_level: WindowsSandboxLevel, ) -> Option { if windows_sandbox_level == WindowsSandboxLevel::Elevated { resolve_windows_elevated_filesystem_overrides( sandbox, - permission_profile, + sandbox_policy, + file_system_sandbox_policy, + network_sandbox_policy, sandbox_policy_cwd, windows_sandbox_level == WindowsSandboxLevel::Elevated, ) @@ -1027,7 +1039,9 @@ pub(crate) fn unsupported_windows_restricted_token_sandbox_reason( } else { resolve_windows_restricted_token_filesystem_overrides( sandbox, - permission_profile, + sandbox_policy, + file_system_sandbox_policy, + network_sandbox_policy, sandbox_policy_cwd, windows_sandbox_level, ) @@ -1037,7 +1051,9 @@ pub(crate) fn unsupported_windows_restricted_token_sandbox_reason( pub(crate) fn resolve_windows_restricted_token_filesystem_overrides( sandbox: SandboxType, - permission_profile: &PermissionProfile, + sandbox_policy: &SandboxPolicy, + file_system_sandbox_policy: &FileSystemSandboxPolicy, + network_sandbox_policy: NetworkSandboxPolicy, sandbox_policy_cwd: &AbsolutePathBuf, windows_sandbox_level: WindowsSandboxLevel, ) -> std::result::Result, String> { @@ -1047,22 +1063,25 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides( return Ok(None); } - let (file_system_sandbox_policy, network_sandbox_policy) = - permission_profile.to_runtime_permissions(); - let needs_direct_runtime_enforcement = file_system_sandbox_policy .needs_direct_runtime_enforcement(network_sandbox_policy, sandbox_policy_cwd); - if permission_profile_supports_windows_restricted_token_sandbox(permission_profile) - && !needs_direct_runtime_enforcement + if should_use_windows_restricted_token_sandbox( + sandbox, + sandbox_policy, + file_system_sandbox_policy, + ) && !needs_direct_runtime_enforcement { return Ok(None); } - if !permission_profile_supports_windows_restricted_token_sandbox(permission_profile) { - let permission_profile_name = permission_profile_display_name(permission_profile); + if !should_use_windows_restricted_token_sandbox( + sandbox, + sandbox_policy, + file_system_sandbox_policy, + ) { return Err(format!( - "windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, permission_profile={permission_profile_name}; refusing to run unsandboxed", + "windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, legacy_policy={sandbox_policy:?}; refusing to run unsandboxed", file_system_sandbox_policy.kind, )); } @@ -1071,7 +1090,7 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides( // but its WRITE_RESTRICTED token does not make capability SID deny-read ACEs // participate in read access checks. Read restrictions therefore require the // elevated backend, even when the filesystem root remains readable. - if !windows_policy_has_root_read_access(&file_system_sandbox_policy, sandbox_policy_cwd) { + if !windows_policy_has_root_read_access(file_system_sandbox_policy, sandbox_policy_cwd) { return Err( "windows unelevated restricted-token sandbox cannot enforce split filesystem read restrictions directly; refusing to run unsandboxed" .to_string(), @@ -1079,7 +1098,7 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides( } let additional_deny_read_paths = codex_windows_sandbox::resolve_windows_deny_read_paths( - &file_system_sandbox_policy, + file_system_sandbox_policy, sandbox_policy_cwd, )?; if !additional_deny_read_paths.is_empty() { @@ -1089,11 +1108,7 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides( ); } - let legacy_projection = compatibility_sandbox_policy_for_permission_profile( - permission_profile, - sandbox_policy_cwd.as_path(), - ); - let legacy_writable_roots = legacy_projection.get_writable_roots_with_cwd(sandbox_policy_cwd); + let legacy_writable_roots = sandbox_policy.get_writable_roots_with_cwd(sandbox_policy_cwd); let split_writable_roots = file_system_sandbox_policy.get_writable_roots_with_cwd(sandbox_policy_cwd); let legacy_root_paths: BTreeSet = legacy_writable_roots @@ -1189,7 +1204,9 @@ fn windows_policy_has_root_read_access( pub(crate) fn resolve_windows_elevated_filesystem_overrides( sandbox: SandboxType, - permission_profile: &PermissionProfile, + sandbox_policy: &SandboxPolicy, + file_system_sandbox_policy: &FileSystemSandboxPolicy, + network_sandbox_policy: NetworkSandboxPolicy, sandbox_policy_cwd: &AbsolutePathBuf, use_windows_elevated_backend: bool, ) -> std::result::Result, String> { @@ -1197,19 +1214,19 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides( return Ok(None); } - let (file_system_sandbox_policy, network_sandbox_policy) = - permission_profile.to_runtime_permissions(); - - if !permission_profile_supports_windows_restricted_token_sandbox(permission_profile) { - let permission_profile_name = permission_profile_display_name(permission_profile); + if !should_use_windows_restricted_token_sandbox( + sandbox, + sandbox_policy, + file_system_sandbox_policy, + ) { return Err(format!( - "windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, permission_profile={permission_profile_name}; refusing to run unsandboxed", + "windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, legacy_policy={sandbox_policy:?}; refusing to run unsandboxed", file_system_sandbox_policy.kind, )); } let additional_deny_read_paths = codex_windows_sandbox::resolve_windows_deny_read_paths( - &file_system_sandbox_policy, + file_system_sandbox_policy, sandbox_policy_cwd, )?; @@ -1225,11 +1242,7 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides( let needs_direct_runtime_enforcement = file_system_sandbox_policy .needs_direct_runtime_enforcement(network_sandbox_policy, sandbox_policy_cwd); let normalize_path = |path: PathBuf| dunce::canonicalize(&path).unwrap_or(path); - let legacy_projection = compatibility_sandbox_policy_for_permission_profile( - permission_profile, - sandbox_policy_cwd.as_path(), - ); - let legacy_writable_roots = legacy_projection.get_writable_roots_with_cwd(sandbox_policy_cwd); + let legacy_writable_roots = sandbox_policy.get_writable_roots_with_cwd(sandbox_policy_cwd); let legacy_root_paths: BTreeSet = legacy_writable_roots .iter() .map(|root| normalize_path(root.root.to_path_buf())) @@ -1251,7 +1264,7 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides( // whether the baseline still reads from the filesystem root and only needs // additional deny ACLs layered on top. let split_has_root_read_access = - windows_policy_has_root_read_access(&file_system_sandbox_policy, sandbox_policy_cwd); + windows_policy_has_root_read_access(file_system_sandbox_policy, sandbox_policy_cwd); let read_roots_override = if split_has_root_read_access { None } else { @@ -1316,14 +1329,6 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides( })) } -fn permission_profile_display_name(permission_profile: &PermissionProfile) -> &'static str { - match permission_profile { - PermissionProfile::Managed { .. } => "Managed", - PermissionProfile::Disabled => "Disabled", - PermissionProfile::External { .. } => "External", - } -} - fn has_reopened_writable_descendant( writable_roots: &[codex_protocol::protocol::WritableRoot], ) -> bool { diff --git a/codex-rs/core/src/exec_tests.rs b/codex-rs/core/src/exec_tests.rs index 84dfaba5d7..1c71d02651 100644 --- a/codex-rs/core/src/exec_tests.rs +++ b/codex-rs/core/src/exec_tests.rs @@ -379,18 +379,34 @@ async fn process_exec_tool_call_preserves_full_buffer_capture_policy() -> Result #[test] fn windows_restricted_token_skips_external_sandbox_policies() { - let permission_profile = PermissionProfile::External { - network: NetworkSandboxPolicy::Restricted, + let policy = SandboxPolicy::ExternalSandbox { + network_access: codex_protocol::protocol::NetworkAccess::Restricted, }; + let file_system_policy = FileSystemSandboxPolicy::from(&policy); - assert!(!permission_profile_supports_windows_restricted_token_sandbox(&permission_profile)); + assert_eq!( + should_use_windows_restricted_token_sandbox( + SandboxType::WindowsRestrictedToken, + &policy, + &file_system_policy, + ), + false + ); } #[test] -fn windows_restricted_token_supports_read_only_profiles() { - let permission_profile = PermissionProfile::read_only(); +fn windows_restricted_token_runs_for_legacy_restricted_policies() { + let policy = SandboxPolicy::new_read_only_policy(); + let file_system_policy = FileSystemSandboxPolicy::from(&policy); - assert!(permission_profile_supports_windows_restricted_token_sandbox(&permission_profile)); + assert_eq!( + should_use_windows_restricted_token_sandbox( + SandboxType::WindowsRestrictedToken, + &policy, + &file_system_policy, + ), + true + ); } #[test] @@ -411,64 +427,39 @@ fn windows_proxy_enforcement_uses_elevated_backend() { #[test] fn windows_restricted_token_rejects_network_only_restrictions() { - let permission_profile = PermissionProfile::from_runtime_permissions( - &FileSystemSandboxPolicy::unrestricted(), - NetworkSandboxPolicy::Restricted, - ); + let policy = SandboxPolicy::ExternalSandbox { + network_access: codex_protocol::protocol::NetworkAccess::Restricted, + }; + let file_system_policy = FileSystemSandboxPolicy::unrestricted(); let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd"); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &sandbox_policy_cwd, WindowsSandboxLevel::RestrictedToken, ), Some( - "windows sandbox backend cannot enforce file_system=Unrestricted, network=Restricted, permission_profile=Managed; refusing to run unsandboxed".to_string() + "windows sandbox backend cannot enforce file_system=Unrestricted, network=Restricted, legacy_policy=ExternalSandbox { network_access: Restricted }; refusing to run unsandboxed".to_string() ) ); } #[test] -fn windows_restricted_token_rejects_managed_root_write_profiles() { - let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ - codex_protocol::permissions::FileSystemSandboxEntry { - path: codex_protocol::permissions::FileSystemPath::Special { - value: codex_protocol::permissions::FileSystemSpecialPath::Root, - }, - access: codex_protocol::permissions::FileSystemAccessMode::Write, - }, - ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); +fn windows_restricted_token_allows_legacy_restricted_policies() { + let policy = SandboxPolicy::new_read_only_policy(); + let file_system_policy = FileSystemSandboxPolicy::from(&policy); let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd"); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &permission_profile, - &sandbox_policy_cwd, - WindowsSandboxLevel::RestrictedToken, - ), - Some( - "windows sandbox backend cannot enforce file_system=Restricted, network=Restricted, permission_profile=Managed; refusing to run unsandboxed" - .to_string() - ) - ); -} - -#[test] -fn windows_restricted_token_allows_read_only_profiles() { - let permission_profile = PermissionProfile::read_only(); - let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd"); - - assert_eq!( - unsupported_windows_restricted_token_sandbox_reason( - SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &sandbox_policy_cwd, WindowsSandboxLevel::RestrictedToken, ), @@ -477,19 +468,22 @@ fn windows_restricted_token_allows_read_only_profiles() { } #[test] -fn windows_restricted_token_allows_workspace_write_profiles() { - let permission_profile = PermissionProfile::workspace_write_with( - &[], - NetworkSandboxPolicy::Restricted, - /*exclude_tmpdir_env_var*/ true, - /*exclude_slash_tmp*/ true, - ); +fn windows_restricted_token_allows_legacy_workspace_write_policies() { + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; + let file_system_policy = FileSystemSandboxPolicy::from(&policy); let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd"); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &sandbox_policy_cwd, WindowsSandboxLevel::RestrictedToken, ), @@ -505,21 +499,22 @@ fn windows_elevated_allows_split_restricted_read_policies() { ) .expect("absolute docs"); std::fs::create_dir_all(docs.as_path()).expect("create docs"); + let policy = SandboxPolicy::ReadOnly { + network_access: false, + }; let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ codex_protocol::permissions::FileSystemSandboxEntry { path: codex_protocol::permissions::FileSystemPath::Path { path: docs }, access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), WindowsSandboxLevel::Elevated, ), @@ -532,6 +527,12 @@ fn windows_restricted_token_rejects_split_only_filesystem_policies() { let temp_dir = tempfile::TempDir::new().expect("tempdir"); let docs = temp_dir.path().join("docs"); std::fs::create_dir_all(&docs).expect("create docs"); + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ codex_protocol::permissions::FileSystemSandboxEntry { path: codex_protocol::permissions::FileSystemPath::Special { @@ -549,15 +550,13 @@ fn windows_restricted_token_rejects_split_only_filesystem_policies() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), WindowsSandboxLevel::RestrictedToken, ), @@ -573,6 +572,12 @@ fn windows_restricted_token_rejects_root_write_read_only_carveouts() { let temp_dir = tempfile::TempDir::new().expect("tempdir"); let docs = temp_dir.path().join("docs"); std::fs::create_dir_all(&docs).expect("create docs"); + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ codex_protocol::permissions::FileSystemSandboxEntry { path: codex_protocol::permissions::FileSystemPath::Special { @@ -588,15 +593,13 @@ fn windows_restricted_token_rejects_root_write_read_only_carveouts() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), WindowsSandboxLevel::RestrictedToken, ), @@ -615,6 +618,12 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() { .abs(); let docs = cwd.join("docs"); std::fs::create_dir_all(docs.as_path()).expect("create docs"); + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ codex_protocol::permissions::FileSystemSandboxEntry { path: codex_protocol::permissions::FileSystemPath::Special { @@ -635,20 +644,17 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); - // The workspace-write compatibility projection already protects top-level - // `.codex`, so the restricted-token overlay only needs the extra read-only - // docs carveout. + // The legacy workspace-write root already protects top-level `.codex`, so + // the restricted-token overlay only needs the extra read-only docs carveout. let expected_deny_write_paths = vec![docs]; assert_eq!( resolve_windows_restricted_token_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &cwd, WindowsSandboxLevel::RestrictedToken, ), @@ -670,6 +676,12 @@ fn windows_restricted_token_rejects_unreadable_split_carveouts() { .abs(); let blocked = cwd.join("blocked"); std::fs::create_dir_all(blocked.as_path()).expect("create blocked"); + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ codex_protocol::permissions::FileSystemSandboxEntry { path: codex_protocol::permissions::FileSystemPath::Special { @@ -690,15 +702,13 @@ fn windows_restricted_token_rejects_unreadable_split_carveouts() { access: codex_protocol::permissions::FileSystemAccessMode::Deny, }, ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); assert_eq!( resolve_windows_restricted_token_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &cwd, WindowsSandboxLevel::RestrictedToken, ), @@ -715,6 +725,9 @@ fn windows_elevated_supports_split_restricted_read_roots() { let docs = temp_dir.path().join("docs"); std::fs::create_dir_all(&docs).expect("create docs"); let expected_docs = dunce::canonicalize(&docs).expect("canonical docs"); + let policy = SandboxPolicy::ReadOnly { + network_access: false, + }; let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ codex_protocol::permissions::FileSystemSandboxEntry { path: codex_protocol::permissions::FileSystemPath::Path { @@ -724,15 +737,13 @@ fn windows_elevated_supports_split_restricted_read_roots() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); assert_eq!( resolve_windows_elevated_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), /*use_windows_elevated_backend*/ true, ), @@ -752,6 +763,12 @@ fn windows_elevated_supports_split_write_read_carveouts() { let docs = temp_dir.path().join("docs"); std::fs::create_dir_all(&docs).expect("create docs"); let expected_docs = dunce::canonicalize(&docs).expect("canonical docs"); + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ codex_protocol::permissions::FileSystemSandboxEntry { path: codex_protocol::permissions::FileSystemPath::Special { @@ -775,15 +792,13 @@ fn windows_elevated_supports_split_write_read_carveouts() { access: codex_protocol::permissions::FileSystemAccessMode::Read, }, ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); assert_eq!( resolve_windows_elevated_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), /*use_windows_elevated_backend*/ true, ), @@ -806,6 +821,12 @@ fn windows_elevated_supports_unreadable_split_carveouts() { let blocked = temp_dir.path().join("blocked"); std::fs::create_dir_all(&blocked).expect("create blocked"); let expected_blocked = dunce::canonicalize(&blocked).expect("canonical blocked"); + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ codex_protocol::permissions::FileSystemSandboxEntry { path: codex_protocol::permissions::FileSystemPath::Special { @@ -829,15 +850,13 @@ fn windows_elevated_supports_unreadable_split_carveouts() { access: codex_protocol::permissions::FileSystemAccessMode::Deny, }, ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); assert_eq!( resolve_windows_elevated_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), /*use_windows_elevated_backend*/ true, ), @@ -865,6 +884,12 @@ fn windows_elevated_supports_unreadable_globs() { let secret = temp_dir.path().join("app").join(".env"); std::fs::create_dir_all(secret.parent().expect("parent")).expect("create parent"); std::fs::write(&secret, "secret").expect("write secret"); + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ codex_protocol::permissions::FileSystemSandboxEntry { path: codex_protocol::permissions::FileSystemPath::Special { @@ -887,15 +912,13 @@ fn windows_elevated_supports_unreadable_globs() { access: codex_protocol::permissions::FileSystemAccessMode::Deny, }, ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); assert_eq!( resolve_windows_elevated_filesystem_overrides( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), /*use_windows_elevated_backend*/ true, ), @@ -918,6 +941,12 @@ fn windows_elevated_rejects_reopened_writable_descendants() { let docs = temp_dir.path().join("docs"); let nested = docs.join("nested"); std::fs::create_dir_all(&nested).expect("create nested"); + let policy = SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }; let file_system_policy = FileSystemSandboxPolicy::restricted(vec![ codex_protocol::permissions::FileSystemSandboxEntry { path: codex_protocol::permissions::FileSystemPath::Special { @@ -948,15 +977,13 @@ fn windows_elevated_rejects_reopened_writable_descendants() { access: codex_protocol::permissions::FileSystemAccessMode::Write, }, ]); - let permission_profile = PermissionProfile::from_runtime_permissions( - &file_system_policy, - NetworkSandboxPolicy::Restricted, - ); assert_eq!( unsupported_windows_restricted_token_sandbox_reason( SandboxType::WindowsRestrictedToken, - &permission_profile, + &policy, + &file_system_policy, + NetworkSandboxPolicy::Restricted, &temp_dir.path().abs(), WindowsSandboxLevel::Elevated, ), diff --git a/codex-rs/core/src/sandboxing/mod.rs b/codex-rs/core/src/sandboxing/mod.rs index 8a2a2849fd..f853ea3ba6 100644 --- a/codex-rs/core/src/sandboxing/mod.rs +++ b/codex-rs/core/src/sandboxing/mod.rs @@ -22,8 +22,10 @@ use codex_protocol::models::PermissionProfile; pub use codex_protocol::models::SandboxPermissions; use codex_protocol::permissions::FileSystemSandboxPolicy; use codex_protocol::permissions::NetworkSandboxPolicy; +use codex_protocol::protocol::SandboxPolicy; use codex_sandboxing::SandboxExecRequest; use codex_sandboxing::SandboxType; +use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile; use codex_utils_absolute_path::AbsolutePathBuf; use std::collections::HashMap; @@ -100,6 +102,15 @@ impl ExecRequest { } } + pub(crate) fn compatibility_sandbox_policy(&self) -> SandboxPolicy { + compatibility_sandbox_policy_for_permission_profile( + &self.permission_profile, + &self.file_system_sandbox_policy, + self.network_sandbox_policy, + self.windows_sandbox_policy_cwd.as_path(), + ) + } + pub(crate) fn from_sandbox_exec_request( request: SandboxExecRequest, options: ExecOptions, diff --git a/codex-rs/core/src/session/session.rs b/codex-rs/core/src/session/session.rs index 122ff6aeb9..6c7c20b314 100644 --- a/codex-rs/core/src/session/session.rs +++ b/codex-rs/core/src/session/session.rs @@ -150,11 +150,17 @@ impl SessionConfiguration { } pub(super) fn sandbox_policy(&self) -> SandboxPolicy { - let permission_profile = self.permission_profile(); - codex_sandboxing::compatibility_sandbox_policy_for_permission_profile( - &permission_profile, - &self.cwd, - ) + self.permission_profile() + .to_legacy_sandbox_policy(&self.cwd) + .unwrap_or_else(|_| { + let file_system_sandbox_policy = self.file_system_sandbox_policy(); + codex_sandboxing::compatibility_sandbox_policy_for_permission_profile( + self.permission_profile_state.permission_profile(), + &file_system_sandbox_policy, + self.network_sandbox_policy(), + &self.cwd, + ) + }) } pub(super) fn file_system_sandbox_policy(&self) -> FileSystemSandboxPolicy { diff --git a/codex-rs/core/src/session/turn_context.rs b/codex-rs/core/src/session/turn_context.rs index 5f52912d59..ba70cfa35d 100644 --- a/codex-rs/core/src/session/turn_context.rs +++ b/codex-rs/core/src/session/turn_context.rs @@ -125,8 +125,12 @@ impl TurnContext { } pub(crate) fn sandbox_policy(&self) -> SandboxPolicy { + let file_system_sandbox_policy = self.file_system_sandbox_policy(); + let network_sandbox_policy = self.network_sandbox_policy(); compatibility_sandbox_policy_for_permission_profile( &self.permission_profile, + &file_system_sandbox_policy, + network_sandbox_policy, #[allow(deprecated)] &self.cwd, ) diff --git a/codex-rs/core/src/spawn.rs b/codex-rs/core/src/spawn.rs index a23a1d749e..a2c4ebe597 100644 --- a/codex-rs/core/src/spawn.rs +++ b/codex-rs/core/src/spawn.rs @@ -30,7 +30,7 @@ pub enum StdioPolicy { Inherit, } -/// Spawns the appropriate child process for the exec params and sandbox settings, +/// Spawns the appropriate child process for the ExecParams and SandboxPolicy, /// ensuring the args and environment variables used to create the `Command` /// (and `Child`) honor the configuration. /// diff --git a/codex-rs/sandboxing/src/manager.rs b/codex-rs/sandboxing/src/manager.rs index 39ef358f8c..bd8f6d9b98 100644 --- a/codex-rs/sandboxing/src/manager.rs +++ b/codex-rs/sandboxing/src/manager.rs @@ -290,18 +290,19 @@ impl SandboxManager { pub fn compatibility_sandbox_policy_for_permission_profile( permissions: &PermissionProfile, + file_system_policy: &FileSystemSandboxPolicy, + network_policy: NetworkSandboxPolicy, cwd: &Path, ) -> SandboxPolicy { permissions .to_legacy_sandbox_policy(cwd) .unwrap_or_else(|_| { - let (file_system_policy, network_policy) = permissions.to_runtime_permissions(); compatibility_workspace_write_policy(file_system_policy, network_policy, cwd) }) } fn compatibility_workspace_write_policy( - file_system_policy: FileSystemSandboxPolicy, + file_system_policy: &FileSystemSandboxPolicy, network_policy: NetworkSandboxPolicy, cwd: &Path, ) -> SandboxPolicy {