Skip to content

Commit 2fde03b

Browse files
stop over-reporting world-writable directories (#6936)
Fix world-writable audit false positives by expanding generic permissions with MapGenericMask and then checking only concrete write bits. The earlier check looked for FILE_GENERIC_WRITE/generic masks directly, which shares bits with read permissions and could flag an Everyone read ACE as writable.
1 parent 056c8f8 commit 2fde03b

File tree

1 file changed

+32
-26
lines changed

1 file changed

+32
-26
lines changed

codex-rs/windows-sandbox-rs/src/audit.rs

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,29 @@ use std::path::PathBuf;
99
use std::time::Duration;
1010
use std::time::Instant;
1111
use windows_sys::Win32::Foundation::CloseHandle;
12-
use windows_sys::Win32::Foundation::LocalFree;
1312
use windows_sys::Win32::Foundation::ERROR_SUCCESS;
1413
use windows_sys::Win32::Foundation::HLOCAL;
1514
use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
15+
use windows_sys::Win32::Foundation::LocalFree;
16+
use windows_sys::Win32::Security::ACCESS_ALLOWED_ACE;
17+
use windows_sys::Win32::Security::ACE_HEADER;
18+
use windows_sys::Win32::Security::ACL;
19+
use windows_sys::Win32::Security::ACL_SIZE_INFORMATION;
20+
use windows_sys::Win32::Security::AclSizeInformation;
1621
use windows_sys::Win32::Security::Authorization::GetNamedSecurityInfoW;
1722
use windows_sys::Win32::Security::Authorization::GetSecurityInfo;
23+
use windows_sys::Win32::Security::DACL_SECURITY_INFORMATION;
24+
use windows_sys::Win32::Security::EqualSid;
25+
use windows_sys::Win32::Security::GetAce;
26+
use windows_sys::Win32::Security::GetAclInformation;
27+
use windows_sys::Win32::Security::MapGenericMask;
28+
use windows_sys::Win32::Security::GENERIC_MAPPING;
1829
use windows_sys::Win32::Storage::FileSystem::CreateFileW;
30+
use windows_sys::Win32::Storage::FileSystem::FILE_ALL_ACCESS;
1931
use windows_sys::Win32::Storage::FileSystem::FILE_APPEND_DATA;
2032
use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_BACKUP_SEMANTICS;
33+
use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_EXECUTE;
34+
use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_READ;
2135
use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_WRITE;
2236
use windows_sys::Win32::Storage::FileSystem::FILE_SHARE_DELETE;
2337
use windows_sys::Win32::Storage::FileSystem::FILE_SHARE_READ;
@@ -26,17 +40,6 @@ use windows_sys::Win32::Storage::FileSystem::FILE_WRITE_ATTRIBUTES;
2640
use windows_sys::Win32::Storage::FileSystem::FILE_WRITE_DATA;
2741
use windows_sys::Win32::Storage::FileSystem::FILE_WRITE_EA;
2842
use windows_sys::Win32::Storage::FileSystem::OPEN_EXISTING;
29-
const GENERIC_ALL_MASK: u32 = 0x1000_0000;
30-
const GENERIC_WRITE_MASK: u32 = 0x4000_0000;
31-
use windows_sys::Win32::Security::AclSizeInformation;
32-
use windows_sys::Win32::Security::EqualSid;
33-
use windows_sys::Win32::Security::GetAce;
34-
use windows_sys::Win32::Security::GetAclInformation;
35-
use windows_sys::Win32::Security::ACCESS_ALLOWED_ACE;
36-
use windows_sys::Win32::Security::ACE_HEADER;
37-
use windows_sys::Win32::Security::ACL;
38-
use windows_sys::Win32::Security::ACL_SIZE_INFORMATION;
39-
use windows_sys::Win32::Security::DACL_SECURITY_INFORMATION;
4043

4144
// Preflight scan limits
4245
const MAX_ITEMS_PER_DIR: i32 = 1000;
@@ -304,7 +307,7 @@ pub fn world_writable_warning_details(
304307
}
305308
}
306309
// Fast mask-based check: does the DACL contain any ACCESS_ALLOWED ACE for
307-
// Everyone that includes generic or specific write bits? Skips inherit-only
310+
// Everyone that grants write after generic bits are expanded? Skips inherit-only
308311
// ACEs (do not apply to the current object).
309312
unsafe fn dacl_quick_world_write_mask_allows(p_dacl: *mut ACL, psid_world: *mut c_void) -> bool {
310313
if p_dacl.is_null() {
@@ -321,6 +324,12 @@ unsafe fn dacl_quick_world_write_mask_allows(p_dacl: *mut ACL, psid_world: *mut
321324
if ok == 0 {
322325
return false;
323326
}
327+
let mapping = GENERIC_MAPPING {
328+
GenericRead: FILE_GENERIC_READ,
329+
GenericWrite: FILE_GENERIC_WRITE,
330+
GenericExecute: FILE_GENERIC_EXECUTE,
331+
GenericAll: FILE_ALL_ACCESS,
332+
};
324333
for i in 0..(info.AceCount as usize) {
325334
let mut p_ace: *mut c_void = std::ptr::null_mut();
326335
if GetAce(p_dacl as *const ACL, i as u32, &mut p_ace) == 0 {
@@ -337,19 +346,16 @@ unsafe fn dacl_quick_world_write_mask_allows(p_dacl: *mut ACL, psid_world: *mut
337346
let base = p_ace as usize;
338347
let sid_ptr =
339348
(base + std::mem::size_of::<ACE_HEADER>() + std::mem::size_of::<u32>()) as *mut c_void; // skip header + mask
340-
if EqualSid(sid_ptr, psid_world) != 0 {
341-
let ace = &*(p_ace as *const ACCESS_ALLOWED_ACE);
342-
let mask = ace.Mask;
343-
let writey = FILE_GENERIC_WRITE
344-
| FILE_WRITE_DATA
345-
| FILE_APPEND_DATA
346-
| FILE_WRITE_EA
347-
| FILE_WRITE_ATTRIBUTES
348-
| GENERIC_WRITE_MASK
349-
| GENERIC_ALL_MASK;
350-
if (mask & writey) != 0 {
351-
return true;
352-
}
349+
if EqualSid(sid_ptr, psid_world) == 0 {
350+
continue;
351+
}
352+
let ace = &*(p_ace as *const ACCESS_ALLOWED_ACE);
353+
let mut mask = ace.Mask;
354+
// Expand generic bits to concrete file rights before checking for write.
355+
MapGenericMask(&mut mask, &mapping);
356+
let write_mask = FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES;
357+
if (mask & write_mask) != 0 {
358+
return true;
353359
}
354360
}
355361
false

0 commit comments

Comments
 (0)