Consolidate Windows filesystem helper sandbox preparation (#61276)

## What changed

Remove `SandboxDirectSpawnTransformRequest` and `transform_for_direct_spawn` in favor of `SandboxManager::for_file_system_helpers().transform()`. Keep Windows wrapper preparation specific to filesystem helpers, and return `SandboxUnavailable` when a Windows token sandbox is requested for a filesystem helper on a non-Windows executor.

Record the helper permission entry count during launch policy preparation, before adding runtime read grants.

## Testing

Cover Windows helper executable read access and explicit workspace write grants, including empty workspace roots. Verify rejection of non-native policy working directories and Windows token helper sandboxes on Unix. Extend the helper isolation test to exercise sandboxed writes as well as reads.

GitOrigin-RevId: f95bb94e12d8d7c6e5c773644abb7b59b47ffc4a
This commit is contained in:
viyatb-oai
2026-11-13 06:39:52 +00:00
committed by copyberry
parent 454bf339f0
commit 5045589550
7 changed files with 262 additions and 269 deletions
+24 -28
View File
@@ -38,12 +38,10 @@ use codex_protocol::protocol::Op;
use codex_protocol::protocol::ReviewDecision;
use codex_protocol::user_input::UserInput;
use codex_sandboxing::SandboxCommand;
use codex_sandboxing::SandboxDirectSpawnTransformRequest;
use codex_sandboxing::SandboxManager;
use codex_sandboxing::SandboxSelection;
use codex_sandboxing::SandboxTransformRequest;
use codex_sandboxing::SandboxType;
use codex_sandboxing::WindowsSandboxProxySettingsMode;
use codex_utils_path_uri::PathUri;
use codex_windows_sandbox_test_support::WindowsSandboxAccountTestGuard;
use core_test_support::PathExt;
@@ -504,33 +502,31 @@ async fn windows_elevated_temp_only_core_and_direct_spawn_enforce_carveouts() ->
let command = format!("echo CHILD-TEMP:{child_var} & {command}");
let (exit_code, stdout, stderr) = if direct_spawn {
let cwd_uri = PathUri::from_abs_path(&cwd);
let request = SandboxManager::new().transform_for_direct_spawn(
SandboxDirectSpawnTransformRequest {
workspace_roots: std::slice::from_ref(&cwd),
windows_sandbox_proxy_settings_mode: WindowsSandboxProxySettingsMode::Preserve,
transform: SandboxTransformRequest {
command: SandboxCommand {
program: "cmd.exe".into(),
args: vec!["/D".into(), "/C".into(), command],
cwd: cwd_uri.clone(),
env,
managed_network: None,
additional_permissions: None,
},
permissions: &permission_profile,
workspace_roots: None,
runtime_readable_paths: &[],
selection: SandboxSelection::Explicit(SandboxType::WindowsRestrictedToken),
enforce_managed_network: false,
environment_id: None,
network: None,
sandbox_policy_cwd: &cwd_uri,
sandbox_exe: Some(codex.as_path()),
use_legacy_landlock: false,
windows_sandbox_level: WindowsSandboxLevel::Elevated,
// Exec-server spawns the Windows sandbox wrapper returned by transform for filesystem
// helpers. Use cmd.exe as the inner command to probe file access through that wrapper.
let request = SandboxManager::for_file_system_helpers()
.transform(SandboxTransformRequest {
command: SandboxCommand {
program: "cmd.exe".into(),
args: vec!["/D".into(), "/C".into(), command],
cwd: cwd_uri.clone(),
env,
managed_network: None,
additional_permissions: None,
},
},
)?;
permissions: &permission_profile,
workspace_roots: Some(std::slice::from_ref(&cwd_uri)),
runtime_readable_paths: &[],
selection: SandboxSelection::Explicit(SandboxType::WindowsRestrictedToken),
enforce_managed_network: false,
environment_id: None,
network: None,
sandbox_policy_cwd: &cwd_uri,
sandbox_exe: Some(codex.as_path()),
use_legacy_landlock: false,
windows_sandbox_level: WindowsSandboxLevel::Elevated,
})?
.into_command_request();
let output = Command::new(&request.command[0])
.args(&request.command[1..])
.current_dir(request.cwd.to_abs_path()?)
+131 -39
View File
@@ -153,8 +153,6 @@ impl FileSystemSandboxRunner {
})?
.into_command_request();
request.sandbox_override = sandbox.sandbox_override;
let policy = request.permission_profile.file_system_sandbox_policy();
tracing::Span::current().record("permission_entries", policy.entries.len());
Ok(request)
}
}
@@ -535,6 +533,32 @@ mod tests {
assert_eq!(request.env.get(&path_key), Some(&path));
}
#[test]
fn sandbox_command_rejects_non_native_policy_cwd() {
#[cfg(unix)]
let foreign_uri = "file://server/share/checkout";
#[cfg(windows)]
let foreign_uri = "file:///usr/local/checkout";
let cwd = PathUri::parse(foreign_uri).expect("valid foreign URI");
let sandbox = sandbox_context_with_cwd(&FileSystemSandboxPolicy::read_only(), cwd.clone());
let paths = ExecServerRuntimeOptions::new(
std::env::current_exe().expect("current executable"),
/*codex_linux_sandbox_exe*/ None,
)
.expect("runtime paths");
let error = FileSystemSandboxRunner::new(paths)
.sandbox_command(&sandbox)
.expect_err("non-native policy cwd should be rejected");
assert_eq!(
error,
crate::rpc::invalid_request(format!(
"'{cwd}' is invalid on '{}'",
std::env::consts::OS
))
);
}
/// Removing the selected directory must not change permission anchoring or prevent launch.
#[test]
fn sandbox_exec_request_uses_filesystem_root_and_preserves_policy_cwd() {
@@ -655,45 +679,113 @@ mod tests {
#[cfg(windows)]
#[test]
fn helper_permissions_include_only_the_helper_executable() {
let codex_self_exe = std::env::current_exe().expect("current exe");
let codex_self_exe = codex_self_exe.canonicalize().expect("canonical helper");
let runtime_paths =
ExecServerRuntimeOptions::new(codex_self_exe, /*codex_linux_sandbox_exe*/ None)
.expect("runtime paths");
// :root covers the cwd's drive. The helper needs an exact read on its own drive.
let drive = if runtime_paths.codex_self_exe.as_path().starts_with(r"C:\") {
r"D:\"
} else {
r"C:\"
};
let cwd = AbsolutePathBuf::from_absolute_path(drive).expect("absolute cwd");
let parent = runtime_paths
.codex_self_exe
.parent()
.expect("current exe parent");
let sibling = parent.join("credentials.json");
fn windows_helper_binds_executable_and_workspace_grants() {
const CHILD: &str = "CODEX_TEST_WINDOWS_HELPER_GRANTS_CHILD";
if std::env::var_os(CHILD).is_none() {
// Preparing the token wrapper persists capability SIDs under CODEX_HOME.
let home = tempfile::tempdir().expect("isolated Codex home");
let output = std::process::Command::new(std::env::current_exe().expect("test binary"))
.args([
"--exact",
"fs_sandbox::tests::windows_helper_binds_executable_and_workspace_grants",
])
.env(CHILD, "1")
.env("CODEX_HOME", home.path())
.output()
.expect("run isolated Windows helper test");
assert!(
output.status.success()
&& String::from_utf8_lossy(&output.stdout).contains("1 passed"),
"isolated helper test: {output:?}"
);
return;
}
let context = crate::FileSystemSandboxContext {
windows_sandbox_selection: codex_file_system::WindowsSandboxSelection::RestrictedToken,
..sandbox_context_with_cwd(
&FileSystemSandboxPolicy::read_only(),
PathUri::from_abs_path(&cwd),
)
let workspace = tempfile::tempdir().expect("workspace");
let other = tempfile::tempdir().expect("other workspace");
let roots = [workspace.path(), other.path()].map(|path| {
AbsolutePathBuf::from_absolute_path(path.canonicalize().expect("canonical workspace"))
.expect("absolute workspace")
});
let drive = roots[0].ancestors().last().expect("workspace drive");
// An unused drive also avoids resolving a mapped drive back into the workspace.
let helper = ('D'..='Z')
.rev()
.map(|letter| std::path::PathBuf::from(format!(r"{letter}:\")))
.find(|root| !root.exists())
.expect("unused drive for the helper policy")
.join(r"codex-test-runtime\helper.exe");
let helper = AbsolutePathBuf::from_absolute_path(helper).expect("helper");
let helper_dir = helper.parent().expect("helper directory");
let runtime_paths = ExecServerRuntimeOptions::new(
helper.to_path_buf(),
/*codex_linux_sandbox_exe*/ None,
)
.expect("runtime paths");
let runner = FileSystemSandboxRunner {
runtime_paths,
helper_env: HashMap::new(),
};
let request = FileSystemSandboxRunner::new(runtime_paths.clone())
.sandbox_command(&context)
.expect("sandbox command");
let policy = request.permission_profile.file_system_sandbox_policy();
assert!(
policy.can_read_local_path_with_cwd(
runtime_paths.codex_self_exe.as_path(),
cwd.as_path(),
)
);
assert!(!policy.can_read_local_path_with_cwd(parent.as_path(), cwd.as_path()));
assert!(!policy.can_read_local_path_with_cwd(sibling.as_path(), cwd.as_path()));
let policy = restricted_policy(vec![
special_entry(FileSystemSpecialPath::Root, FileSystemAccessMode::Read),
special_entry(
FileSystemSpecialPath::project_roots(/*subpath*/ None),
FileSystemAccessMode::Write,
),
]);
for (grants, policy_cwd, wrapper_roots, writable) in [
(&roots[..], &roots[0], &roots[..], [true, true]),
// The wrapper encodes the command's drive root when no write roots were granted.
(
&roots[..0],
&roots[0],
std::slice::from_ref(&drive),
[false, false],
),
(&roots[1..], &roots[1], &roots[1..], [false, true]),
] {
let sandbox = crate::FileSystemSandboxContext {
workspace_roots: grants.iter().map(PathUri::from_abs_path).collect(),
windows_sandbox_selection:
codex_file_system::WindowsSandboxSelection::RestrictedToken,
..sandbox_context_with_cwd(&policy, PathUri::from_abs_path(policy_cwd))
};
let request = runner
.sandbox_command(&sandbox)
.expect("prepare Windows helper");
let args = &request.command;
assert_eq!(
args.windows(2)
.filter(|pair| pair[0] == "--workspace-root")
.map(|pair| std::path::Path::new(&pair[1]))
.collect::<Vec<_>>(),
wrapper_roots
.iter()
.map(AbsolutePathBuf::as_path)
.collect::<Vec<_>>()
);
let profile = args
.windows(2)
.find(|pair| pair[0] == "--permission-profile")
.expect("serialized helper policy");
let profile: PermissionProfile =
serde_json::from_str(&profile[1]).expect("helper policy");
let fs = profile.file_system_sandbox_policy();
assert_eq!(
roots
.each_ref()
.map(|root| fs.can_write_local_path_with_cwd(root, policy_cwd)),
writable
);
assert_eq!(
fs.resolve_access_for_local_path_with_cwd(&helper, policy_cwd),
FileSystemAccessMode::Read
);
assert!(!fs.can_read_local_path_with_cwd(&helper_dir, policy_cwd));
assert!(
!fs.can_read_local_path_with_cwd(&helper_dir.join("credentials.json"), policy_cwd)
);
}
}
#[cfg(unix)]
@@ -1225,14 +1225,19 @@ async fn sandboxed_file_operations_cannot_read_or_write_helper_siblings() -> Res
std::os::unix::fs::symlink(&sibling, &escaping_link)?;
let sandbox = workspace_write_sandbox(workspace.clone());
let allowed_file = workspace.join("allowed.txt");
std::fs::write(&allowed_file, b"allowed")?;
let allowed_contents = file_system
.read_file(
&PathUri::from_host_native_path(&allowed_file)?,
let allowed_uri = PathUri::from_host_native_path(&allowed_file)?;
file_system
.write_file(
&allowed_uri,
b"allowed".to_vec(),
Default::default(),
Some(&sandbox),
)
.await?;
assert_eq!(std::fs::read(&allowed_file)?, b"allowed");
let allowed_contents = file_system
.read_file(&allowed_uri, Default::default(), Some(&sandbox))
.await?;
assert_eq!(allowed_contents, b"allowed");
#[cfg(target_os = "macos")]
+5 -8
View File
@@ -113,7 +113,10 @@ impl PreparedLaunchPolicy {
});
};
let cwd = native_path(policy_cwd, "sandbox policy cwd")?;
let workspace_roots = native_workspace_roots(roots)?;
let workspace_roots = roots
.iter()
.map(|root| native_path(root, "sandbox workspace root"))
.collect::<io::Result<Vec<_>>>()?;
let (file_system, network) = permissions.to_runtime_permissions();
for entry in &file_system.entries {
if let FileSystemPath::Path { path } = &entry.path {
@@ -123,6 +126,7 @@ impl PreparedLaunchPolicy {
let mut file_system =
file_system.materialize_project_roots_with_workspace_roots(&workspace_roots);
let permissions = if is_file_system_helper {
tracing::Span::current().record("permission_entries", file_system.entries.len());
if !file_system.has_full_disk_read_access() {
let minimal = FileSystemSandboxEntry::new(
FileSystemPath::Special {
@@ -170,13 +174,6 @@ impl PreparedLaunchPolicy {
}
}
pub(super) fn native_workspace_roots(roots: &[PathUri]) -> io::Result<Vec<AbsolutePathBuf>> {
roots
.iter()
.map(|root| native_path(root, "sandbox workspace root"))
.collect::<io::Result<Vec<_>>>()
}
fn native_path(path: &PathUri, field: &str) -> io::Result<AbsolutePathBuf> {
path.to_abs_path()
.map_err(|err| io::Error::new(err.kind(), format!("invalid {field}: {err}")))
-1
View File
@@ -31,7 +31,6 @@ pub use denial::is_likely_executor_managed_sandbox_denied;
pub use denial::is_likely_sandbox_denied;
pub use linux_pid_namespace::LinuxSandboxPidNamespace;
pub use manager::SandboxCommand;
pub use manager::SandboxDirectSpawnTransformRequest;
pub use manager::SandboxExecRequest;
pub use manager::SandboxManager;
pub use manager::SandboxSelection;
+18 -74
View File
@@ -149,17 +149,6 @@ pub struct SandboxTransformRequest<'a> {
pub windows_sandbox_level: WindowsSandboxLevel,
}
/// Bundled arguments for a sandbox transformation whose result will be spawned
/// directly from argv.
///
/// Direct-spawn callers will not run a later platform-specific launcher, so the
/// returned command must encode any sandbox wrapper it needs.
pub struct SandboxDirectSpawnTransformRequest<'a> {
pub transform: SandboxTransformRequest<'a>,
pub workspace_roots: &'a [AbsolutePathBuf],
pub windows_sandbox_proxy_settings_mode: codex_windows_sandbox::WindowsSandboxProxySettingsMode,
}
// TODO(anp): Revisit this preparation type once this module's PathUri migration is complete.
struct PendingSandboxedExecRequest {
native_command_cwd: AbsolutePathBuf,
@@ -624,15 +613,22 @@ impl SandboxManager {
}
SandboxType::WindowsRestrictedToken => {
exec.permission_profile = pending?.effective_permission_profile;
#[cfg(windows)]
if is_file_system_helper {
wrap_windows_sandbox_exec_request_for_direct_spawn(
&mut exec,
&policy.workspace_roots,
/*codex_home*/ None,
request.sandbox_exe,
codex_windows_sandbox::WindowsSandboxProxySettingsMode::Preserve,
)?;
#[cfg(not(windows))]
return Err(SandboxTransformError::SandboxUnavailable);
#[cfg(windows)]
{
let codex_home =
codex_utils_home_dir::find_codex_home().map_err(|err| {
SandboxTransformError::WindowsSandboxPreparation(err.to_string())
})?;
wrap_windows_file_system_helper(
&mut exec,
&policy.workspace_roots,
&codex_home,
request.sandbox_exe,
)?;
}
}
}
}
@@ -679,67 +675,15 @@ impl SandboxManager {
exec.arg0 = Some(arg0.to_owned());
Ok(())
}
pub fn transform_for_direct_spawn(
&self,
request: SandboxDirectSpawnTransformRequest<'_>,
) -> Result<SandboxExecRequest, SandboxTransformError> {
#[cfg(windows)]
{
self.transform_for_direct_spawn_with_codex_home(request, /*codex_home*/ None)
}
#[cfg(not(windows))]
{
self.transform(request.transform)
.map(WrappedSandboxCommand::into_command_request)
}
}
#[cfg(target_os = "windows")]
fn transform_for_direct_spawn_with_codex_home(
&self,
request: SandboxDirectSpawnTransformRequest<'_>,
codex_home: Option<&Path>,
) -> Result<SandboxExecRequest, SandboxTransformError> {
let roots = request.transform.workspace_roots;
let fallback_roots = request.workspace_roots;
let proxy_settings_mode = request.windows_sandbox_proxy_settings_mode;
let sandbox_exe = request.transform.sandbox_exe;
let mut request = self.transform(request.transform)?.into_command_request();
if request.sandbox != SandboxType::WindowsRestrictedToken {
return Ok(request);
}
let workspace_roots = roots
.map(launch::native_workspace_roots)
.transpose()
.map_err(SandboxTransformError::InvalidPolicy)?;
wrap_windows_sandbox_exec_request_for_direct_spawn(
&mut request,
workspace_roots.as_deref().unwrap_or(fallback_roots),
codex_home,
sandbox_exe,
proxy_settings_mode,
)?;
Ok(request)
}
}
#[cfg(target_os = "windows")]
fn wrap_windows_sandbox_exec_request_for_direct_spawn(
fn wrap_windows_file_system_helper(
request: &mut SandboxExecRequest,
workspace_roots: &[AbsolutePathBuf],
codex_home: Option<&Path>,
codex_home: &Path,
sandbox_exe: Option<&Path>,
proxy_settings_mode: codex_windows_sandbox::WindowsSandboxProxySettingsMode,
) -> Result<(), SandboxTransformError> {
let owned_codex_home;
let codex_home = if let Some(codex_home) = codex_home {
codex_home
} else {
owned_codex_home = codex_utils_home_dir::find_codex_home()
.map_err(|err| SandboxTransformError::WindowsSandboxPreparation(err.to_string()))?;
owned_codex_home.as_path()
};
let sandbox_exe = sandbox_exe.ok_or_else(|| {
SandboxTransformError::WindowsSandboxPreparation(
"missing Codex executable path".to_string(),
@@ -821,7 +765,7 @@ fn wrap_windows_sandbox_exec_request_for_direct_spawn(
request.windows_sandbox_level,
proxy_enforced,
network_proxy_restricting_sid.as_deref(),
proxy_settings_mode,
codex_windows_sandbox::WindowsSandboxProxySettingsMode::Preserve,
read_roots_override,
read_roots_include_platform_defaults,
write_roots_override,
+75 -115
View File
@@ -1,6 +1,4 @@
use super::SandboxCommand;
#[cfg(windows)]
use super::SandboxDirectSpawnTransformRequest;
use super::SandboxManager;
use super::SandboxSelection;
use super::SandboxTransformRequest;
@@ -394,35 +392,58 @@ fn managed_mitm_ca_bundle_becomes_readable_for_restricted_sandbox() {
);
}
#[cfg(unix)]
fn sandbox_transform_request(cwd: &PathUri, sandbox: SandboxType) -> SandboxTransformRequest<'_> {
SandboxTransformRequest {
command: SandboxCommand {
program: "true".into(),
args: Vec::new(),
cwd: cwd.clone(),
env: HashMap::new(),
managed_network: None,
additional_permissions: None,
},
permissions: &PermissionProfile::Disabled,
workspace_roots: None,
runtime_readable_paths: &[],
selection: SandboxSelection::Explicit(sandbox),
enforce_managed_network: false,
environment_id: None,
network: None,
sandbox_policy_cwd: cwd,
sandbox_exe: None,
use_legacy_landlock: false,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
}
}
#[cfg(unix)]
#[test]
fn windows_token_filesystem_helper_is_unavailable_on_unix() {
let cwd = PathUri::from_abs_path(&AbsolutePathBuf::current_dir().expect("current dir"));
let request = SandboxTransformRequest {
permissions: &PermissionProfile::read_only(),
workspace_roots: Some(std::slice::from_ref(&cwd)),
windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
..sandbox_transform_request(&cwd, SandboxType::WindowsRestrictedToken)
};
assert!(matches!(
SandboxManager::for_file_system_helpers().transform(request),
Err(super::SandboxTransformError::SandboxUnavailable)
));
}
#[cfg(unix)]
fn transform_linux_seccomp_request(
codex_linux_sandbox_exe: &std::path::Path,
manager: SandboxManager,
) -> super::WrappedSandboxCommand {
let cwd = AbsolutePathBuf::current_dir().expect("current dir");
let cwd_uri = PathUri::from_abs_path(&cwd);
let permissions = PermissionProfile::Disabled;
let cwd = PathUri::from_abs_path(&AbsolutePathBuf::current_dir().expect("current dir"));
manager
.transform(SandboxTransformRequest {
command: SandboxCommand {
program: "true".into(),
args: Vec::new(),
cwd: cwd_uri.clone(),
env: HashMap::new(),
managed_network: None,
additional_permissions: None,
},
permissions: &permissions,
workspace_roots: None,
runtime_readable_paths: &[],
selection: SandboxSelection::Explicit(SandboxType::LinuxSeccomp),
enforce_managed_network: false,
environment_id: None,
network: None,
sandbox_policy_cwd: &cwd_uri,
sandbox_exe: Some(codex_linux_sandbox_exe),
use_legacy_landlock: false,
windows_sandbox_level: WindowsSandboxLevel::Disabled,
..sandbox_transform_request(&cwd, SandboxType::LinuxSeccomp)
})
.expect("transform")
}
@@ -700,7 +721,7 @@ async fn linux_unix_socket_grant_uses_effective_managed_policy() -> anyhow::Resu
#[cfg(target_os = "windows")]
#[test]
fn transform_for_direct_spawn_windows_preserves_only_wrapper_setup_environment() {
fn windows_wrapper_preserves_only_setup_environment() {
let mut env = HashMap::from([
("Path".to_string(), r"C:\Windows\System32".to_string()),
("username".to_string(), "wrong-user".to_string()),
@@ -760,7 +781,7 @@ fn wrapper_runtime_selection_uses_the_parent_not_environment_overrides() {
#[cfg(target_os = "windows")]
#[test]
fn transform_for_direct_spawn_windows_materializes_inner_helper() {
fn windows_file_system_wrapper_materializes_inner_helper() {
let codex_home = tempfile::TempDir::new().expect("codex home");
let helper_dir = tempfile::TempDir::new().expect("helper dir");
let configured_helper = helper_dir.path().join("configured-codex-helper.exe");
@@ -796,50 +817,37 @@ fn transform_for_direct_spawn_windows_materializes_inner_helper() {
]),
NetworkSandboxPolicy::Restricted,
);
let other_workspace = tempfile::TempDir::new().expect("other workspace");
let other_workspace_root = AbsolutePathBuf::from_absolute_path(
canonicalize(other_workspace.path()).expect("canonical other workspace"),
let policy = super::PreparedLaunchPolicy::resolve(
permissions,
&cwd_uri,
Some(std::slice::from_ref(&cwd_uri)),
&[],
&super::SandboxOperation::FileSystemHelper,
)
.expect("absolute other workspace");
let workspace_roots = [cwd, other_workspace_root];
let manager = SandboxManager::new();
let prepare = |roots, policy_cwd| {
manager
.transform_for_direct_spawn_with_codex_home(
SandboxDirectSpawnTransformRequest {
workspace_roots: workspace_roots.as_slice(),
windows_sandbox_proxy_settings_mode:
codex_windows_sandbox::WindowsSandboxProxySettingsMode::Preserve,
transform: SandboxTransformRequest {
command: SandboxCommand {
program: configured_helper.as_os_str().to_owned(),
args: vec!["--codex-run-as-fs-helper".to_string()],
cwd: cwd_uri.clone(),
env: HashMap::from([(
"Path".to_string(),
r"C:\Windows\System32".to_string(),
)]),
managed_network: None,
additional_permissions: None,
},
permissions: &permissions,
workspace_roots: roots,
runtime_readable_paths: &[],
selection: SandboxSelection::Explicit(SandboxType::WindowsRestrictedToken),
enforce_managed_network: false,
environment_id: None,
network: None,
sandbox_policy_cwd: policy_cwd,
sandbox_exe: Some(configured_helper.as_path()),
use_legacy_landlock: false,
windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
},
},
Some(codex_home.path()),
)
.expect("transform for direct spawn")
.expect("resolve helper policy");
let mut exec_request = super::SandboxExecRequest {
sandbox_override: super::SandboxOverride::NoOverride,
command: vec![
configured_helper.display().to_string(),
"--codex-run-as-fs-helper".to_string(),
],
cwd: cwd_uri.clone(),
sandbox_policy_cwd: cwd_uri,
env: HashMap::from([("Path".to_string(), r"C:\Windows\System32".to_string())]),
network: None,
network_environment_id: None,
sandbox: SandboxType::WindowsRestrictedToken,
windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
permission_profile: policy.permissions,
arg0: None,
};
let exec_request = prepare(/*roots*/ None, &cwd_uri);
super::wrap_windows_file_system_helper(
&mut exec_request,
&policy.workspace_roots,
codex_home.path(),
Some(&configured_helper),
)
.expect("wrap filesystem helper");
let inner_env_json = exec_request
.command
@@ -885,54 +893,6 @@ fn transform_for_direct_spawn_windows_materializes_inner_helper() {
exec_request.command[separator_index + 2],
"--codex-run-as-fs-helper"
);
let explicit_root = PathUri::from_abs_path(&workspace_roots[1]);
// The wrapper encodes the cwd for an empty root list. Its serialized policy must
// already have removed the workspace write grant.
for (request, expected_roots, expected_writes) in [
(&exec_request, &workspace_roots[..], None),
(
&prepare(Some(&[]), &cwd_uri),
&workspace_roots[..1],
Some([false, false]),
),
(
// The legacy workspace-write projection always includes its policy cwd.
// Anchor it at the grant and check the command cwd independently.
&prepare(Some(std::slice::from_ref(&explicit_root)), &explicit_root),
&workspace_roots[1..],
Some([false, true]),
),
] {
assert_eq!(
request
.command
.windows(2)
.filter_map(|args| {
(args[0] == "--workspace-root").then_some(std::path::PathBuf::from(&args[1]))
})
.collect::<Vec<_>>(),
expected_roots
.iter()
.map(|root| root.as_path().to_path_buf())
.collect::<Vec<_>>()
);
if let Some(expected_writes) = expected_writes {
let profile_json = request
.command
.windows(2)
.find(|args| args[0] == "--permission-profile")
.expect("serialized wrapper policy");
let policy = serde_json::from_str::<PermissionProfile>(&profile_json[1])
.expect("decode wrapper policy")
.file_system_sandbox_policy();
assert_eq!(
workspace_roots
.each_ref()
.map(|root| policy.can_write_local_path_with_cwd(root, &workspace_roots[0])),
expected_writes
);
}
}
assert_eq!(
materialized_helper
.parent()