fix(windows-sandbox): allow deletion in writable roots (#31138)

## Why

The legacy unelevated Windows sandbox allowed tools to create and update
files in workspace-write roots, but it could not delete files that
already existed there. This breaks operations such as `apply_patch` file
deletion and replacement in the workspace, `TEMP`, and `TMP`.

The delete grant must also preserve deny-write carveouts. Granting
`FILE_DELETE_CHILD` on a writable parent would let the sandbox remove
protected children such as `.git` or an explicit read-only subpath even
when those children have direct deny ACEs.

This addresses the delete-failure variant reported in #30009 and #30712.
It does not claim to fix their separate split-root setup,
elevated-helper, or proxy-related failure modes.

## What Changed

- Give writable-root capability ACEs inheritable `DELETE` rights without
granting parent-level `FILE_DELETE_CHILD`, so descendants can be removed
while protected children remain protected.
- Replace stale write ACEs that still contain `FILE_DELETE_CHILD`, and
make elevated setup detect and refresh that unsafe legacy state.
- Keep read-only capability handling unchanged.
- Add Windows regressions covering pre-existing files in the workspace,
`TEMP`, and `TMP`, plus protected `.git` and outside-root controls.

The core ACL behavior is in
[`acl.rs`](https://github.com/openai/codex/blob/767540eec35cc88dafc34d87fcec7e58087413fb/codex-rs/windows-sandbox-rs/src/acl.rs#L303-L438),
stale-ACE detection is in
[`setup_main/win.rs`](https://github.com/openai/codex/blob/767540eec35cc88dafc34d87fcec7e58087413fb/codex-rs/windows-sandbox-rs/src/bin/setup_main/win.rs#L163-L179),
and the end-to-end regression is in
[`unified_exec/tests.rs`](https://github.com/openai/codex/blob/767540eec35cc88dafc34d87fcec7e58087413fb/codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs#L458-L568).

## How to Test

On Windows:

1. Start Codex with the legacy unelevated Windows sandbox and a
workspace-write permission profile.
2. Seed pre-existing files in the workspace, `TEMP`, and `TMP`; also
create a sibling file outside the writable roots and a protected `.git`
directory.
3. Delete the three files inside writable roots through a sandboxed
command or `apply_patch`.
4. Confirm the writable-root files are deleted, while the outside-root
file and protected `.git` directory remain intact.

Targeted tests:

- `just test -p codex-windows-sandbox`
- Windows-only
`legacy_workspace_write_delete_is_limited_to_writable_roots`
- Windows-only `write_root_refresh_replaces_stale_delete_child_grant`

The final SHA passed all 31 required checks, including the Windows Bazel
test matrix, in [run
28886245161](https://github.com/openai/codex/actions/runs/28886245161).
This commit is contained in:
Felipe Coury authored and GitHub committed 2026-07-08 15:05:57 -03:00
1 parent 48cf582331
commit 166534fc22
4 files changed
+214 -26

No files matched your search

@@ -1,6 +1,7 @@
use crate::acl::add_allow_ace;
use crate::acl::add_deny_write_ace;
use crate::acl::allow_null_device;
use crate::acl::ensure_allow_write_aces;
use crate::allow::AllowDenyPaths;
use crate::allow::compute_allow_paths_for_permissions;
use crate::cap::load_or_create_cap_sids;
@@ -294,7 +295,7 @@ pub(crate) fn apply_legacy_session_acl_rules(
let Some(root_sid) = matching_root_capability(p, acl_sids.write_root_sids) else {
continue;
};
let _ = add_allow_ace(p, root_sid.sid.as_ptr());
let _ = ensure_allow_write_aces(p, &[root_sid.sid.as_ptr()]);
}
}
for p in &deny {