[codex] Grant Windows sandbox access to primary runtime (#31574)

## Why

Codex Desktop installs its managed primary runtime under
`%USERPROFILE%\.cache\codex-runtimes`. Elevated Windows sandbox commands
run as dedicated sandbox users. The synchronous runtime ACL refresh
repairs read/execute access for the Desktop runtime directories under
`%LOCALAPPDATA%\OpenAI\Codex`, but did not include the managed primary
runtime cache.

As a result, the Desktop app could discover a bundled runtime while a
sandboxed command received `ACCESS_DENIED` when reading or executing it.

## What changed

- Include `%USERPROFILE%\.cache\codex-runtimes` in the managed runtime
paths considered by the Windows sandbox ACL refresh.
- Reuse the existing inherited read/execute ACL repair; no write
permission is added.
- Add Windows-target regression coverage for the runtime path list and
the primary-runtime-only case.

## Impact

Bundled Python, Node, and native tools remain usable from elevated
Windows sandbox sessions without broadening write access or granting
access to the rest of the user profile.

## Validation

- `just fmt`
- `just test -p codex-windows-sandbox` (10/10 host-side tests passed)
- Windows-target path tests included for CI
This commit is contained in:
Abhinav
2026-07-08 13:22:10 -07:00
committed by GitHub
parent 2780bd588f
commit c6b124bb31
2 changed files with 51 additions and 7 deletions
@@ -10,20 +10,22 @@ use windows_sys::Win32::Security::OBJECT_INHERIT_ACE;
use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_EXECUTE;
use windows_sys::Win32::Storage::FileSystem::FILE_GENERIC_READ;
#[cfg(test)]
#[path = "setup_runtime_bin_tests.rs"]
mod tests;
pub(super) fn ensure_codex_app_runtime_paths_readable(
sandbox_group_psid: *mut c_void,
refresh_errors: &mut Vec<String>,
log: &mut dyn Write,
) -> Result<()> {
let local_app_data = local_app_data_root();
let Some(local_app_data) = local_app_data else {
return Ok(());
};
let read_execute_mask = FILE_GENERIC_READ | FILE_GENERIC_EXECUTE;
let codex_root = local_app_data.join("OpenAI").join("Codex");
let runtime_paths = runtime_paths(
local_app_data_root(),
std::env::var_os("USERPROFILE").map(PathBuf::from),
);
for runtime_path in [codex_root.join("bin"), codex_root.join("runtimes")] {
for runtime_path in runtime_paths {
if !runtime_path.is_dir() {
continue;
}
@@ -86,6 +88,20 @@ pub(super) fn ensure_codex_app_runtime_paths_readable(
Ok(())
}
fn runtime_paths(local_app_data: Option<PathBuf>, user_profile: Option<PathBuf>) -> Vec<PathBuf> {
let mut runtime_paths = Vec::new();
if let Some(local_app_data) = local_app_data {
let codex_root = local_app_data.join("OpenAI").join("Codex");
runtime_paths.extend([codex_root.join("bin"), codex_root.join("runtimes")]);
}
// The managed primary runtime is installed outside the LocalAppData runtime roots.
if let Some(user_profile) = user_profile {
runtime_paths.push(user_profile.join(".cache").join("codex-runtimes"));
}
runtime_paths
}
fn local_app_data_root() -> Option<PathBuf> {
std::env::var_os("LOCALAPPDATA")
.map(PathBuf::from)
@@ -0,0 +1,28 @@
use super::runtime_paths;
use pretty_assertions::assert_eq;
use std::path::PathBuf;
#[test]
fn runtime_paths_include_desktop_and_primary_runtime_roots() {
let local_app_data = PathBuf::from(r"C:\Users\user\AppData\Local");
let user_profile = PathBuf::from(r"C:\Users\user");
assert_eq!(
runtime_paths(Some(local_app_data), Some(user_profile)),
vec![
PathBuf::from(r"C:\Users\user\AppData\Local\OpenAI\Codex\bin"),
PathBuf::from(r"C:\Users\user\AppData\Local\OpenAI\Codex\runtimes"),
PathBuf::from(r"C:\Users\user\.cache\codex-runtimes"),
]
);
}
#[test]
fn primary_runtime_path_does_not_depend_on_local_app_data() {
let user_profile = PathBuf::from(r"C:\Users\user");
assert_eq!(
runtime_paths(/*local_app_data*/ None, Some(user_profile)),
vec![PathBuf::from(r"C:\Users\user\.cache\codex-runtimes")]
);
}