mirror of
https://github.com/openai/codex.git
synced 2026-11-13 06:45:07 +00:00
Propagate executor home directories into sandbox contexts (#41204)
## What changed - Report the executor's user home in environment metadata. - Cache the reported home on selected turn environments and include it in filesystem sandbox contexts, including `apply_patch` requests. - Preserve compatibility with executors that do not report a home directory. ## Testing - Cover local, remote, inherited, serialized, and `apply_patch` sandbox contexts. GitOrigin-RevId: ea65e68e33a80bc9bcfc7c53b1486ab944699470
This commit is contained in:
1 parent
5bf0ba3dd6
commit
d4d2b293b4
11 files changed
+73
-12
No files matched your search
@@ -137,6 +137,7 @@ type PendingConfigurationResult = Result<EnvironmentConfig, String>;
|
||||
struct ResolvedEnvironment {
|
||||
environment: Arc<Environment>,
|
||||
shell: Option<Shell>,
|
||||
user_home_dir: Option<PathUri>,
|
||||
temporary_directories: Option<Vec<PathUri>>,
|
||||
shell_snapshot: ShellSnapshotTask,
|
||||
shell_snapshot_v2_supported: bool,
|
||||
@@ -257,6 +258,7 @@ impl ThreadEnvironments {
|
||||
futures::future::ready(Ok(ResolvedEnvironment {
|
||||
environment: environment.environment,
|
||||
shell: environment.shell,
|
||||
user_home_dir: environment.user_home_dir,
|
||||
temporary_directories: environment.temporary_directories,
|
||||
shell_snapshot: environment.shell_snapshot,
|
||||
shell_snapshot_v2_supported: environment.shell_snapshot_v2_supported,
|
||||
@@ -623,10 +625,10 @@ impl ThreadEnvironments {
|
||||
};
|
||||
// Resolve the attachment only after both prerequisites are ready.
|
||||
let ((), installed_config) = tokio::try_join!(connection_ready, configuration_ready)?;
|
||||
let (shell, temporary_directories, shell_snapshot_v2_supported) = if environment.is_remote()
|
||||
{
|
||||
let (shell, user_home_dir, temporary_dirs, snapshot_v2) = if environment.is_remote() {
|
||||
match environment.info().await {
|
||||
Ok(info) => {
|
||||
let user_home_dir = info.user_home_dir;
|
||||
let temporary_directories = info.temporary_directories;
|
||||
let shell_snapshot_v2_supported = info.capabilities.shell_snapshot_v2;
|
||||
let shell = match Shell::from_environment_shell_info(info.shell) {
|
||||
@@ -638,16 +640,22 @@ impl ThreadEnvironments {
|
||||
None
|
||||
}
|
||||
};
|
||||
(shell, temporary_directories, shell_snapshot_v2_supported)
|
||||
(
|
||||
shell,
|
||||
user_home_dir,
|
||||
temporary_directories,
|
||||
shell_snapshot_v2_supported,
|
||||
)
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!("failed to get info for environment `{environment_id}`: {err}");
|
||||
(None, None, false)
|
||||
(None, None, None, false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(
|
||||
Some(local_shell),
|
||||
PathUri::from_host_native_path("~").ok(),
|
||||
Some(EnvironmentInfo::local_temporary_directories()),
|
||||
cfg!(unix),
|
||||
)
|
||||
@@ -662,9 +670,10 @@ impl ThreadEnvironments {
|
||||
Ok(ResolvedEnvironment {
|
||||
environment,
|
||||
shell,
|
||||
temporary_directories,
|
||||
user_home_dir,
|
||||
temporary_directories: temporary_dirs,
|
||||
shell_snapshot: task,
|
||||
shell_snapshot_v2_supported,
|
||||
shell_snapshot_v2_supported: snapshot_v2,
|
||||
installed_config,
|
||||
})
|
||||
}
|
||||
@@ -742,6 +751,7 @@ impl TurnEnvironmentState {
|
||||
turn_environment.shell_snapshot = environment.shell_snapshot;
|
||||
turn_environment.shell_snapshot_v2_supported =
|
||||
environment.shell_snapshot_v2_supported;
|
||||
turn_environment.user_home_dir = environment.user_home_dir;
|
||||
turn_environment.temporary_directories = environment.temporary_directories;
|
||||
Some(Self::Ready(turn_environment))
|
||||
}
|
||||
@@ -1018,6 +1028,7 @@ mod tests {
|
||||
"id": info["id"],
|
||||
"result": {
|
||||
"shell": { "name": "zsh", "path": "/bin/zsh" },
|
||||
"userHomeDir": "file:///home/remote",
|
||||
"temporaryDirectories": ["file:///tmp/remote"],
|
||||
}
|
||||
})
|
||||
@@ -1145,6 +1156,7 @@ url = "ws://127.0.0.1:8765"
|
||||
|
||||
let snapshot = turn_environments.snapshot().await;
|
||||
let environment = snapshot.primary().expect("local environment");
|
||||
let expected_user_home_dir = PathUri::from_host_native_path("~").ok();
|
||||
let expected_temporary_directories = EnvironmentInfo::local_temporary_directories();
|
||||
|
||||
assert_eq!(environment.shell.as_ref(), Some(&local_shell));
|
||||
@@ -1157,6 +1169,7 @@ url = "ws://127.0.0.1:8765"
|
||||
FileSystemSandboxPolicyContext {
|
||||
cwd: environment.cwd(),
|
||||
workspace_roots: &[],
|
||||
user_home_dir: expected_user_home_dir.as_ref(),
|
||||
temporary_directories: Some(expected_temporary_directories.as_slice()),
|
||||
}
|
||||
);
|
||||
@@ -1472,6 +1485,7 @@ url = "ws://127.0.0.1:8765"
|
||||
);
|
||||
assert_eq!(attached.to_selections(), vec![remote, local]);
|
||||
let environment = attached.primary().expect("remote environment");
|
||||
let expected_user_home_dir = PathUri::parse("file:///home/remote").expect("remote home");
|
||||
let expected_temporary_directories =
|
||||
[PathUri::parse("file:///tmp/remote").expect("remote temporary directory")];
|
||||
assert_eq!(
|
||||
@@ -1482,6 +1496,7 @@ url = "ws://127.0.0.1:8765"
|
||||
FileSystemSandboxPolicyContext {
|
||||
cwd: environment.cwd(),
|
||||
workspace_roots: &[],
|
||||
user_home_dir: Some(&expected_user_home_dir),
|
||||
temporary_directories: Some(expected_temporary_directories.as_slice()),
|
||||
}
|
||||
);
|
||||
@@ -1707,6 +1722,8 @@ url = "ws://127.0.0.1:8765"
|
||||
Arc::clone(&inherited_environment),
|
||||
/*shell*/ None,
|
||||
);
|
||||
inherited.user_home_dir =
|
||||
Some(PathUri::parse("file:///home/inherited").expect("home directory"));
|
||||
inherited.temporary_directories = Some(vec![
|
||||
PathUri::parse("file:///tmp/inherited").expect("temporary directory"),
|
||||
]);
|
||||
@@ -1752,6 +1769,12 @@ url = "ws://127.0.0.1:8765"
|
||||
let inherited = snapshot.primary().expect("inherited environment");
|
||||
assert!(Arc::ptr_eq(&inherited.environment, &inherited_environment));
|
||||
assert_eq!(inherited.config(), &child_config);
|
||||
assert_eq!(
|
||||
inherited
|
||||
.sandbox_context(/*additional_permissions*/ None)
|
||||
.user_home_dir,
|
||||
Some(PathUri::parse("file:///home/inherited").expect("home directory")),
|
||||
);
|
||||
assert_eq!(
|
||||
inherited
|
||||
.sandbox_context(/*additional_permissions*/ None)
|
||||
|
||||
@@ -9297,6 +9297,7 @@ async fn capability_discovery_uses_environment_permission_profile() {
|
||||
permissions: environment.permission_profile().clone().into(),
|
||||
cwd: Some(environment.cwd().clone()),
|
||||
workspace_roots: environment.workspace_roots().to_vec(),
|
||||
user_home_dir: environment.user_home_dir.clone(),
|
||||
temporary_directories: environment.temporary_directories.clone(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::Elevated,
|
||||
windows_sandbox_private_desktop: false,
|
||||
|
||||
@@ -48,6 +48,8 @@ pub(crate) struct TurnEnvironment {
|
||||
pub(crate) selection: TurnEnvironmentSelection,
|
||||
pub(crate) config_origin: EnvironmentConfigOrigin,
|
||||
pub(crate) environment: Arc<Environment>,
|
||||
/// Cached from the selected executor; `None` means it did not report one.
|
||||
pub(crate) user_home_dir: Option<PathUri>,
|
||||
/// Cached from the selected executor; `None` means it did not report them.
|
||||
pub(crate) temporary_directories: Option<Vec<PathUri>>,
|
||||
pub(crate) shell: Option<shell::Shell>,
|
||||
@@ -67,6 +69,7 @@ impl TurnEnvironment {
|
||||
selection,
|
||||
config_origin,
|
||||
environment,
|
||||
user_home_dir: None,
|
||||
temporary_directories: None,
|
||||
shell,
|
||||
shell_snapshot: futures::future::ready(None).boxed().shared(),
|
||||
@@ -131,6 +134,7 @@ impl TurnEnvironment {
|
||||
permissions: permissions.into(),
|
||||
cwd: Some(self.cwd().clone()),
|
||||
workspace_roots: self.workspace_roots().to_vec(),
|
||||
user_home_dir: self.user_home_dir.clone(),
|
||||
temporary_directories: self.temporary_directories.clone(),
|
||||
windows_sandbox_level: executor_windows_sandbox_level(
|
||||
config.windows_sandbox_level,
|
||||
@@ -165,6 +169,7 @@ impl std::fmt::Debug for TurnEnvironment {
|
||||
.field("environment", &self.environment)
|
||||
.field("cwd", &self.selection.cwd)
|
||||
.field("workspace_roots", &self.config().workspace_roots)
|
||||
.field("user_home_dir", &self.user_home_dir)
|
||||
.field("temporary_directories", &self.temporary_directories)
|
||||
.field("shell", &self.shell)
|
||||
.field("config", self.config())
|
||||
|
||||
@@ -100,6 +100,7 @@ impl ApplyPatchRuntime {
|
||||
permissions: permissions.into(),
|
||||
cwd: Some(attempt.sandbox_cwd.clone()),
|
||||
workspace_roots: attempt.workspace_roots.to_vec(),
|
||||
user_home_dir: req.turn_environment.user_home_dir.clone(),
|
||||
temporary_directories: None,
|
||||
windows_sandbox_level: executor_windows_sandbox_level(
|
||||
attempt.windows_sandbox_level,
|
||||
|
||||
@@ -288,7 +288,7 @@ async fn file_system_sandbox_context_respects_sandbox_request() {
|
||||
let path = std::env::temp_dir()
|
||||
.join("apply-patch-runtime-none.txt")
|
||||
.abs();
|
||||
let req = ApplyPatchRequest {
|
||||
let mut req = ApplyPatchRequest {
|
||||
turn_environment: test_turn_environment(codex_exec_server::LOCAL_ENVIRONMENT_ID),
|
||||
action: ApplyPatchAction::new_add_for_test(
|
||||
&PathUri::from_abs_path(&path),
|
||||
@@ -329,6 +329,8 @@ async fn file_system_sandbox_context_respects_sandbox_request() {
|
||||
);
|
||||
|
||||
let cwd = PathUri::parse("file:///C:/workspace").expect("Windows workspace URI");
|
||||
let user_home_dir = PathUri::parse("file:///C:/Users/remote").expect("Windows home URI");
|
||||
req.turn_environment.user_home_dir = Some(user_home_dir.clone());
|
||||
let permissions = PermissionProfile::workspace_write();
|
||||
let attempt = SandboxAttempt {
|
||||
sandbox_requested: true,
|
||||
@@ -345,6 +347,7 @@ async fn file_system_sandbox_context_respects_sandbox_request() {
|
||||
permissions: permissions.into(),
|
||||
cwd: Some(cwd.clone()),
|
||||
workspace_roots: vec![cwd],
|
||||
user_home_dir: Some(user_home_dir),
|
||||
temporary_directories: None,
|
||||
windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
|
||||
windows_sandbox_private_desktop: false,
|
||||
|
||||
@@ -513,6 +513,7 @@ impl<'a> SandboxAttempt<'a> {
|
||||
permissions: exec_server_permissions.into(),
|
||||
cwd: Some(exec_request.windows_sandbox_policy_cwd.clone()),
|
||||
workspace_roots: self.workspace_roots.to_vec(),
|
||||
user_home_dir: None,
|
||||
temporary_directories: None,
|
||||
windows_sandbox_level: executor_windows_sandbox_level(
|
||||
self.windows_sandbox_level,
|
||||
|
||||
@@ -370,6 +370,7 @@ fn exec_server_env_keeps_command_native_and_carries_sandbox_context() {
|
||||
permissions: exec_server_permissions.clone().into(),
|
||||
cwd: Some(cwd_uri.clone()),
|
||||
workspace_roots: vec![cwd_uri.clone()],
|
||||
user_home_dir: None,
|
||||
temporary_directories: None,
|
||||
windows_sandbox_level: if cfg!(windows) {
|
||||
codex_protocol::config_types::WindowsSandboxLevel::RestrictedToken
|
||||
|
||||
Reference in new issue
Block a user