mirror of
https://github.com/openai/codex.git
synced 2026-10-27 18:48:37 +00:00
Handle remote Windows sandbox workspaces
This commit is contained in:
@@ -4,6 +4,7 @@ use crate::ipc_framed::Message;
|
||||
use crate::ipc_framed::SpawnRequest;
|
||||
use crate::ipc_framed::read_frame;
|
||||
use crate::ipc_framed::write_frame;
|
||||
use crate::path_normalization::ensure_sandbox_local_path;
|
||||
use crate::runner_pipe::PIPE_ACCESS_INBOUND;
|
||||
use crate::runner_pipe::PIPE_ACCESS_OUTBOUND;
|
||||
use crate::runner_pipe::connect_pipe;
|
||||
@@ -75,6 +76,7 @@ pub(crate) fn spawn_runner_transport(
|
||||
sandbox_creds: &SandboxCreds,
|
||||
log_dir: Option<&Path>,
|
||||
) -> Result<RunnerTransport> {
|
||||
ensure_sandbox_local_path(cwd, "sandbox workspace")?;
|
||||
let (pipe_in_name, pipe_out_name) = pipe_pair();
|
||||
let h_pipe_in =
|
||||
create_named_pipe(&pipe_in_name, PIPE_ACCESS_OUTBOUND, &sandbox_creds.username)?;
|
||||
|
||||
@@ -38,6 +38,7 @@ mod windows_impl {
|
||||
use crate::logging::log_note;
|
||||
use crate::logging::log_start;
|
||||
use crate::logging::log_success;
|
||||
use crate::path_normalization::ensure_sandbox_local_path;
|
||||
use crate::policy::SandboxPolicy;
|
||||
use crate::policy::parse_policy;
|
||||
use crate::token::convert_string_sid_to_sid;
|
||||
@@ -241,6 +242,7 @@ mod windows_impl {
|
||||
write_roots_override,
|
||||
deny_write_paths_override,
|
||||
} = request;
|
||||
ensure_sandbox_local_path(cwd, "sandbox workspace")?;
|
||||
let policy = parse_policy(policy_json_or_preset)?;
|
||||
normalize_null_device_env(&mut env_map);
|
||||
ensure_non_interactive_pager(&mut env_map);
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
use anyhow::Result;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
#[cfg(target_os = "windows")]
|
||||
use windows_sys::Win32::Storage::FileSystem::DRIVE_REMOTE;
|
||||
#[cfg(target_os = "windows")]
|
||||
use windows_sys::Win32::Storage::FileSystem::GetDriveTypeW;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
use crate::winutil::to_wide;
|
||||
|
||||
pub fn canonicalize_path(path: &Path) -> PathBuf {
|
||||
dunce::canonicalize(path).unwrap_or_else(|_| path.to_path_buf())
|
||||
@@ -12,9 +20,64 @@ pub fn canonical_path_key(path: &Path) -> String {
|
||||
.to_ascii_lowercase()
|
||||
}
|
||||
|
||||
pub fn ensure_sandbox_local_path(path: &Path, context: &str) -> Result<()> {
|
||||
if let Some(kind) = describe_remote_windows_path(path) {
|
||||
anyhow::bail!(
|
||||
"Windows sandbox does not support {kind} for {context}: {}. Use a local checkout or disable sandbox/full-access for this workspace.",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ensure_sandbox_local_paths<'a>(
|
||||
paths: impl IntoIterator<Item = &'a PathBuf>,
|
||||
context: &str,
|
||||
) -> Result<()> {
|
||||
for path in paths {
|
||||
ensure_sandbox_local_path(path, context)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn describe_remote_windows_path(path: &Path) -> Option<&'static str> {
|
||||
let path_str = path.as_os_str().to_string_lossy();
|
||||
if path_str.starts_with(r"\\") || path_str.starts_with("//") {
|
||||
return Some("UNC network-share paths");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if let Some(root) = drive_root(path) {
|
||||
let root_wide = to_wide(root);
|
||||
if unsafe { GetDriveTypeW(root_wide.as_ptr()) } == DRIVE_REMOTE {
|
||||
return Some("mapped network-drive paths");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn drive_root(path: &Path) -> Option<String> {
|
||||
use std::path::Component;
|
||||
use std::path::Prefix;
|
||||
|
||||
match path.components().next()? {
|
||||
Component::Prefix(prefix)
|
||||
if matches!(prefix.kind(), Prefix::Disk(_) | Prefix::VerbatimDisk(_)) =>
|
||||
{
|
||||
Some(format!(r"{}\", prefix.as_os_str().to_string_lossy()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::canonical_path_key;
|
||||
use super::ensure_sandbox_local_path;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::path::Path;
|
||||
|
||||
@@ -23,6 +86,27 @@ 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)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unc_paths_are_rejected_for_sandbox_use() {
|
||||
let err =
|
||||
ensure_sandbox_local_path(Path::new(r"\\server\share\workspace"), "sandbox workspace")
|
||||
.expect_err("UNC path should be rejected");
|
||||
|
||||
assert!(
|
||||
err.to_string()
|
||||
.contains("Windows sandbox does not support UNC network-share paths")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_paths_are_allowed_for_sandbox_use() {
|
||||
ensure_sandbox_local_path(Path::new(r"C:\Users\Dev\Repo"), "sandbox workspace")
|
||||
.expect("local drive path should be allowed");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ use crate::allow::compute_allow_paths;
|
||||
use crate::helper_materialization::helper_bin_dir;
|
||||
use crate::logging::log_note;
|
||||
use crate::path_normalization::canonical_path_key;
|
||||
use crate::path_normalization::ensure_sandbox_local_path;
|
||||
use crate::path_normalization::ensure_sandbox_local_paths;
|
||||
use crate::policy::SandboxPolicy;
|
||||
use crate::setup_error::SetupErrorCode;
|
||||
use crate::setup_error::SetupFailure;
|
||||
@@ -165,7 +167,7 @@ fn run_setup_refresh_inner(
|
||||
) {
|
||||
return Ok(());
|
||||
}
|
||||
let (read_roots, write_roots) = build_payload_roots(&request, &overrides);
|
||||
let (read_roots, write_roots) = build_payload_roots(&request, &overrides)?;
|
||||
let deny_write_paths = build_payload_deny_write_paths(&request, overrides.deny_write_paths);
|
||||
let network_identity =
|
||||
SandboxNetworkIdentity::from_policy(request.policy, request.proxy_enforced);
|
||||
@@ -741,7 +743,7 @@ pub fn run_elevated_setup(
|
||||
format!("failed to create sandbox dir {}: {err}", sbx_dir.display()),
|
||||
)
|
||||
})?;
|
||||
let (read_roots, write_roots) = build_payload_roots(&request, &overrides);
|
||||
let (read_roots, write_roots) = build_payload_roots(&request, &overrides)?;
|
||||
let deny_write_paths = build_payload_deny_write_paths(&request, overrides.deny_write_paths);
|
||||
let network_identity =
|
||||
SandboxNetworkIdentity::from_policy(request.policy, request.proxy_enforced);
|
||||
@@ -772,7 +774,8 @@ pub fn run_elevated_setup(
|
||||
fn build_payload_roots(
|
||||
request: &SandboxSetupRequest<'_>,
|
||||
overrides: &SetupRootOverrides,
|
||||
) -> (Vec<PathBuf>, Vec<PathBuf>) {
|
||||
) -> Result<(Vec<PathBuf>, Vec<PathBuf>)> {
|
||||
ensure_sandbox_local_path(request.command_cwd, "sandbox workspace")?;
|
||||
let write_roots = if let Some(roots) = overrides.write_roots.as_deref() {
|
||||
canonical_existing(roots)
|
||||
} else {
|
||||
@@ -810,7 +813,9 @@ fn build_payload_roots(
|
||||
read_roots = filter_ssh_config_dependency_roots(read_roots);
|
||||
let write_root_set: HashSet<PathBuf> = write_roots.iter().cloned().collect();
|
||||
read_roots.retain(|root| !write_root_set.contains(root));
|
||||
(read_roots, write_roots)
|
||||
ensure_sandbox_local_paths(read_roots.iter(), "sandbox readable roots")?;
|
||||
ensure_sandbox_local_paths(write_roots.iter(), "sandbox writable roots")?;
|
||||
Ok((read_roots, write_roots))
|
||||
}
|
||||
|
||||
fn build_payload_deny_write_paths(
|
||||
@@ -1416,7 +1421,8 @@ mod tests {
|
||||
proxy_enforced: false,
|
||||
},
|
||||
&super::SetupRootOverrides::default(),
|
||||
);
|
||||
)
|
||||
.expect("build payload roots");
|
||||
let expected_helper =
|
||||
dunce::canonicalize(helper_bin_dir(&codex_home)).expect("canonical helper dir");
|
||||
let expected_cwd = dunce::canonicalize(&command_cwd).expect("canonical workspace");
|
||||
@@ -1466,7 +1472,8 @@ mod tests {
|
||||
write_roots: None,
|
||||
deny_write_paths: None,
|
||||
},
|
||||
);
|
||||
)
|
||||
.expect("build payload roots");
|
||||
let expected_helper =
|
||||
dunce::canonicalize(helper_bin_dir(&codex_home)).expect("canonical helper dir");
|
||||
let expected_cwd = dunce::canonicalize(&command_cwd).expect("canonical workspace");
|
||||
@@ -1513,7 +1520,8 @@ mod tests {
|
||||
write_roots: None,
|
||||
deny_write_paths: None,
|
||||
},
|
||||
);
|
||||
)
|
||||
.expect("build payload roots");
|
||||
let expected_helper =
|
||||
dunce::canonicalize(helper_bin_dir(&codex_home)).expect("canonical helper dir");
|
||||
let expected_cwd = dunce::canonicalize(&command_cwd).expect("canonical workspace");
|
||||
|
||||
Reference in New Issue
Block a user