From 64177aaa222738e2372cdf7f929388883b483094 Mon Sep 17 00:00:00 2001 From: jif-oai Date: Fri, 17 Apr 2026 09:33:12 +0100 Subject: [PATCH] fix: reduce writable root (#17947) --- codex-rs/core/src/memories/phase2.rs | 22 +++++++++--- codex-rs/core/src/memories/tests.rs | 54 ++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 12 deletions(-) diff --git a/codex-rs/core/src/memories/phase2.rs b/codex-rs/core/src/memories/phase2.rs index dd194edfb..ea6b8dfc4 100644 --- a/codex-rs/core/src/memories/phase2.rs +++ b/codex-rs/core/src/memories/phase2.rs @@ -16,6 +16,8 @@ use crate::memories::storage::sync_rollout_summaries_from_memories; use codex_config::Constrained; use codex_features::Feature; use codex_protocol::ThreadId; +use codex_protocol::permissions::FileSystemSandboxPolicy; +use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::SandboxPolicy; use codex_protocol::protocol::SessionSource; @@ -296,7 +298,7 @@ mod agent { let root = memory_root(&config.codex_home); let mut agent_config = config.as_ref().clone(); - agent_config.cwd = root; + agent_config.cwd = root.clone(); // Consolidation threads must never feed back into phase-1 memory generation. agent_config.memories.generate_memories = false; // Approval policy @@ -307,20 +309,30 @@ mod agent { let _ = agent_config.features.disable(Feature::MemoryTool); // Sandbox policy - let writable_roots = vec![agent_config.codex_home.clone()]; - // The consolidation agent only needs local codex_home write access and no network. + let writable_roots = vec![root]; + // The consolidation agent only needs local memory-root write access and no network. let consolidation_sandbox_policy = SandboxPolicy::WorkspaceWrite { writable_roots, read_only_access: Default::default(), network_access: false, - exclude_tmpdir_env_var: false, - exclude_slash_tmp: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, }; + let consolidation_file_system_sandbox_policy = + FileSystemSandboxPolicy::from_legacy_sandbox_policy( + &consolidation_sandbox_policy, + agent_config.cwd.as_path(), + ); + let consolidation_network_sandbox_policy = + NetworkSandboxPolicy::from(&consolidation_sandbox_policy); agent_config .permissions .sandbox_policy .set(consolidation_sandbox_policy) .ok()?; + agent_config.permissions.file_system_sandbox_policy = + consolidation_file_system_sandbox_policy; + agent_config.permissions.network_sandbox_policy = consolidation_network_sandbox_policy; agent_config.model = Some( config diff --git a/codex-rs/core/src/memories/tests.rs b/codex-rs/core/src/memories/tests.rs index ab3538af6..31155b4d3 100644 --- a/codex-rs/core/src/memories/tests.rs +++ b/codex-rs/core/src/memories/tests.rs @@ -429,6 +429,8 @@ mod phase2 { use codex_config::Constrained; use codex_login::CodexAuth; use codex_protocol::ThreadId; + use codex_protocol::permissions::FileSystemSandboxPolicy; + use codex_protocol::permissions::NetworkSandboxPolicy; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::Op; use codex_protocol::protocol::SandboxPolicy; @@ -473,6 +475,8 @@ mod phase2 { codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path(codex_home.path()) .expect("codex home is absolute"); config.cwd = config.codex_home.clone(); + config.permissions.file_system_sandbox_policy = FileSystemSandboxPolicy::unrestricted(); + config.permissions.network_sandbox_policy = NetworkSandboxPolicy::Enabled; let config = Arc::new(config); let state_db = codex_state::StateRuntime::init( @@ -683,17 +687,53 @@ mod phase2 { config_snapshot.cwd.as_path(), memory_root(&harness.config.codex_home).as_path() ); - match config_snapshot.sandbox_policy { - SandboxPolicy::WorkspaceWrite { writable_roots, .. } => { - assert!( - writable_roots - .iter() - .any(|root| root.as_path() == harness.config.codex_home.as_path()), - "consolidation subagent should have codex_home as writable root" + match &config_snapshot.sandbox_policy { + SandboxPolicy::WorkspaceWrite { + writable_roots, + network_access, + .. + } => { + assert!(!*network_access); + pretty_assertions::assert_eq!( + writable_roots.as_slice(), + [memory_root(&harness.config.codex_home)], + "consolidation subagent should only be able to write the memory root" ); } other => panic!("unexpected sandbox policy: {other:?}"), } + let turn_context = subagent.codex.session.new_default_turn().await; + pretty_assertions::assert_eq!( + turn_context.file_system_sandbox_policy, + FileSystemSandboxPolicy::from_legacy_sandbox_policy( + &config_snapshot.sandbox_policy, + config_snapshot.cwd.as_path(), + ), + "consolidation subagent split filesystem policy should match the memory-root legacy policy" + ); + assert!( + turn_context + .file_system_sandbox_policy + .can_write_path_with_cwd( + memory_root(&harness.config.codex_home).as_path(), + config_snapshot.cwd.as_path(), + ), + "consolidation subagent should be able to write the memory root" + ); + assert!( + !turn_context + .file_system_sandbox_policy + .can_write_path_with_cwd( + harness.config.codex_home.join("config.toml").as_path(), + config_snapshot.cwd.as_path(), + ), + "consolidation subagent should not inherit codex_home write access" + ); + pretty_assertions::assert_eq!( + turn_context.network_sandbox_policy, + NetworkSandboxPolicy::Restricted, + "consolidation subagent split network policy should preserve no-network sandboxing" + ); subagent.codex.session.ensure_rollout_materialized().await; subagent .codex