diff --git a/codex-rs/sandboxing/src/policy_transforms.rs b/codex-rs/sandboxing/src/policy_transforms.rs index 134ac84f15..6d30b0f6f4 100644 --- a/codex-rs/sandboxing/src/policy_transforms.rs +++ b/codex-rs/sandboxing/src/policy_transforms.rs @@ -383,9 +383,15 @@ fn materialize_cwd_dependent_entry( access: entry.access, }) .unwrap_or_else(|| entry.clone()), - FileSystemPath::Path { .. } - | FileSystemPath::GlobPattern { .. } - | FileSystemPath::Special { .. } => entry.clone(), + FileSystemPath::GlobPattern { pattern } => FileSystemSandboxEntry { + path: FileSystemPath::GlobPattern { + pattern: AbsolutePathBuf::resolve_path_against_base(pattern, cwd) + .to_string_lossy() + .into_owned(), + }, + access: entry.access, + }, + FileSystemPath::Path { .. } | FileSystemPath::Special { .. } => entry.clone(), } } diff --git a/codex-rs/sandboxing/src/policy_transforms_tests.rs b/codex-rs/sandboxing/src/policy_transforms_tests.rs index e34158bc99..a0c93d9122 100644 --- a/codex-rs/sandboxing/src/policy_transforms_tests.rs +++ b/codex-rs/sandboxing/src/policy_transforms_tests.rs @@ -509,6 +509,75 @@ fn intersect_permission_profiles_rejects_concrete_grants_matched_by_requested_de ); } +#[test] +fn intersect_permission_profiles_materializes_relative_deny_globs_for_reuse() { + let temp_dir = TempDir::new().expect("create temp dir"); + let request_cwd = AbsolutePathBuf::from_absolute_path(temp_dir.path().join("request-cwd")) + .expect("absolute request cwd"); + let later_cwd = AbsolutePathBuf::from_absolute_path(temp_dir.path().join("later-cwd")) + .expect("absolute later cwd"); + let cwd_write = FileSystemSandboxEntry { + path: FileSystemPath::Special { + value: FileSystemSpecialPath::CurrentWorkingDirectory, + }, + access: FileSystemAccessMode::Write, + }; + let deny_env_files = FileSystemSandboxEntry { + path: FileSystemPath::GlobPattern { + pattern: "**/*.env".to_string(), + }, + access: FileSystemAccessMode::None, + }; + let permissions = PermissionProfile { + file_system: Some(FileSystemPermissions { + entries: vec![cwd_write, deny_env_files], + glob_scan_max_depth: std::num::NonZeroUsize::new(2), + }), + ..Default::default() + }; + + let intersected = + intersect_permission_profiles(permissions.clone(), permissions, request_cwd.as_path()); + + assert_eq!( + intersected, + PermissionProfile { + file_system: Some(FileSystemPermissions { + entries: vec![ + FileSystemSandboxEntry { + path: FileSystemPath::Path { + path: request_cwd.clone(), + }, + access: FileSystemAccessMode::Write, + }, + FileSystemSandboxEntry { + path: FileSystemPath::GlobPattern { + pattern: request_cwd.join("**/*.env").to_string_lossy().into_owned(), + }, + access: FileSystemAccessMode::None, + }, + ], + glob_scan_max_depth: std::num::NonZeroUsize::new(2), + }), + ..Default::default() + } + ); + assert_eq!( + intersect_permission_profiles( + PermissionProfile { + file_system: Some(FileSystemPermissions::from_read_write_roots( + /*read*/ None, + Some(vec![later_cwd.join("token.env")]), + )), + ..Default::default() + }, + intersected, + later_cwd.as_path(), + ), + PermissionProfile::default() + ); +} + #[test] fn intersect_permission_profiles_drops_broader_cwd_grant_for_requested_child_path() { let temp_dir = TempDir::new().expect("create temp dir"); @@ -577,7 +646,20 @@ fn intersect_permission_profiles_uses_granted_bounded_glob_scan_depth() { intersect_permission_profiles(requested, granted, cwd.as_path()), PermissionProfile { file_system: Some(FileSystemPermissions { - entries: vec![root_write, deny_env_files], + entries: vec![ + root_write, + FileSystemSandboxEntry { + path: FileSystemPath::GlobPattern { + pattern: AbsolutePathBuf::resolve_path_against_base( + "**/*.env", + cwd.as_path() + ) + .to_string_lossy() + .into_owned(), + }, + access: FileSystemAccessMode::None, + }, + ], glob_scan_max_depth: std::num::NonZeroUsize::new(4), }), ..Default::default() @@ -619,7 +701,20 @@ fn intersect_permission_profiles_uses_granted_unbounded_glob_scan_depth() { intersect_permission_profiles(requested, granted, cwd.as_path()), PermissionProfile { file_system: Some(FileSystemPermissions { - entries: vec![root_write, deny_env_files], + entries: vec![ + root_write, + FileSystemSandboxEntry { + path: FileSystemPath::GlobPattern { + pattern: AbsolutePathBuf::resolve_path_against_base( + "**/*.env", + cwd.as_path() + ) + .to_string_lossy() + .into_owned(), + }, + access: FileSystemAccessMode::None, + }, + ], glob_scan_max_depth: None, }), ..Default::default()