diff --git a/codex-rs/exec-server/src/fs_sandbox.rs b/codex-rs/exec-server/src/fs_sandbox.rs index a1c0aea914..4e58e4de96 100644 --- a/codex-rs/exec-server/src/fs_sandbox.rs +++ b/codex-rs/exec-server/src/fs_sandbox.rs @@ -504,6 +504,12 @@ impl WindowsSandboxWrapperRequestFile { request_dir.display() )) })?; + codex_windows_sandbox::ensure_current_user_cleanup_access(&request_dir).map_err(|err| { + internal_error(format!( + "failed to grant cleanup access to windows fs sandbox wrapper request dir {}: {err}", + request_dir.display() + )) + })?; let path = request_dir.join(format!( "fs-helper-wrapper-request-{}.json", uuid::Uuid::new_v4() diff --git a/codex-rs/windows-sandbox-rs/src/acl.rs b/codex-rs/windows-sandbox-rs/src/acl.rs index a368371dc4..ca4ac7d428 100644 --- a/codex-rs/windows-sandbox-rs/src/acl.rs +++ b/codex-rs/windows-sandbox-rs/src/acl.rs @@ -1,3 +1,4 @@ +use crate::winutil::resolve_sid; use crate::winutil::to_wide; use anyhow::Result; use anyhow::anyhow; @@ -416,6 +417,14 @@ pub unsafe fn ensure_allow_write_aces(path: &Path, sids: &[*mut c_void]) -> Resu ensure_allow_mask_aces(path, sids, WRITE_ALLOW_MASK) } +/// Ensure the current real user can clean up files and child directories under +/// `path`, even when the parent tree grants write access without delete rights. +pub fn ensure_current_user_cleanup_access(path: &Path) -> Result { + let real_user = std::env::var("USERNAME").unwrap_or_else(|_| "Administrators".to_string()); + let real_user_sid = resolve_sid(&real_user)?; + unsafe { ensure_allow_write_aces(path, &[real_user_sid.as_ptr() as *mut c_void]) } +} + /// Adds an allow ACE granting read/write/execute to the given SID on the target path. /// /// # Safety @@ -482,6 +491,30 @@ pub unsafe fn add_allow_ace(path: &Path, psid: *mut c_void) -> Result { Ok(added) } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn current_user_cleanup_access_grants_delete_rights() -> Result<()> { + let temp_dir = tempfile::TempDir::new()?; + let request_dir = temp_dir.path().join("wrapper-requests"); + std::fs::create_dir(&request_dir)?; + + ensure_current_user_cleanup_access(&request_dir)?; + + let real_user = std::env::var("USERNAME").unwrap_or_else(|_| "Administrators".to_string()); + let real_user_sid = resolve_sid(&real_user)?; + assert!(path_mask_allows( + &request_dir, + &[real_user_sid.as_ptr() as *mut c_void], + DELETE | FILE_DELETE_CHILD, + /*require_all_bits*/ true, + )?); + Ok(()) + } +} + /// Adds a deny ACE to prevent write/append/delete for the given SID on the target path. /// /// # Safety diff --git a/codex-rs/windows-sandbox-rs/src/lib.rs b/codex-rs/windows-sandbox-rs/src/lib.rs index 0786f27786..e3c9d3d126 100644 --- a/codex-rs/windows-sandbox-rs/src/lib.rs +++ b/codex-rs/windows-sandbox-rs/src/lib.rs @@ -136,6 +136,8 @@ pub use acl::ensure_allow_mask_aces_with_inheritance; #[cfg(target_os = "windows")] pub use acl::ensure_allow_write_aces; #[cfg(target_os = "windows")] +pub use acl::ensure_current_user_cleanup_access; +#[cfg(target_os = "windows")] pub use acl::fetch_dacl_handle; #[cfg(target_os = "windows")] pub use acl::path_mask_allows;