mirror of
https://github.com/openai/codex.git
synced 2026-09-20 12:47:38 +00:00
exec-server: preserve empty workspace roots
This commit is contained in:
@@ -72,11 +72,7 @@ impl FileSystemSandboxRunner {
|
||||
.iter()
|
||||
.map(native_workspace_root)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let workspace_roots = if native_workspace_roots.is_empty() {
|
||||
std::slice::from_ref(&cwd.native)
|
||||
} else {
|
||||
native_workspace_roots.as_slice()
|
||||
};
|
||||
let workspace_roots = native_workspace_roots.as_slice();
|
||||
let native_permissions: PermissionProfile =
|
||||
sandbox.permissions.clone().try_into().map_err(|err| {
|
||||
invalid_request(format!("invalid sandbox permission path URI: {err}"))
|
||||
|
||||
@@ -56,11 +56,7 @@ pub(crate) fn prepare_exec_request(
|
||||
.iter()
|
||||
.map(|root| native_path(root, "sandbox workspace root"))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let workspace_roots = if native_workspace_roots.is_empty() {
|
||||
std::slice::from_ref(&native_sandbox_policy_cwd)
|
||||
} else {
|
||||
native_workspace_roots.as_slice()
|
||||
};
|
||||
let workspace_roots = native_workspace_roots.as_slice();
|
||||
let permissions = permissions.materialize_project_roots_with_workspace_roots(workspace_roots);
|
||||
let managed_mitm_ca_trust_bundle_path = params.managed_network.as_ref().and_then(|_| {
|
||||
CUSTOM_CA_ENV_KEYS.iter().find_map(|key| {
|
||||
|
||||
@@ -13,26 +13,26 @@ use codex_exec_server::ExecOutputStream;
|
||||
use codex_exec_server::ExecParams;
|
||||
use codex_exec_server::ExecProcess;
|
||||
use codex_exec_server::ExecProcessEvent;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
use codex_exec_server::FileSystemSandboxContext;
|
||||
use codex_exec_server::ProcessId;
|
||||
use codex_exec_server::ProcessSignal;
|
||||
use codex_exec_server::ReadResponse;
|
||||
use codex_exec_server::StartedExecProcess;
|
||||
use codex_exec_server::WriteStatus;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||
#[cfg(target_os = "linux")]
|
||||
#[cfg(unix)]
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -240,6 +240,51 @@ async fn remote_tty_process_uses_configured_sandbox_helper_with_hostile_path() -
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn remote_process_preserves_empty_workspace_roots() -> Result<()> {
|
||||
let context = create_process_context(/*use_remote*/ true).await?;
|
||||
let tmp = TempDir::new()?;
|
||||
let file = tmp.path().join("excluded.txt");
|
||||
std::fs::write(&file, b"excluded")?;
|
||||
let cwd = PathUri::from_host_native_path(tmp.path())?;
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
}]);
|
||||
let mut sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
|
||||
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted),
|
||||
cwd.clone(),
|
||||
);
|
||||
sandbox.workspace_roots.clear();
|
||||
|
||||
let session = context
|
||||
.backend
|
||||
.start(ExecParams {
|
||||
process_id: ProcessId::from("proc-empty-workspace-roots"),
|
||||
argv: vec!["/bin/cat".to_string(), file.to_string_lossy().into_owned()],
|
||||
cwd,
|
||||
env_policy: None,
|
||||
env: HashMap::new(),
|
||||
tty: false,
|
||||
pipe_stdin: false,
|
||||
arg0: None,
|
||||
sandbox: Some(sandbox),
|
||||
enforce_managed_network: false,
|
||||
managed_network: None,
|
||||
})
|
||||
.await?;
|
||||
let (stdout, _stderr, exit_code, closed) =
|
||||
collect_process_output_from_events(session.process).await?;
|
||||
|
||||
assert!(!stdout.contains("excluded"), "unexpected stdout: {stdout}");
|
||||
assert_ne!(exit_code, Some(0));
|
||||
assert!(closed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read_process_until_change(
|
||||
session: Arc<dyn ExecProcess>,
|
||||
wake_rx: &mut watch::Receiver<u64>,
|
||||
|
||||
@@ -263,6 +263,35 @@ async fn remote_read_file_materializes_environment_workspace_roots() -> Result<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn remote_read_file_preserves_empty_workspace_roots() -> Result<()> {
|
||||
let context = create_file_system_context(FileSystemImplementation::Remote).await?;
|
||||
let file_system = context.file_system;
|
||||
let tmp = TempDir::new()?;
|
||||
let file = tmp.path().join("excluded.txt");
|
||||
std::fs::write(&file, b"excluded")?;
|
||||
|
||||
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||
},
|
||||
access: FileSystemAccessMode::Read,
|
||||
}]);
|
||||
let mut sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
|
||||
PermissionProfile::from_runtime_permissions(&policy, NetworkSandboxPolicy::Restricted),
|
||||
PathUri::from_host_native_path(tmp.path())?,
|
||||
);
|
||||
sandbox.workspace_roots.clear();
|
||||
|
||||
let error = file_system
|
||||
.read_file(&PathUri::from_host_native_path(&file)?, Some(&sandbox))
|
||||
.await
|
||||
.expect_err("empty workspace roots should not grant cwd access");
|
||||
assert_sandbox_denied(&error);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(FileSystemImplementation::Local ; "local")]
|
||||
#[test_case(FileSystemImplementation::Remote ; "remote")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
|
||||
@@ -171,10 +171,11 @@ impl FileSystemSandboxContext {
|
||||
permissions: PermissionProfile<AbsolutePathBuf>,
|
||||
cwd: Option<PathUri>,
|
||||
) -> Self {
|
||||
let workspace_roots = cwd.iter().cloned().collect();
|
||||
Self {
|
||||
permissions: permissions.into(),
|
||||
cwd,
|
||||
workspace_roots: Vec::new(),
|
||||
workspace_roots,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
use_legacy_landlock: false,
|
||||
|
||||
Reference in New Issue
Block a user