use long-lived sessions for codex sandbox windows (#18953)

`codex sandbox windows` previously did a one-shot spawn for all
commands.
This change uses the `unified_exec` session to spawn long-lived
processes instead, and implements a simple bridge to forward stdin to
the spawned session and stdout/stderr from the spawned session back to
the caller.

It also fixes a bug with the new shared spawn context code where the
"no-network env" was being applied to both elevated and unelevated
sandbox spawns. It should only be applied for the unelevated sandbox
because the elevated one uses firewall rules instead of an env-based
network suppression strategy.
This commit is contained in:
iceweasel-oai authored and GitHub committed 2026-04-22 06:39:29 +00:00
1 parent 69c8913e24
commit 3a451b6321
2 files changed
+370 -78

No files matched your search

+110 -5
View File
@@ -86,7 +86,7 @@ pub(crate) fn should_apply_network_block(policy: &SandboxPolicy) -> bool {
!policy.has_full_network_access()
}
pub(crate) fn prepare_legacy_spawn_context(
fn prepare_spawn_context_common(
policy_json_or_preset: &str,
codex_home: &Path,
cwd: &Path,
@@ -111,9 +111,6 @@ pub(crate) fn prepare_legacy_spawn_context(
if add_git_safe_directory {
inject_git_safe_directory(env_map, cwd);
}
if should_apply_network_block(&policy) {
apply_no_network_to_env(env_map)?;
}
ensure_codex_home_exists(codex_home)?;
let sandbox_base = codex_home.join(".sandbox");
@@ -132,6 +129,30 @@ pub(crate) fn prepare_legacy_spawn_context(
})
}
pub(crate) fn prepare_legacy_spawn_context(
policy_json_or_preset: &str,
codex_home: &Path,
cwd: &Path,
env_map: &mut HashMap<String, String>,
command: &[String],
inherit_path: bool,
add_git_safe_directory: bool,
) -> Result<SpawnContext> {
let common = prepare_spawn_context_common(
policy_json_or_preset,
codex_home,
cwd,
env_map,
command,
inherit_path,
add_git_safe_directory,
)?;
if should_apply_network_block(&common.policy) {
apply_no_network_to_env(env_map)?;
}
Ok(common)
}
pub(crate) fn prepare_legacy_session_security(
policy: &SandboxPolicy,
codex_home: &Path,
@@ -243,7 +264,7 @@ pub(crate) fn prepare_elevated_spawn_context(
env_map: &mut HashMap<String, String>,
command: &[String],
) -> Result<ElevatedSpawnContext> {
let common = prepare_legacy_spawn_context(
let common = prepare_spawn_context_common(
policy_json_or_preset,
codex_home,
cwd,
@@ -252,6 +273,7 @@ pub(crate) fn prepare_elevated_spawn_context(
/*inherit_path*/ true,
/*add_git_safe_directory*/ true,
)?;
let AllowDenyPaths { allow, deny } = compute_allow_paths(
&common.policy,
sandbox_policy_cwd,
@@ -304,3 +326,86 @@ pub(crate) fn prepare_elevated_spawn_context(
cap_sids,
})
}
#[cfg(test)]
mod tests {
use super::SandboxPolicy;
use super::prepare_legacy_spawn_context;
use super::prepare_spawn_context_common;
use super::should_apply_network_block;
use pretty_assertions::assert_eq;
use std::collections::HashMap;
use tempfile::TempDir;
#[test]
fn no_network_env_rewrite_applies_for_workspace_write() {
assert!(should_apply_network_block(
&SandboxPolicy::new_workspace_write_policy(),
));
}
#[test]
fn no_network_env_rewrite_skips_when_network_access_is_allowed() {
assert!(!should_apply_network_block(
&SandboxPolicy::WorkspaceWrite {
writable_roots: Vec::new(),
read_only_access: Default::default(),
network_access: true,
exclude_tmpdir_env_var: false,
exclude_slash_tmp: false,
},
));
}
#[test]
fn legacy_spawn_env_applies_offline_network_rewrite() {
let codex_home = TempDir::new().expect("tempdir");
let cwd = TempDir::new().expect("tempdir");
let mut env_map = HashMap::new();
let _context = prepare_legacy_spawn_context(
"workspace-write",
codex_home.path(),
cwd.path(),
&mut env_map,
&["cmd.exe".to_string()],
/*inherit_path*/ true,
/*add_git_safe_directory*/ false,
)
.expect("legacy env prep");
assert_eq!(env_map.get("SBX_NONET_ACTIVE"), Some(&"1".to_string()));
assert_eq!(
env_map.get("HTTP_PROXY"),
Some(&"http://127.0.0.1:9".to_string())
);
}
#[test]
fn common_spawn_env_keeps_network_env_unchanged() {
let codex_home = TempDir::new().expect("tempdir");
let cwd = TempDir::new().expect("tempdir");
let mut env_map = HashMap::from([(
"HTTP_PROXY".to_string(),
"http://user.proxy:8080".to_string(),
)]);
let context = prepare_spawn_context_common(
"workspace-write",
codex_home.path(),
cwd.path(),
&mut env_map,
&["cmd.exe".to_string()],
/*inherit_path*/ true,
/*add_git_safe_directory*/ true,
)
.expect("preserve existing env prep");
assert_eq!(context.policy, SandboxPolicy::new_workspace_write_policy());
assert_eq!(env_map.get("SBX_NONET_ACTIVE"), None);
assert_eq!(
env_map.get("HTTP_PROXY"),
Some(&"http://user.proxy:8080".to_string())
);
}
}