windows-sandbox: drive write roots from resolved permissions (#22923)

## Why

This is the third PR in the Windows sandbox `SandboxPolicy` ->
`PermissionProfile` migration stack.

#22896 introduced `ResolvedWindowsSandboxPermissions`, and #22918 moved
elevated runner IPC to carry `PermissionProfile`. This PR starts moving
the remaining setup/spawn helpers away from asking legacy enum questions
like “is this `WorkspaceWrite`?” and toward resolved runtime permission
questions like “does this profile require write capability roots?”

## What changed

- Added resolved-permissions helpers for network identity and
write-capability detection.
- Moved setup write-root gathering to operate on
`ResolvedWindowsSandboxPermissions`, with the legacy `SandboxPolicy`
wrapper left in place for existing call sites.
- Updated identity setup, elevated capture setup, and world-writable
audit denies to use resolved write roots.
- Updated spawn preparation to carry resolved permissions in
`SpawnContext` and use them for network blocking, setup write roots,
elevated capability SID selection, and legacy capability roots.
- Removed a now-unused legacy write-root helper.

## Verification

- `cargo test -p codex-windows-sandbox`
- `just fix -p codex-windows-sandbox`
- Existing stack checks are green on #22896 and #22918; CI has started
for this PR.
















---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/22923).
* #23715
* #23714
* #23167
* __->__ #22923
This commit is contained in:
Michael Bolin authored and GitHub committed 2026-05-20 14:30:42 -07:00
1 parent f48be015d6
commit e1ec0eee5f
10 files changed
+361 -134

No files matched your search

+43 -5
View File
@@ -18,6 +18,7 @@ use serial_test::serial;
use std::collections::HashMap;
use std::ffi::OsString;
use std::path::Path;
use std::path::PathBuf;
use tempfile::TempDir;
struct EnvVarGuard {
@@ -46,13 +47,49 @@ impl Drop for EnvVarGuard {
}
}
enum TestCodexHome {
Persistent(PathBuf),
Temporary(TempDir),
}
impl TestCodexHome {
fn path(&self) -> &Path {
match self {
Self::Persistent(path) => path.as_path(),
Self::Temporary(temp_dir) => temp_dir.path(),
}
}
}
fn codex_home_for_windows_sandbox_test(name: &str) -> anyhow::Result<TestCodexHome> {
if let Some(test_tmpdir) = std::env::var_os("TEST_TMPDIR") {
// The elevated backend provisions machine-local sandbox users. Bazel
// retries run in the same Windows VM, so keep CODEX_HOME stable within
// the test temp root and let setup reconcile its persisted ACL state.
let codex_home = PathBuf::from(test_tmpdir).join(name);
std::fs::create_dir_all(&codex_home)
.with_context(|| format!("create stable test CODEX_HOME {}", codex_home.display()))?;
return Ok(TestCodexHome::Persistent(codex_home));
}
Ok(TestCodexHome::Temporary(TempDir::new()?))
}
fn stage_windows_sandbox_helpers() -> anyhow::Result<()> {
let test_exe = std::env::current_exe().context("resolve current Windows test executable")?;
let test_exe_dir = test_exe
.parent()
.context("Windows test executable should have a parent directory")?;
let resources_dir = test_exe_dir.join("codex-resources");
std::fs::create_dir_all(&resources_dir)?;
match std::fs::create_dir_all(&resources_dir) {
Ok(()) => {}
Err(err)
if err.kind() == std::io::ErrorKind::PermissionDenied && resources_dir.is_dir() => {}
Err(err) => {
return Err(err)
.with_context(|| format!("create resources dir {}", resources_dir.display()));
}
}
for helper_name in ["codex-windows-sandbox-setup", "codex-command-runner"] {
let helper = codex_utils_cargo_bin::cargo_bin(helper_name)?;
let file_name = Path::new(helper_name).with_extension("exe");
@@ -64,8 +101,9 @@ fn stage_windows_sandbox_helpers() -> anyhow::Result<()> {
#[tokio::test]
#[serial(codex_home)]
async fn windows_restricted_token_rejects_exact_and_glob_deny_read_policy() -> anyhow::Result<()> {
let temp_home = TempDir::new()?;
let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", temp_home.path().as_os_str());
let codex_home =
codex_home_for_windows_sandbox_test("windows-restricted-token-deny-read-codex-home")?;
let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", codex_home.path().as_os_str());
let workspace = TempDir::new()?;
let cwd = dunce::canonicalize(workspace.path())?.abs();
let secret = cwd.join("secret.env");
@@ -144,8 +182,8 @@ async fn windows_restricted_token_rejects_exact_and_glob_deny_read_policy() -> a
#[tokio::test]
#[serial(codex_home)]
async fn windows_elevated_enforces_exact_and_glob_deny_read_policy() -> anyhow::Result<()> {
let temp_home = TempDir::new()?;
let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", temp_home.path().as_os_str());
let codex_home = codex_home_for_windows_sandbox_test("windows-elevated-deny-read-codex-home")?;
let _codex_home_guard = EnvVarGuard::set("CODEX_HOME", codex_home.path().as_os_str());
stage_windows_sandbox_helpers()?;
let workspace = TempDir::new()?;
let cwd = dunce::canonicalize(workspace.path())?.abs();