diff --git a/codex-rs/app-server/src/message_processor.rs b/codex-rs/app-server/src/message_processor.rs index 55f857351a..93af870384 100644 --- a/codex-rs/app-server/src/message_processor.rs +++ b/codex-rs/app-server/src/message_processor.rs @@ -191,8 +191,15 @@ impl MessageProcessor { } // This function is stubbed out to return None on non-Windows platforms + let cwd = match std::env::current_dir() { + Ok(cwd) => cwd, + Err(_) => return, + }; if let Some((sample_paths, extra_count, failed_scan)) = - codex_windows_sandbox::world_writable_warning_details(self.config.codex_home.as_path()) + codex_windows_sandbox::world_writable_warning_details( + self.config.codex_home.as_path(), + cwd, + ) { self.outgoing .send_server_notification(ServerNotification::WindowsWorldWritableWarning( diff --git a/codex-rs/tui/src/chatwidget.rs b/codex-rs/tui/src/chatwidget.rs index 9429ce143e..2642648a08 100644 --- a/codex-rs/tui/src/chatwidget.rs +++ b/codex-rs/tui/src/chatwidget.rs @@ -2300,7 +2300,11 @@ impl ChatWidget { { return None; } - codex_windows_sandbox::world_writable_warning_details(self.config.codex_home.as_path()) + let cwd = match std::env::current_dir() { + Ok(cwd) => cwd, + Err(_) => return Some((Vec::new(), 0, true)), + }; + codex_windows_sandbox::world_writable_warning_details(self.config.codex_home.as_path(), cwd) } #[cfg(not(target_os = "windows"))] diff --git a/codex-rs/windows-sandbox-rs/src/audit.rs b/codex-rs/windows-sandbox-rs/src/audit.rs index 383c652c7c..8873bc2e91 100644 --- a/codex-rs/windows-sandbox-rs/src/audit.rs +++ b/codex-rs/windows-sandbox-rs/src/audit.rs @@ -8,35 +8,35 @@ use std::path::Path; use std::path::PathBuf; use std::time::Duration; use std::time::Instant; +use windows_sys::Win32::Foundation::CloseHandle; use windows_sys::Win32::Foundation::LocalFree; use windows_sys::Win32::Foundation::ERROR_SUCCESS; use windows_sys::Win32::Foundation::HLOCAL; +use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE; use windows_sys::Win32::Security::Authorization::GetNamedSecurityInfoW; use windows_sys::Win32::Security::Authorization::GetSecurityInfo; -use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE; -use windows_sys::Win32::Foundation::CloseHandle; use windows_sys::Win32::Storage::FileSystem::CreateFileW; +use windows_sys::Win32::Storage::FileSystem::FILE_APPEND_DATA; use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS; +use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_WRITE; use windows_sys::Win32::Storage::FileSystem::FILE_SHARE_DELETE; use windows_sys::Win32::Storage::FileSystem::FILE_SHARE_READ; use windows_sys::Win32::Storage::FileSystem::FILE_SHARE_WRITE; -use windows_sys::Win32::Storage::FileSystem::OPEN_EXISTING; -use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_WRITE; +use windows_sys::Win32::Storage::FileSystem::FILE_WRITE_ATTRIBUTES; use windows_sys::Win32::Storage::FileSystem::FILE_WRITE_DATA; -use windows_sys::Win32::Storage::FileSystem::FILE_APPEND_DATA; use windows_sys::Win32::Storage::FileSystem::FILE_WRITE_EA; -use windows_sys::Win32::Storage::FileSystem::FILE_WRITE_ATTRIBUTES; +use windows_sys::Win32::Storage::FileSystem::OPEN_EXISTING; const GENERIC_ALL_MASK: u32 = 0x1000_0000; const GENERIC_WRITE_MASK: u32 = 0x4000_0000; -use windows_sys::Win32::Security::ACL; -use windows_sys::Win32::Security::DACL_SECURITY_INFORMATION; -use windows_sys::Win32::Security::ACL_SIZE_INFORMATION; use windows_sys::Win32::Security::AclSizeInformation; -use windows_sys::Win32::Security::GetAclInformation; +use windows_sys::Win32::Security::EqualSid; use windows_sys::Win32::Security::GetAce; +use windows_sys::Win32::Security::GetAclInformation; use windows_sys::Win32::Security::ACCESS_ALLOWED_ACE; use windows_sys::Win32::Security::ACE_HEADER; -use windows_sys::Win32::Security::EqualSid; +use windows_sys::Win32::Security::ACL; +use windows_sys::Win32::Security::ACL_SIZE_INFORMATION; +use windows_sys::Win32::Security::DACL_SECURITY_INFORMATION; // Preflight scan limits const MAX_ITEMS_PER_DIR: i32 = 1000; @@ -162,7 +162,9 @@ unsafe fn path_has_world_write_allow(path: &Path) -> Result { let psid_world = world.as_mut_ptr() as *mut c_void; // Very fast mask-based check for world-writable grants (includes GENERIC_*). if !dacl_quick_world_write_mask_allows(p_dacl, psid_world) { - if !p_sd.is_null() { LocalFree(p_sd as HLOCAL); } + if !p_sd.is_null() { + LocalFree(p_sd as HLOCAL); + } return Ok(false); } // Quick detector flagged a write grant for Everyone: treat as writable. @@ -202,7 +204,9 @@ pub fn audit_everyone_writable( let has = unsafe { path_has_world_write_allow(&p)? }; if has { let key = normalize_path_key(&p); - if seen.insert(key) { flagged.push(p); } + if seen.insert(key) { + flagged.push(p); + } } } } @@ -218,7 +222,9 @@ pub fn audit_everyone_writable( let has_root = unsafe { path_has_world_write_allow(&root)? }; if has_root { let key = normalize_path_key(&root); - if seen.insert(key) { flagged.push(root.clone()); } + if seen.insert(key) { + flagged.push(root.clone()); + } } // one level down best-effort if let Ok(read) = std::fs::read_dir(&root) { @@ -240,13 +246,17 @@ pub fn audit_everyone_writable( // Skip noisy/irrelevant Windows system subdirectories let pl = p.to_string_lossy().to_ascii_lowercase(); let norm = pl.replace('\\', "/"); - if SKIP_DIR_SUFFIXES.iter().any(|s| norm.ends_with(s)) { continue; } + if SKIP_DIR_SUFFIXES.iter().any(|s| norm.ends_with(s)) { + continue; + } if ft.is_dir() { checked += 1; let has_child = unsafe { path_has_world_write_allow(&p)? }; if has_child { let key = normalize_path_key(&p); - if seen.insert(key) { flagged.push(p); } + if seen.insert(key) { + flagged.push(p); + } } } } @@ -258,20 +268,12 @@ pub fn audit_everyone_writable( for p in &flagged { list.push_str(&format!("\n - {}", p.display())); } - crate::logging::log_note( - &format!( - "AUDIT: world-writable scan FAILED; checked={checked}; duration_ms={elapsed_ms}; flagged:{}", - list - ), - logs_base_dir, - ); + return Ok(flagged); } // Log success once if nothing flagged crate::logging::log_note( - &format!( - "AUDIT: world-writable scan OK; checked={checked}; duration_ms={elapsed_ms}" - ), + &format!("AUDIT: world-writable scan OK; checked={checked}; duration_ms={elapsed_ms}"), logs_base_dir, ); Ok(Vec::new()) @@ -284,14 +286,10 @@ fn normalize_windows_path_for_display(p: impl AsRef) -> String { pub fn world_writable_warning_details( codex_home: impl AsRef, + cwd: impl AsRef, ) -> Option<(Vec, usize, bool)> { - let cwd = match std::env::current_dir() { - Ok(cwd) => cwd, - Err(_) => return Some((Vec::new(), 0, true)), - }; - let env_map: HashMap = std::env::vars().collect(); - match audit_everyone_writable(&cwd, &env_map, Some(codex_home.as_ref())) { + match audit_everyone_writable(cwd.as_ref(), &env_map, Some(codex_home.as_ref())) { Ok(paths) if paths.is_empty() => None, Ok(paths) => { let as_strings: Vec = paths @@ -329,16 +327,16 @@ unsafe fn dacl_quick_world_write_mask_allows(p_dacl: *mut ACL, psid_world: *mut continue; } let hdr = &*(p_ace as *const ACE_HEADER); - if hdr.AceType != 0 { // ACCESS_ALLOWED_ACE_TYPE + if hdr.AceType != 0 { + // ACCESS_ALLOWED_ACE_TYPE continue; } if (hdr.AceFlags & INHERIT_ONLY_ACE) != 0 { continue; } let base = p_ace as usize; - let sid_ptr = (base - + std::mem::size_of::() - + std::mem::size_of::()) as *mut c_void; // skip header + mask + let sid_ptr = + (base + std::mem::size_of::() + std::mem::size_of::()) as *mut c_void; // skip header + mask if EqualSid(sid_ptr, psid_world) != 0 { let ace = &*(p_ace as *const ACCESS_ALLOWED_ACE); let mask = ace.Mask; diff --git a/codex-rs/windows-sandbox-rs/src/lib.rs b/codex-rs/windows-sandbox-rs/src/lib.rs index 9d5e9aeae2..a2b4b6a538 100644 --- a/codex-rs/windows-sandbox-rs/src/lib.rs +++ b/codex-rs/windows-sandbox-rs/src/lib.rs @@ -467,6 +467,7 @@ mod stub { pub fn world_writable_warning_details( _codex_home: impl AsRef, + _cwd: impl AsRef, ) -> Option<(Vec, usize, bool)> { None }