Compare commits

...

1 Commits

Author SHA1 Message Date
David Wiesen
e4ff78d28d fix(windows-sandbox): normalize mapped-drive workdirs 2026-04-25 20:45:03 -07:00
5 changed files with 52 additions and 21 deletions

View File

@@ -34,6 +34,7 @@ mod windows_impl {
use crate::logging::log_failure;
use crate::logging::log_start;
use crate::logging::log_success;
use crate::path_normalization::execution_path;
use crate::policy::SandboxPolicy;
use crate::policy::parse_policy;
use crate::runner_client::spawn_runner_transport;
@@ -126,6 +127,8 @@ mod windows_impl {
write_roots_override,
deny_write_paths_override,
} = request;
let cwd = execution_path(cwd);
let sandbox_policy_cwd = execution_path(sandbox_policy_cwd);
let policy = parse_policy(policy_json_or_preset)?;
normalize_null_device_env(&mut env_map);
ensure_non_interactive_pager(&mut env_map);
@@ -186,10 +189,10 @@ mod windows_impl {
(|| -> Result<CaptureResult> {
let spawn_request = SpawnRequest {
command: command.clone(),
cwd: cwd.to_path_buf(),
cwd: cwd.clone(),
env: env_map.clone(),
policy_json_or_preset: policy_json_or_preset.to_string(),
sandbox_policy_cwd: sandbox_policy_cwd.to_path_buf(),
sandbox_policy_cwd: sandbox_policy_cwd.clone(),
codex_home: sandbox_base.clone(),
real_codex_home: codex_home.to_path_buf(),
cap_sids,

View File

@@ -249,6 +249,7 @@ mod windows_impl {
use super::logging::log_failure;
use super::logging::log_success;
use super::path_normalization::canonicalize_path;
use super::path_normalization::execution_path;
use super::policy::SandboxPolicy;
use super::process::create_process_as_user;
use super::sandbox_utils::ensure_codex_home_exists;
@@ -378,7 +379,7 @@ mod windows_impl {
#[allow(clippy::expect_used)]
let psid_generic =
convert_string_sid_to_sid(&caps.workspace).expect("valid workspace SID");
let ws_sid = workspace_cap_sid_for_cwd(codex_home, cwd)?;
let ws_sid = workspace_cap_sid_for_cwd(codex_home, &current_dir)?;
#[allow(clippy::expect_used)]
let psid_workspace =
convert_string_sid_to_sid(&ws_sid).expect("valid workspace SID");
@@ -411,8 +412,9 @@ mod windows_impl {
}
let persist_aces = is_workspace_write;
let normalized_policy_cwd = execution_path(sandbox_policy_cwd);
let AllowDenyPaths { allow, mut deny } =
compute_allow_paths(&policy, sandbox_policy_cwd, &current_dir, &env_map);
compute_allow_paths(&policy, &normalized_policy_cwd, &current_dir, &env_map);
for path in additional_deny_write_paths {
if path.exists() {
deny.insert(path.clone());
@@ -607,13 +609,18 @@ mod windows_impl {
#[allow(clippy::expect_used)]
let psid_generic =
unsafe { convert_string_sid_to_sid(&caps.workspace) }.expect("valid workspace SID");
let ws_sid = workspace_cap_sid_for_cwd(codex_home, cwd)?;
let current_dir = execution_path(cwd);
let ws_sid = workspace_cap_sid_for_cwd(codex_home, &current_dir)?;
#[allow(clippy::expect_used)]
let psid_workspace =
unsafe { convert_string_sid_to_sid(&ws_sid) }.expect("valid workspace SID");
let current_dir = cwd.to_path_buf();
let AllowDenyPaths { allow, deny } =
compute_allow_paths(sandbox_policy, sandbox_policy_cwd, &current_dir, env_map);
let normalized_policy_cwd = execution_path(sandbox_policy_cwd);
let AllowDenyPaths { allow, deny } = compute_allow_paths(
sandbox_policy,
&normalized_policy_cwd,
&current_dir,
env_map,
);
let canonical_cwd = canonicalize_path(&current_dir);
unsafe {
for p in &allow {

View File

@@ -1,6 +1,14 @@
use std::path::Path;
use std::path::PathBuf;
/// Normalize a path before handing it to Windows process-launch APIs.
///
/// For existing paths this prefers the canonical form, which helps mapped-drive
/// workspaces resolve to a form the sandbox logon user can access.
pub fn execution_path(path: &Path) -> PathBuf {
canonicalize_path(path)
}
pub fn canonicalize_path(path: &Path) -> PathBuf {
dunce::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
}
@@ -23,6 +31,9 @@ mod tests {
let windows_style = Path::new(r"C:\Users\Dev\Repo");
let slash_style = Path::new("c:/users/dev/repo");
assert_eq!(canonical_path_key(windows_style), canonical_path_key(slash_style));
assert_eq!(
canonical_path_key(windows_style),
canonical_path_key(slash_style)
);
}
}

View File

@@ -13,6 +13,7 @@ use crate::identity::SandboxCreds;
use crate::identity::require_logon_sandbox_creds;
use crate::logging::log_start;
use crate::path_normalization::canonicalize_path;
use crate::path_normalization::execution_path;
use crate::policy::SandboxPolicy;
use crate::policy::parse_policy;
use crate::sandbox_utils::ensure_codex_home_exists;
@@ -105,11 +106,12 @@ fn prepare_spawn_context_common(
normalize_null_device_env(env_map);
ensure_non_interactive_pager(env_map);
let current_dir = execution_path(cwd);
if inherit_path {
inherit_path_env(env_map);
}
if add_git_safe_directory {
inject_git_safe_directory(env_map, cwd);
inject_git_safe_directory(env_map, &current_dir);
}
ensure_codex_home_exists(codex_home)?;
@@ -122,7 +124,7 @@ fn prepare_spawn_context_common(
Ok(SpawnContext {
policy,
current_dir: cwd.to_path_buf(),
current_dir,
sandbox_base,
logs_base_dir,
is_workspace_write,
@@ -158,6 +160,7 @@ pub(crate) fn prepare_legacy_session_security(
codex_home: &Path,
cwd: &Path,
) -> Result<LegacySessionSecurity> {
let current_dir = execution_path(cwd);
let caps = load_or_create_cap_sids(codex_home)?;
let (h_token, psid_generic, psid_workspace, cap_sid_str) = unsafe {
match policy {
@@ -168,7 +171,7 @@ pub(crate) fn prepare_legacy_session_security(
}
SandboxPolicy::WorkspaceWrite { .. } => {
let psid_generic = LocalSid::from_string(&caps.workspace)?;
let workspace_sid = workspace_cap_sid_for_cwd(codex_home, cwd)?;
let workspace_sid = workspace_cap_sid_for_cwd(codex_home, &current_dir)?;
let psid_workspace = LocalSid::from_string(&workspace_sid)?;
let base = get_current_token_for_restriction()?;
let h_token = create_workspace_write_token_with_caps_from(
@@ -273,10 +276,11 @@ pub(crate) fn prepare_elevated_spawn_context(
/*inherit_path*/ true,
/*add_git_safe_directory*/ true,
)?;
let normalized_policy_cwd = execution_path(sandbox_policy_cwd);
let AllowDenyPaths { allow, deny } = compute_allow_paths(
&common.policy,
sandbox_policy_cwd,
&normalized_policy_cwd,
&common.current_dir,
env_map,
);
@@ -289,8 +293,8 @@ pub(crate) fn prepare_elevated_spawn_context(
};
let sandbox_creds = require_logon_sandbox_creds(
&common.policy,
sandbox_policy_cwd,
cwd,
&normalized_policy_cwd,
&common.current_dir,
env_map,
codex_home,
/*read_roots_override*/ None,
@@ -306,7 +310,7 @@ pub(crate) fn prepare_elevated_spawn_context(
vec![caps.readonly.clone()],
),
SandboxPolicy::WorkspaceWrite { .. } => {
let cap_sid = workspace_cap_sid_for_cwd(codex_home, cwd)?;
let cap_sid = workspace_cap_sid_for_cwd(codex_home, &common.current_dir)?;
(
LocalSid::from_string(&caps.workspace)?,
vec![caps.workspace.clone(), cap_sid],

View File

@@ -7,6 +7,7 @@ use crate::ipc_framed::EmptyPayload;
use crate::ipc_framed::FramedMessage;
use crate::ipc_framed::Message;
use crate::ipc_framed::SpawnRequest;
use crate::path_normalization::execution_path;
use crate::runner_client::spawn_runner_transport;
use crate::spawn_prep::prepare_elevated_spawn_context;
use anyhow::Result;
@@ -39,13 +40,15 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
&mut env_map,
&command,
)?;
let normalized_cwd = elevated.common.current_dir.clone();
let normalized_policy_cwd = execution_path(sandbox_policy_cwd);
let spawn_request = SpawnRequest {
command: command.clone(),
cwd: cwd.to_path_buf(),
cwd: normalized_cwd.clone(),
env: env_map.clone(),
policy_json_or_preset: policy_json_or_preset.to_string(),
sandbox_policy_cwd: sandbox_policy_cwd.to_path_buf(),
sandbox_policy_cwd: normalized_policy_cwd,
codex_home: elevated.common.sandbox_base.clone(),
real_codex_home: codex_home.to_path_buf(),
cap_sids: elevated.cap_sids.clone(),
@@ -55,12 +58,15 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
use_private_desktop,
};
let codex_home = codex_home.to_path_buf();
let cwd = cwd.to_path_buf();
let sandbox_creds = elevated.sandbox_creds.clone();
let logs_base_dir = elevated.common.logs_base_dir.clone();
let transport = tokio::task::spawn_blocking(move || -> Result<_> {
let mut transport =
spawn_runner_transport(&codex_home, &cwd, &sandbox_creds, logs_base_dir.as_deref())?;
let mut transport = spawn_runner_transport(
&codex_home,
&normalized_cwd,
&sandbox_creds,
logs_base_dir.as_deref(),
)?;
transport.send_spawn_request(spawn_request)?;
transport.read_spawn_ready_with_timeout()?;
Ok(transport)