mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
sandboxing: migrate cwd inputs to PathUri (#27816)
## Why Sandbox cwd values can cross app-server and exec-server host boundaries. They should retain URI semantics until the receiving host validates them instead of being interpreted early as native paths. ## What - Carry `PathUri` through filesystem sandbox contexts, sandbox commands, and transform inputs. - Convert command and policy cwd once in `SandboxManager::transform`, then keep launch requests native. - Preserve sandbox cwd over remote filesystem transport and reject non-native URIs without fallback. - Cache paired native/URI turn-environment cwd values during migration, with immutable access to keep them synchronized. - Extend existing protocol, forwarding, transform, and core runtime tests.
This commit is contained in:
@@ -57,7 +57,7 @@ pub(crate) async fn load_project_instructions(
|
||||
config,
|
||||
filesystem.as_ref(),
|
||||
&turn_environment.environment_id,
|
||||
&turn_environment.cwd,
|
||||
turn_environment.cwd(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
||||
@@ -258,14 +258,17 @@ fn resolved_local_environments<const N: usize>(
|
||||
ResolvedTurnEnvironments {
|
||||
turn_environments: environments
|
||||
.into_iter()
|
||||
.map(|(environment_id, cwd)| TurnEnvironment {
|
||||
environment_id: environment_id.to_string(),
|
||||
environment: Arc::new(
|
||||
Environment::create_for_tests(/*exec_server_url*/ None)
|
||||
.expect("local environment"),
|
||||
),
|
||||
cwd,
|
||||
shell: None,
|
||||
.map(|(environment_id, cwd)| {
|
||||
TurnEnvironment::new(
|
||||
environment_id.to_string(),
|
||||
Arc::new(
|
||||
Environment::create_for_tests(/*exec_server_url*/ None)
|
||||
.expect("local environment"),
|
||||
),
|
||||
cwd,
|
||||
/*shell*/ None,
|
||||
)
|
||||
.expect("local cwd URI")
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ impl EnvironmentContextEnvironment {
|
||||
.iter()
|
||||
.map(|environment| Self {
|
||||
id: environment.environment_id.clone(),
|
||||
cwd: environment.cwd.clone(),
|
||||
cwd: environment.cwd().clone(),
|
||||
shell: environment
|
||||
.shell
|
||||
.as_ref()
|
||||
|
||||
@@ -58,7 +58,7 @@ impl ResolvedTurnEnvironments {
|
||||
return None;
|
||||
};
|
||||
|
||||
(!environment.environment.is_remote()).then_some(&environment.cwd)
|
||||
(!environment.environment.is_remote()).then_some(environment.cwd())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,12 +96,12 @@ pub(crate) async fn resolve_environment_selections(
|
||||
None
|
||||
}
|
||||
};
|
||||
turn_environments.push(TurnEnvironment {
|
||||
turn_environments.push(TurnEnvironment::new(
|
||||
environment_id,
|
||||
environment,
|
||||
cwd: selected_environment.cwd.clone(),
|
||||
selected_environment.cwd.clone(),
|
||||
shell,
|
||||
});
|
||||
)?);
|
||||
}
|
||||
Ok(ResolvedTurnEnvironments { turn_environments })
|
||||
}
|
||||
@@ -270,22 +270,26 @@ url = "ws://127.0.0.1:8765"
|
||||
.expect("remote environment"),
|
||||
);
|
||||
let remote = ResolvedTurnEnvironments {
|
||||
turn_environments: vec![TurnEnvironment {
|
||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||
environment: remote_environment.clone(),
|
||||
cwd: cwd.clone(),
|
||||
shell: None,
|
||||
}],
|
||||
turn_environments: vec![
|
||||
TurnEnvironment::new(
|
||||
REMOTE_ENVIRONMENT_ID.to_string(),
|
||||
remote_environment.clone(),
|
||||
cwd.clone(),
|
||||
/*shell*/ None,
|
||||
)
|
||||
.expect("remote cwd URI"),
|
||||
],
|
||||
};
|
||||
let multiple = ResolvedTurnEnvironments {
|
||||
turn_environments: vec![
|
||||
local.primary().expect("local environment").clone(),
|
||||
TurnEnvironment {
|
||||
environment_id: REMOTE_ENVIRONMENT_ID.to_string(),
|
||||
environment: remote_environment,
|
||||
cwd: cwd.clone(),
|
||||
shell: None,
|
||||
},
|
||||
TurnEnvironment::new(
|
||||
REMOTE_ENVIRONMENT_ID.to_string(),
|
||||
remote_environment,
|
||||
cwd.clone(),
|
||||
/*shell*/ None,
|
||||
)
|
||||
.expect("remote cwd URI"),
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@ use codex_sandboxing::SandboxType;
|
||||
use codex_sandboxing::SandboxablePreference;
|
||||
use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use codex_utils_pty::DEFAULT_OUTPUT_BYTES_CAP;
|
||||
use codex_utils_pty::process_group::kill_child_process_group;
|
||||
|
||||
@@ -372,6 +373,14 @@ pub fn build_exec_request(
|
||||
"command args are empty",
|
||||
))
|
||||
})?;
|
||||
let cwd = PathUri::from_abs_path(&cwd).map_err(|_| {
|
||||
CodexErr::InvalidRequest("command cwd cannot be represented as a file URI".to_string())
|
||||
})?;
|
||||
let sandbox_policy_cwd_uri = PathUri::from_abs_path(sandbox_cwd).map_err(|_| {
|
||||
CodexErr::InvalidRequest(
|
||||
"sandbox policy cwd cannot be represented as a file URI".to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
let manager = SandboxManager::new();
|
||||
let command = SandboxCommand {
|
||||
@@ -392,24 +401,21 @@ pub fn build_exec_request(
|
||||
sandbox: sandbox_type,
|
||||
enforce_managed_network,
|
||||
network: network.as_ref(),
|
||||
sandbox_policy_cwd: sandbox_cwd,
|
||||
sandbox_policy_cwd: &sandbox_policy_cwd_uri,
|
||||
codex_linux_sandbox_exe: codex_linux_sandbox_exe.as_deref(),
|
||||
use_legacy_landlock,
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_private_desktop,
|
||||
})
|
||||
.map(|request| {
|
||||
let windows_sandbox_policy_cwd = AbsolutePathBuf::try_from(sandbox_cwd.to_path_buf())
|
||||
.unwrap_or_else(|_| request.cwd.clone());
|
||||
let windows_sandbox_workspace_roots = if windows_sandbox_workspace_roots.is_empty() {
|
||||
vec![windows_sandbox_policy_cwd.clone()]
|
||||
vec![request.sandbox_policy_cwd.clone()]
|
||||
} else {
|
||||
windows_sandbox_workspace_roots.to_vec()
|
||||
};
|
||||
ExecRequest::from_sandbox_exec_request(
|
||||
request,
|
||||
options,
|
||||
windows_sandbox_policy_cwd,
|
||||
windows_sandbox_workspace_roots,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -743,7 +743,7 @@ async fn run_review_on_session(
|
||||
.parent_turn
|
||||
.environments
|
||||
.primary()
|
||||
.map(|environment| environment.cwd.clone())
|
||||
.map(|environment| environment.cwd().clone())
|
||||
.unwrap_or_else(|| params.parent_turn.config.cwd.clone());
|
||||
|
||||
let submit_result = run_before_review_deadline(
|
||||
|
||||
@@ -103,12 +103,12 @@ impl ExecRequest {
|
||||
pub(crate) fn from_sandbox_exec_request(
|
||||
request: SandboxExecRequest,
|
||||
options: ExecOptions,
|
||||
windows_sandbox_policy_cwd: AbsolutePathBuf,
|
||||
windows_sandbox_workspace_roots: Vec<AbsolutePathBuf>,
|
||||
) -> Self {
|
||||
let SandboxExecRequest {
|
||||
command,
|
||||
cwd,
|
||||
sandbox_policy_cwd: windows_sandbox_policy_cwd,
|
||||
mut env,
|
||||
network,
|
||||
sandbox,
|
||||
|
||||
@@ -314,7 +314,7 @@ impl Session {
|
||||
let mcp_runtime_context = match turn_context.environments.primary() {
|
||||
Some(turn_environment) => McpRuntimeContext::new(
|
||||
Arc::clone(&self.services.environment_manager),
|
||||
turn_environment.cwd.to_path_buf(),
|
||||
turn_environment.cwd().to_path_buf(),
|
||||
),
|
||||
None => McpRuntimeContext::new(
|
||||
Arc::clone(&self.services.environment_manager),
|
||||
|
||||
@@ -1135,7 +1135,7 @@ impl Session {
|
||||
let mcp_runtime_context = match turn_environment {
|
||||
Some(turn_environment) => McpRuntimeContext::new(
|
||||
Arc::clone(&sess.services.environment_manager),
|
||||
turn_environment.cwd.to_path_buf(),
|
||||
turn_environment.cwd().to_path_buf(),
|
||||
),
|
||||
None => McpRuntimeContext::new(
|
||||
Arc::clone(&sess.services.environment_manager),
|
||||
|
||||
@@ -4035,12 +4035,15 @@ fn turn_environments_for_tests(
|
||||
cwd: &codex_utils_absolute_path::AbsolutePathBuf,
|
||||
) -> crate::environment_selection::ResolvedTurnEnvironments {
|
||||
crate::environment_selection::ResolvedTurnEnvironments {
|
||||
turn_environments: vec![TurnEnvironment {
|
||||
environment_id: codex_exec_server::LOCAL_ENVIRONMENT_ID.to_string(),
|
||||
environment: Arc::clone(environment),
|
||||
cwd: cwd.clone(),
|
||||
shell: None,
|
||||
}],
|
||||
turn_environments: vec![
|
||||
TurnEnvironment::new(
|
||||
codex_exec_server::LOCAL_ENVIRONMENT_ID.to_string(),
|
||||
Arc::clone(environment),
|
||||
cwd.clone(),
|
||||
/*shell*/ None,
|
||||
)
|
||||
.expect("turn environment"),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5656,8 +5659,14 @@ async fn request_permissions_tool_resolves_relative_paths_against_selected_envir
|
||||
mcp_elicitations: true,
|
||||
}))
|
||||
.expect("test setup should allow updating approval policy");
|
||||
turn_context_mut.environments.turn_environments[0].environment_id = "remote".to_string();
|
||||
turn_context_mut.environments.turn_environments[0].cwd = environment_cwd.clone();
|
||||
let current_environment = turn_context_mut.environments.turn_environments[0].clone();
|
||||
turn_context_mut.environments.turn_environments[0] = TurnEnvironment::new(
|
||||
"remote".to_string(),
|
||||
current_environment.environment,
|
||||
environment_cwd.clone(),
|
||||
current_environment.shell,
|
||||
)
|
||||
.expect("environment cwd URI");
|
||||
|
||||
let call_id = "call-1".to_string();
|
||||
let handler = RequestPermissionsHandler;
|
||||
@@ -6246,15 +6255,15 @@ async fn primary_environment_uses_first_turn_environment() {
|
||||
let first_environment = turn_context.environments.turn_environments[0].clone();
|
||||
#[allow(deprecated)]
|
||||
let second_cwd = turn_context.cwd.join("second");
|
||||
turn_context
|
||||
.environments
|
||||
.turn_environments
|
||||
.push(TurnEnvironment {
|
||||
environment_id: "second".to_string(),
|
||||
environment: Arc::clone(&first_environment.environment),
|
||||
cwd: second_cwd.clone(),
|
||||
shell: None,
|
||||
});
|
||||
turn_context.environments.turn_environments.push(
|
||||
TurnEnvironment::new(
|
||||
"second".to_string(),
|
||||
Arc::clone(&first_environment.environment),
|
||||
second_cwd.clone(),
|
||||
/*shell*/ None,
|
||||
)
|
||||
.expect("turn environment"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
turn_context
|
||||
@@ -6271,13 +6280,13 @@ async fn primary_environment_uses_first_turn_environment() {
|
||||
.iter()
|
||||
.find(|environment| environment.environment_id == "second")
|
||||
.expect("second environment")
|
||||
.cwd,
|
||||
second_cwd
|
||||
.cwd(),
|
||||
&second_cwd
|
||||
);
|
||||
assert_eq!(turn_context.environments.turn_environments.len(), 2);
|
||||
assert_eq!(
|
||||
turn_context.environments.turn_environments[1].cwd,
|
||||
second_cwd
|
||||
turn_context.environments.turn_environments[1].cwd(),
|
||||
&second_cwd
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -416,10 +416,10 @@ async fn turn_diff_display_roots(turn_context: &TurnContext) -> Vec<(String, Pat
|
||||
for turn_environment in &turn_context.environments.turn_environments {
|
||||
let root = get_git_repo_root_with_fs(
|
||||
turn_environment.environment.get_filesystem().as_ref(),
|
||||
&turn_environment.cwd,
|
||||
turn_environment.cwd(),
|
||||
)
|
||||
.await
|
||||
.unwrap_or_else(|| turn_environment.cwd.clone())
|
||||
.unwrap_or_else(|| turn_environment.cwd().clone())
|
||||
.into_path_buf();
|
||||
display_roots.push((turn_environment.environment_id.clone(), root));
|
||||
}
|
||||
@@ -630,7 +630,7 @@ async fn build_extension_turn_input_items(
|
||||
.enumerate()
|
||||
.map(|(index, environment)| TurnInputEnvironment {
|
||||
environment_id: environment.environment_id.clone(),
|
||||
cwd: environment.cwd.as_path().to_path_buf(),
|
||||
cwd: environment.cwd().as_path().to_path_buf(),
|
||||
is_primary: index == 0,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
@@ -17,6 +17,7 @@ use codex_protocol::protocol::TurnEnvironmentSelection;
|
||||
use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
|
||||
use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;
|
||||
use codex_sandboxing::policy_transforms::effective_network_sandbox_policy;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
@@ -39,11 +40,45 @@ impl TurnSkillsContext {
|
||||
pub(crate) struct TurnEnvironment {
|
||||
pub(crate) environment_id: String,
|
||||
pub(crate) environment: Arc<Environment>,
|
||||
pub(crate) cwd: AbsolutePathBuf,
|
||||
// Keep both representations together while cwd consumers migrate to URI semantics. Keeping
|
||||
// them synchronized means neither representation can be exposed through a mutable reference;
|
||||
// updates must rebuild the validated pair through `TurnEnvironment::new`. Once
|
||||
// `TurnEnvironment::cwd` itself becomes a `PathUri`, convert only at native filesystem and
|
||||
// process-launch boundaries and remove this paired migration state.
|
||||
cwd: AbsolutePathBuf,
|
||||
cwd_uri: PathUri,
|
||||
pub(crate) shell: Option<shell::Shell>,
|
||||
}
|
||||
|
||||
impl TurnEnvironment {
|
||||
pub(crate) fn new(
|
||||
environment_id: String,
|
||||
environment: Arc<Environment>,
|
||||
cwd: AbsolutePathBuf,
|
||||
shell: Option<shell::Shell>,
|
||||
) -> CodexResult<Self> {
|
||||
let cwd_uri = PathUri::from_abs_path(&cwd).map_err(|_| {
|
||||
CodexErr::InvalidRequest(
|
||||
"turn environment cwd cannot be represented as a file URI".to_string(),
|
||||
)
|
||||
})?;
|
||||
Ok(Self {
|
||||
environment_id,
|
||||
environment,
|
||||
cwd,
|
||||
cwd_uri,
|
||||
shell,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn cwd(&self) -> &AbsolutePathBuf {
|
||||
&self.cwd
|
||||
}
|
||||
|
||||
pub(crate) fn cwd_uri(&self) -> &PathUri {
|
||||
&self.cwd_uri
|
||||
}
|
||||
|
||||
pub(crate) fn selection(&self) -> TurnEnvironmentSelection {
|
||||
TurnEnvironmentSelection {
|
||||
environment_id: self.environment_id.clone(),
|
||||
@@ -291,7 +326,7 @@ impl TurnContext {
|
||||
pub(crate) fn file_system_sandbox_context(
|
||||
&self,
|
||||
additional_permissions: Option<AdditionalPermissionProfile>,
|
||||
cwd: &AbsolutePathBuf,
|
||||
cwd: &PathUri,
|
||||
) -> FileSystemSandboxContext {
|
||||
let (base_file_system_sandbox_policy, base_network_sandbox_policy) =
|
||||
self.permission_profile.to_runtime_permissions();
|
||||
@@ -712,7 +747,7 @@ impl Session {
|
||||
let primary_turn_environment = turn_environments.primary().cloned();
|
||||
let cwd = primary_turn_environment
|
||||
.as_ref()
|
||||
.map(|turn_environment| turn_environment.cwd.clone())
|
||||
.map(|turn_environment| turn_environment.cwd().clone())
|
||||
.unwrap_or_else(|| session_configuration.cwd().clone());
|
||||
let per_turn_config = Self::build_per_turn_config(&session_configuration, cwd.clone());
|
||||
{
|
||||
|
||||
@@ -648,12 +648,12 @@ async fn resume_and_fork_do_not_restore_thread_environments_from_rollout() {
|
||||
.expect("build resumed turn context");
|
||||
assert_eq!(resumed_turn.environments.turn_environments.len(), 1);
|
||||
assert_eq!(
|
||||
resumed_turn.environments.turn_environments[0].cwd,
|
||||
default_cwd
|
||||
resumed_turn.environments.turn_environments[0].cwd(),
|
||||
&default_cwd
|
||||
);
|
||||
assert_ne!(
|
||||
resumed_turn.environments.turn_environments[0].cwd,
|
||||
selected_cwd
|
||||
resumed_turn.environments.turn_environments[0].cwd(),
|
||||
&selected_cwd
|
||||
);
|
||||
|
||||
let forked = manager
|
||||
@@ -675,12 +675,12 @@ async fn resume_and_fork_do_not_restore_thread_environments_from_rollout() {
|
||||
.expect("build forked turn context");
|
||||
assert_eq!(forked_turn.environments.turn_environments.len(), 1);
|
||||
assert_eq!(
|
||||
forked_turn.environments.turn_environments[0].cwd,
|
||||
default_cwd
|
||||
forked_turn.environments.turn_environments[0].cwd(),
|
||||
&default_cwd
|
||||
);
|
||||
assert_ne!(
|
||||
forked_turn.environments.turn_environments[0].cwd,
|
||||
selected_cwd
|
||||
forked_turn.environments.turn_environments[0].cwd(),
|
||||
&selected_cwd
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -312,5 +312,5 @@ fn single_local_environment_cwd(turn: &TurnContext) -> Result<&AbsolutePathBuf,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(&turn_environment.cwd)
|
||||
Ok(turn_environment.cwd())
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ use codex_sandboxing::policy_transforms::normalize_additional_permissions;
|
||||
use codex_tools::ToolName;
|
||||
use codex_tools::ToolSpec;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
|
||||
const APPLY_PATCH_ARGUMENT_DIFF_BUFFER_INTERVAL: Duration = Duration::from_millis(500);
|
||||
/// Handles freeform `apply_patch` requests and routes verified patches to the
|
||||
@@ -358,9 +359,12 @@ impl ApplyPatchHandler {
|
||||
"apply_patch is unavailable in this session".to_string(),
|
||||
));
|
||||
};
|
||||
let cwd = turn_environment.cwd.clone();
|
||||
let cwd = turn_environment.cwd().clone();
|
||||
let fs = turn_environment.environment.get_filesystem();
|
||||
let sandbox = turn.file_system_sandbox_context(/*additional_permissions*/ None, &cwd);
|
||||
let sandbox = turn.file_system_sandbox_context(
|
||||
/*additional_permissions*/ None,
|
||||
turn_environment.cwd_uri(),
|
||||
);
|
||||
match codex_apply_patch::verify_apply_patch_args(args, &cwd, fs.as_ref(), Some(&sandbox))
|
||||
.await
|
||||
{
|
||||
@@ -522,7 +526,14 @@ pub(crate) async fn intercept_apply_patch(
|
||||
call_id: &str,
|
||||
tool_name: &str,
|
||||
) -> Result<Option<FunctionToolOutput>, FunctionCallError> {
|
||||
let sandbox = turn.file_system_sandbox_context(/*additional_permissions*/ None, cwd);
|
||||
let sandbox_cwd = PathUri::from_abs_path(cwd).map_err(|_| {
|
||||
FunctionCallError::RespondToModel(
|
||||
"unable to prepare filesystem sandbox: working directory cannot be represented as a file URI"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
let sandbox =
|
||||
turn.file_system_sandbox_context(/*additional_permissions*/ None, &sandbox_cwd);
|
||||
match codex_apply_patch::maybe_parse_apply_patch_verified(command, cwd, fs, Some(&sandbox))
|
||||
.await
|
||||
{
|
||||
|
||||
@@ -117,19 +117,20 @@ async fn to_extension_call(invocation: &ToolInvocation) -> ExtensionToolCall {
|
||||
let additional_permissions = apply_granted_turn_permissions(
|
||||
invocation.session.as_ref(),
|
||||
&environment.environment_id,
|
||||
environment.cwd.as_path(),
|
||||
environment.cwd().as_path(),
|
||||
SandboxPermissions::UseDefault,
|
||||
/*additional_permissions*/ None,
|
||||
)
|
||||
.await
|
||||
.additional_permissions;
|
||||
let file_system_sandbox_context = invocation
|
||||
.turn
|
||||
.file_system_sandbox_context(additional_permissions, environment.cwd_uri());
|
||||
environments.push(ToolEnvironment {
|
||||
environment_id: environment.environment_id.clone(),
|
||||
cwd: environment.cwd.clone(),
|
||||
cwd: environment.cwd().clone(),
|
||||
file_system: environment.environment.get_filesystem(),
|
||||
file_system_sandbox_context: invocation
|
||||
.turn
|
||||
.file_system_sandbox_context(additional_permissions, &environment.cwd),
|
||||
file_system_sandbox_context,
|
||||
});
|
||||
}
|
||||
ExtensionToolCall {
|
||||
@@ -310,6 +311,12 @@ mod tests {
|
||||
let turn_id = turn.sub_id.clone();
|
||||
let model = turn.model_info.slug.clone();
|
||||
let truncation_policy = turn.truncation_policy;
|
||||
let expected_sandbox_cwds = turn
|
||||
.environments
|
||||
.turn_environments
|
||||
.iter()
|
||||
.map(|environment| Some(environment.cwd_uri().clone()))
|
||||
.collect::<Vec<_>>();
|
||||
let history_item = ResponseItem::Message {
|
||||
id: None,
|
||||
role: "user".to_string(),
|
||||
@@ -354,6 +361,14 @@ mod tests {
|
||||
);
|
||||
assert_eq!(captured_call.model, model);
|
||||
assert_eq!(captured_call.truncation_policy, truncation_policy);
|
||||
assert_eq!(
|
||||
captured_call
|
||||
.environments
|
||||
.iter()
|
||||
.map(|environment| environment.file_system_sandbox_context.cwd.clone())
|
||||
.collect::<Vec<_>>(),
|
||||
expected_sandbox_cwds
|
||||
);
|
||||
assert_eq!(
|
||||
captured_call.conversation_history.items(),
|
||||
std::slice::from_ref(&history_item)
|
||||
|
||||
@@ -71,7 +71,7 @@ impl RequestPermissionsHandler {
|
||||
));
|
||||
};
|
||||
let mut args: RequestPermissionsArgs =
|
||||
parse_arguments_with_base_path(&arguments, &turn_environment.cwd)?;
|
||||
parse_arguments_with_base_path(&arguments, turn_environment.cwd())?;
|
||||
args.permissions = normalize_additional_permissions(args.permissions.into())
|
||||
.map(codex_protocol::request_permissions::RequestPermissionProfile::from)
|
||||
.map_err(FunctionCallError::RespondToModel)?;
|
||||
|
||||
@@ -134,8 +134,8 @@ impl ExecCommandHandler {
|
||||
.as_deref()
|
||||
.filter(|workdir| !workdir.is_empty())
|
||||
.map_or_else(
|
||||
|| turn_environment.cwd.clone(),
|
||||
|workdir| turn_environment.cwd.join(workdir),
|
||||
|| turn_environment.cwd().clone(),
|
||||
|workdir| turn_environment.cwd().join(workdir),
|
||||
);
|
||||
let environment = Arc::clone(&turn_environment.environment);
|
||||
let fs = environment.get_filesystem();
|
||||
@@ -270,7 +270,7 @@ impl ExecCommandHandler {
|
||||
yield_time_ms,
|
||||
max_output_tokens,
|
||||
cwd,
|
||||
sandbox_cwd: turn_environment.cwd.clone(),
|
||||
sandbox_cwd: turn_environment.cwd().clone(),
|
||||
environment,
|
||||
shell_mode,
|
||||
network: context.turn.network.clone(),
|
||||
|
||||
@@ -143,9 +143,12 @@ impl ViewImageHandler {
|
||||
"view_image is unavailable in this session".to_string(),
|
||||
));
|
||||
};
|
||||
let cwd = turn_environment.cwd.clone();
|
||||
let cwd = turn_environment.cwd().clone();
|
||||
let abs_path = cwd.join(path);
|
||||
let sandbox = turn.file_system_sandbox_context(/*additional_permissions*/ None, &cwd);
|
||||
let sandbox = turn.file_system_sandbox_context(
|
||||
/*additional_permissions*/ None,
|
||||
turn_environment.cwd_uri(),
|
||||
);
|
||||
let fs = turn_environment.environment.get_filesystem();
|
||||
let path_uri = PathUri::from_abs_path(&abs_path).map_err(|error| {
|
||||
FunctionCallError::RespondToModel(format!(
|
||||
@@ -268,16 +271,34 @@ impl ToolOutput for ViewImageOutput {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::session::tests::make_session_and_context;
|
||||
use crate::session::turn_context::TurnEnvironment;
|
||||
use crate::tools::context::ToolCallSource;
|
||||
use crate::tools::context::ToolInvocation;
|
||||
use crate::turn_diff_tracker::TurnDiffTracker;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use core_test_support::TempDirExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
fn replace_primary_environment_cwd(turn: &mut crate::TurnContext, cwd: AbsolutePathBuf) {
|
||||
let current = turn
|
||||
.environments
|
||||
.turn_environments
|
||||
.first()
|
||||
.cloned()
|
||||
.expect("default local turn environment");
|
||||
turn.environments.turn_environments[0] = TurnEnvironment::new(
|
||||
current.environment_id,
|
||||
current.environment,
|
||||
cwd,
|
||||
current.shell,
|
||||
)
|
||||
.expect("image cwd URI");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn log_preview_omits_image_data() {
|
||||
let output = ViewImageOutput {
|
||||
@@ -314,11 +335,7 @@ mod tests {
|
||||
let image_dir = tempfile::tempdir().expect("create image temp dir");
|
||||
let image_cwd = image_dir.abs();
|
||||
|
||||
turn.environments
|
||||
.turn_environments
|
||||
.first_mut()
|
||||
.expect("default local turn environment")
|
||||
.cwd = image_cwd.clone();
|
||||
replace_primary_environment_cwd(&mut turn, image_cwd.clone());
|
||||
let image_path = image_cwd.join("image.png");
|
||||
std::fs::write(image_path.as_path(), b"not a real image").expect("write test image");
|
||||
turn.permission_profile = PermissionProfile::read_only();
|
||||
@@ -381,11 +398,7 @@ mod tests {
|
||||
let image_dir = tempfile::tempdir().expect("create image temp dir");
|
||||
let image_cwd = image_dir.abs();
|
||||
|
||||
turn.environments
|
||||
.turn_environments
|
||||
.first_mut()
|
||||
.expect("default local turn environment")
|
||||
.cwd = image_cwd.clone();
|
||||
replace_primary_environment_cwd(&mut turn, image_cwd.clone());
|
||||
let image_path = image_cwd.join("image.png");
|
||||
std::fs::write(image_path.as_path(), b"not a real image").expect("write test image");
|
||||
turn.permission_profile = PermissionProfile::Disabled;
|
||||
|
||||
@@ -39,6 +39,7 @@ use codex_protocol::protocol::NetworkPolicyRuleAction;
|
||||
use codex_protocol::protocol::ReviewDecision;
|
||||
use codex_sandboxing::SandboxManager;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use std::time::Instant;
|
||||
|
||||
pub(crate) struct ToolOrchestrator {
|
||||
@@ -239,13 +240,18 @@ impl ToolOrchestrator {
|
||||
let use_legacy_landlock = turn_ctx.features.use_legacy_landlock();
|
||||
#[allow(deprecated)]
|
||||
let sandbox_cwd = tool.sandbox_cwd(req).unwrap_or(&turn_ctx.cwd);
|
||||
let sandbox_policy_cwd = PathUri::from_abs_path(sandbox_cwd).map_err(|_| {
|
||||
ToolError::Codex(CodexErr::InvalidRequest(
|
||||
"sandbox policy cwd cannot be represented as a file URI".to_string(),
|
||||
))
|
||||
})?;
|
||||
let workspace_roots = turn_ctx.config.effective_workspace_roots();
|
||||
let initial_attempt = SandboxAttempt {
|
||||
sandbox: initial_sandbox,
|
||||
permissions: &turn_ctx.permission_profile,
|
||||
enforce_managed_network: managed_network_active,
|
||||
manager: &self.sandbox,
|
||||
sandbox_cwd,
|
||||
sandbox_cwd: &sandbox_policy_cwd,
|
||||
workspace_roots: workspace_roots.as_slice(),
|
||||
codex_linux_sandbox_exe: turn_ctx.codex_linux_sandbox_exe.as_ref(),
|
||||
use_legacy_landlock,
|
||||
@@ -418,7 +424,7 @@ impl ToolOrchestrator {
|
||||
permissions: &turn_ctx.permission_profile,
|
||||
enforce_managed_network: managed_network_active,
|
||||
manager: &self.sandbox,
|
||||
sandbox_cwd,
|
||||
sandbox_cwd: &sandbox_policy_cwd,
|
||||
workspace_roots: workspace_roots.as_slice(),
|
||||
codex_linux_sandbox_exe: retry_codex_linux_sandbox_exe,
|
||||
use_legacy_landlock,
|
||||
|
||||
@@ -11,16 +11,18 @@ use codex_sandboxing::SandboxManager;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_sandboxing::policy_transforms::effective_file_system_sandbox_policy;
|
||||
use codex_sandboxing::policy_transforms::effective_network_sandbox_policy;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use core_test_support::PathBufExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::HashMap;
|
||||
fn test_turn_environment(environment_id: &str) -> crate::session::turn_context::TurnEnvironment {
|
||||
crate::session::turn_context::TurnEnvironment {
|
||||
environment_id: environment_id.to_string(),
|
||||
environment: std::sync::Arc::new(codex_exec_server::Environment::default_for_tests()),
|
||||
cwd: std::env::temp_dir().abs(),
|
||||
shell: None,
|
||||
}
|
||||
crate::session::turn_context::TurnEnvironment::new(
|
||||
environment_id.to_string(),
|
||||
std::sync::Arc::new(codex_exec_server::Environment::default_for_tests()),
|
||||
std::env::temp_dir().abs(),
|
||||
/*shell*/ None,
|
||||
)
|
||||
.expect("turn environment")
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -206,12 +208,13 @@ async fn file_system_sandbox_context_uses_active_attempt() {
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
);
|
||||
let manager = SandboxManager::new();
|
||||
let sandbox_policy_cwd = PathUri::from_abs_path(&path).expect("path URI");
|
||||
let attempt = SandboxAttempt {
|
||||
sandbox: SandboxType::MacosSeatbelt,
|
||||
permissions: &permissions,
|
||||
enforce_managed_network: false,
|
||||
manager: &manager,
|
||||
sandbox_cwd: &path,
|
||||
sandbox_cwd: &sandbox_policy_cwd,
|
||||
workspace_roots: std::slice::from_ref(&path),
|
||||
codex_linux_sandbox_exe: None,
|
||||
use_legacy_landlock: true,
|
||||
@@ -232,7 +235,10 @@ async fn file_system_sandbox_context_uses_active_attempt() {
|
||||
let expected_permissions =
|
||||
PermissionProfile::from_runtime_permissions(&file_system_policy, network_policy);
|
||||
assert_eq!(sandbox.permissions, expected_permissions);
|
||||
assert_eq!(sandbox.cwd, Some(path.clone()));
|
||||
assert_eq!(
|
||||
sandbox.cwd,
|
||||
Some(codex_utils_path_uri::PathUri::from_abs_path(&path).expect("path URI"))
|
||||
);
|
||||
assert_eq!(
|
||||
sandbox.windows_sandbox_level,
|
||||
WindowsSandboxLevel::RestrictedToken
|
||||
@@ -260,12 +266,13 @@ async fn no_sandbox_attempt_has_no_file_system_context() {
|
||||
};
|
||||
let permissions = PermissionProfile::Disabled;
|
||||
let manager = SandboxManager::new();
|
||||
let sandbox_policy_cwd = PathUri::from_abs_path(&path).expect("path URI");
|
||||
let attempt = SandboxAttempt {
|
||||
sandbox: SandboxType::None,
|
||||
permissions: &permissions,
|
||||
enforce_managed_network: false,
|
||||
manager: &manager,
|
||||
sandbox_cwd: &path,
|
||||
sandbox_cwd: &sandbox_policy_cwd,
|
||||
workspace_roots: std::slice::from_ref(&path),
|
||||
codex_linux_sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
|
||||
@@ -21,10 +21,12 @@ use codex_network_proxy::PROXY_ENV_KEYS;
|
||||
use codex_network_proxy::PROXY_GIT_SSH_COMMAND_ENV_KEY;
|
||||
use codex_network_proxy::is_managed_mitm_ca_trust_bundle_path;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::error::CodexErr;
|
||||
use codex_protocol::models::AdditionalPermissionProfile;
|
||||
use codex_sandboxing::SandboxCommand;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use std::collections::HashMap;
|
||||
#[cfg(unix)]
|
||||
use std::path::Path;
|
||||
@@ -33,8 +35,9 @@ pub(crate) mod apply_patch;
|
||||
pub(crate) mod shell;
|
||||
pub(crate) mod unified_exec;
|
||||
|
||||
/// Shared helper to construct sandbox transform inputs from a tokenized command line.
|
||||
/// Validates that at least a program is present.
|
||||
/// Shared helper to construct sandbox transform inputs from a tokenized command line and native
|
||||
/// working directory. Validates that at least a program is present and that the working directory
|
||||
/// has a file URI representation.
|
||||
pub(crate) fn build_sandbox_command(
|
||||
command: &[String],
|
||||
cwd: &AbsolutePathBuf,
|
||||
@@ -44,10 +47,15 @@ pub(crate) fn build_sandbox_command(
|
||||
let (program, args) = command
|
||||
.split_first()
|
||||
.ok_or_else(|| ToolError::Rejected("command args are empty".to_string()))?;
|
||||
let cwd = PathUri::from_abs_path(cwd).map_err(|_| {
|
||||
ToolError::Codex(CodexErr::InvalidRequest(
|
||||
"command cwd cannot be represented as a file URI".to_string(),
|
||||
))
|
||||
})?;
|
||||
Ok(SandboxCommand {
|
||||
program: program.clone().into(),
|
||||
args: args.to_vec(),
|
||||
cwd: cwd.clone(),
|
||||
cwd,
|
||||
env: env.clone(),
|
||||
additional_permissions,
|
||||
})
|
||||
|
||||
@@ -25,6 +25,7 @@ use codex_protocol::models::PermissionProfile;
|
||||
use codex_sandboxing::SandboxManager;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use core_test_support::PathBufExt;
|
||||
use core_test_support::PathExt;
|
||||
use pretty_assertions::assert_eq;
|
||||
@@ -88,18 +89,21 @@ async fn test_network_proxy() -> anyhow::Result<NetworkProxy> {
|
||||
async fn explicit_escalation_prepares_exec_without_managed_network() -> anyhow::Result<()> {
|
||||
let proxy = test_network_proxy().await?;
|
||||
let dir = tempdir().expect("create temp dir");
|
||||
let cwd = dir.path().abs();
|
||||
let command_cwd = dir.path().join("command").abs();
|
||||
let native_sandbox_policy_cwd = dir.path().join("sandbox-policy").abs();
|
||||
let mut env = HashMap::from([("CUSTOM_ENV".to_string(), "kept".to_string())]);
|
||||
proxy.apply_to_env(&mut env);
|
||||
|
||||
let command = vec!["/bin/echo".to_string(), "ok".to_string()];
|
||||
let command = build_sandbox_command(
|
||||
&command,
|
||||
&cwd,
|
||||
&command_cwd,
|
||||
&exec_env_for_sandbox_permissions(&env, SandboxPermissions::RequireEscalated),
|
||||
/*additional_permissions*/ None,
|
||||
)
|
||||
.expect("build sandbox command");
|
||||
assert_eq!(command.cwd, PathUri::from_abs_path(&command_cwd)?);
|
||||
let sandbox_policy_cwd = PathUri::from_abs_path(&native_sandbox_policy_cwd)?;
|
||||
let options = ExecOptions {
|
||||
expiration: ExecExpiration::DefaultTimeout,
|
||||
capture_policy: ExecCapturePolicy::ShellTool,
|
||||
@@ -111,8 +115,8 @@ async fn explicit_escalation_prepares_exec_without_managed_network() -> anyhow::
|
||||
permissions: &permissions,
|
||||
enforce_managed_network: false,
|
||||
manager: &manager,
|
||||
sandbox_cwd: &cwd,
|
||||
workspace_roots: std::slice::from_ref(&cwd),
|
||||
sandbox_cwd: &sandbox_policy_cwd,
|
||||
workspace_roots: std::slice::from_ref(&native_sandbox_policy_cwd),
|
||||
codex_linux_sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
@@ -131,6 +135,11 @@ async fn explicit_escalation_prepares_exec_without_managed_network() -> anyhow::
|
||||
)
|
||||
.expect("prepare exec request");
|
||||
|
||||
assert_eq!(exec_request.cwd, command_cwd);
|
||||
assert_eq!(
|
||||
exec_request.windows_sandbox_policy_cwd,
|
||||
native_sandbox_policy_cwd
|
||||
);
|
||||
assert_eq!(exec_request.network, None);
|
||||
for key in PROXY_ENV_KEYS {
|
||||
assert_eq!(exec_request.env.get(*key), None, "{key} should be unset");
|
||||
|
||||
@@ -310,7 +310,7 @@ impl ToolRuntime<ShellRequest, ExecToolCallOutput> for ShellRuntime {
|
||||
};
|
||||
let env = attempt
|
||||
.env_for(command, options, managed_network)
|
||||
.map_err(|err| ToolError::Codex(err.into()))?;
|
||||
.map_err(ToolError::Codex)?;
|
||||
let out = execute_env(env, Self::stdout_stream(ctx))
|
||||
.await
|
||||
.map_err(ToolError::Codex)?;
|
||||
|
||||
@@ -66,6 +66,7 @@ use codex_shell_escalation::ShellCommandExecutor;
|
||||
use codex_shell_escalation::ShellCommandExecutorFuture;
|
||||
use codex_shell_escalation::Stopwatch;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
@@ -143,7 +144,7 @@ pub(super) async fn try_run_zsh_fork(
|
||||
options,
|
||||
managed_network_for_sandbox_permissions(req.network.as_ref(), req.sandbox_permissions),
|
||||
)
|
||||
.map_err(|err| ToolError::Codex(err.into()))?;
|
||||
.map_err(ToolError::Codex)?;
|
||||
let crate::sandboxing::ExecRequest {
|
||||
command,
|
||||
cwd: sandbox_cwd,
|
||||
@@ -968,10 +969,19 @@ impl CoreShellCommandExecutor {
|
||||
self.windows_sandbox_level,
|
||||
self.network.is_some(),
|
||||
);
|
||||
let cwd = PathUri::from_abs_path(workdir).map_err(|_| {
|
||||
CodexErr::InvalidRequest("command cwd cannot be represented as a file URI".to_string())
|
||||
})?;
|
||||
let sandbox_policy_cwd =
|
||||
PathUri::from_abs_path(&self.sandbox_policy_cwd).map_err(|_| {
|
||||
CodexErr::InvalidRequest(
|
||||
"sandbox policy cwd cannot be represented as a file URI".to_string(),
|
||||
)
|
||||
})?;
|
||||
let command = SandboxCommand {
|
||||
program: program.clone().into(),
|
||||
args: args.to_vec(),
|
||||
cwd: workdir.clone(),
|
||||
cwd,
|
||||
env,
|
||||
additional_permissions,
|
||||
};
|
||||
@@ -985,7 +995,7 @@ impl CoreShellCommandExecutor {
|
||||
sandbox,
|
||||
enforce_managed_network: self.network.is_some(),
|
||||
network: self.network.as_ref(),
|
||||
sandbox_policy_cwd: &self.sandbox_policy_cwd,
|
||||
sandbox_policy_cwd: &sandbox_policy_cwd,
|
||||
codex_linux_sandbox_exe: self.codex_linux_sandbox_exe.as_deref(),
|
||||
use_legacy_landlock: self.use_legacy_landlock,
|
||||
windows_sandbox_level: self.windows_sandbox_level,
|
||||
@@ -994,7 +1004,6 @@ impl CoreShellCommandExecutor {
|
||||
let mut exec_request = crate::sandboxing::ExecRequest::from_sandbox_exec_request(
|
||||
exec_request,
|
||||
options,
|
||||
self.sandbox_policy_cwd.clone(),
|
||||
self.windows_sandbox_workspace_roots.clone(),
|
||||
);
|
||||
if let Some(network) = exec_request.network.as_ref() {
|
||||
|
||||
@@ -326,11 +326,16 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
|
||||
if let UnifiedExecShellMode::ZshFork(zsh_fork_config) = &self.shell_mode {
|
||||
let command =
|
||||
build_sandbox_command(&command, &req.cwd, &env, req.additional_permissions.clone())
|
||||
.map_err(|_| ToolError::Rejected("missing command line for PTY".to_string()))?;
|
||||
.map_err(|error| match error {
|
||||
ToolError::Rejected(_) => {
|
||||
ToolError::Rejected("missing command line for PTY".to_string())
|
||||
}
|
||||
error @ ToolError::Codex(_) => error,
|
||||
})?;
|
||||
let options = unified_exec_options(attempt.network_denial_cancellation_token.clone());
|
||||
let mut exec_env = attempt
|
||||
.env_for(command, options, managed_network)
|
||||
.map_err(|err| ToolError::Codex(err.into()))?;
|
||||
.map_err(ToolError::Codex)?;
|
||||
exec_env.exec_server_env_config = req.exec_server_env_config.clone();
|
||||
match zsh_fork_backend::maybe_prepare_unified_exec(
|
||||
req,
|
||||
@@ -377,11 +382,16 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
|
||||
}
|
||||
let command =
|
||||
build_sandbox_command(&command, &req.cwd, &env, req.additional_permissions.clone())
|
||||
.map_err(|_| ToolError::Rejected("missing command line for PTY".to_string()))?;
|
||||
.map_err(|error| match error {
|
||||
ToolError::Rejected(_) => {
|
||||
ToolError::Rejected("missing command line for PTY".to_string())
|
||||
}
|
||||
error @ ToolError::Codex(_) => error,
|
||||
})?;
|
||||
let options = unified_exec_options(attempt.network_denial_cancellation_token.clone());
|
||||
let mut exec_env = attempt
|
||||
.env_for(command, options, managed_network)
|
||||
.map_err(|err| ToolError::Codex(err.into()))?;
|
||||
.map_err(ToolError::Codex)?;
|
||||
exec_env.exec_server_env_config = req.exec_server_env_config.clone();
|
||||
self.manager
|
||||
.open_session_with_exec_env(
|
||||
|
||||
@@ -21,12 +21,12 @@ use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::ReviewDecision;
|
||||
use codex_sandboxing::SandboxCommand;
|
||||
use codex_sandboxing::SandboxManager;
|
||||
use codex_sandboxing::SandboxTransformError;
|
||||
use codex_sandboxing::SandboxTransformRequest;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_sandboxing::SandboxablePreference;
|
||||
use codex_tools::ToolName;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use futures::Future;
|
||||
use futures::future::BoxFuture;
|
||||
use serde::Serialize;
|
||||
@@ -411,7 +411,7 @@ pub(crate) struct SandboxAttempt<'a> {
|
||||
pub permissions: &'a codex_protocol::models::PermissionProfile,
|
||||
pub enforce_managed_network: bool,
|
||||
pub(crate) manager: &'a SandboxManager,
|
||||
pub(crate) sandbox_cwd: &'a AbsolutePathBuf,
|
||||
pub(crate) sandbox_cwd: &'a PathUri,
|
||||
pub(crate) workspace_roots: &'a [AbsolutePathBuf],
|
||||
pub codex_linux_sandbox_exe: Option<&'a std::path::PathBuf>,
|
||||
pub use_legacy_landlock: bool,
|
||||
@@ -426,8 +426,9 @@ impl<'a> SandboxAttempt<'a> {
|
||||
command: SandboxCommand,
|
||||
options: ExecOptions,
|
||||
network: Option<&NetworkProxy>,
|
||||
) -> Result<crate::sandboxing::ExecRequest, SandboxTransformError> {
|
||||
self.manager
|
||||
) -> Result<crate::sandboxing::ExecRequest, CodexErr> {
|
||||
let request = self
|
||||
.manager
|
||||
.transform(SandboxTransformRequest {
|
||||
command,
|
||||
permissions: self.permissions,
|
||||
@@ -442,19 +443,12 @@ impl<'a> SandboxAttempt<'a> {
|
||||
windows_sandbox_level: self.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
||||
})
|
||||
.map(|request| {
|
||||
let windows_sandbox_policy_cwd =
|
||||
codex_utils_absolute_path::AbsolutePathBuf::try_from(
|
||||
self.sandbox_cwd.to_path_buf(),
|
||||
)
|
||||
.unwrap_or_else(|_| request.cwd.clone());
|
||||
crate::sandboxing::ExecRequest::from_sandbox_exec_request(
|
||||
request,
|
||||
options,
|
||||
windows_sandbox_policy_cwd,
|
||||
self.workspace_roots.to_vec(),
|
||||
)
|
||||
})
|
||||
.map_err(CodexErr::from)?;
|
||||
Ok(crate::sandboxing::ExecRequest::from_sandbox_exec_request(
|
||||
request,
|
||||
options,
|
||||
self.workspace_roots.to_vec(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -573,21 +573,22 @@ async fn zsh_fork_unified_exec_keeps_shell_parameter_when_remote_environment_ava
|
||||
.environments
|
||||
.primary()
|
||||
.expect("primary environment")
|
||||
.cwd
|
||||
.cwd()
|
||||
.clone();
|
||||
turn.environments
|
||||
.turn_environments
|
||||
.push(crate::session::turn_context::TurnEnvironment {
|
||||
environment_id: "remote".to_string(),
|
||||
environment: Arc::new(
|
||||
turn.environments.turn_environments.push(
|
||||
crate::session::turn_context::TurnEnvironment::new(
|
||||
"remote".to_string(),
|
||||
Arc::new(
|
||||
codex_exec_server::Environment::create_for_tests(Some(
|
||||
"ws://127.0.0.1:1/remote-exec-server".to_string(),
|
||||
))
|
||||
.expect("remote test environment"),
|
||||
),
|
||||
cwd: remote_cwd,
|
||||
shell: None,
|
||||
});
|
||||
remote_cwd,
|
||||
/*shell*/ None,
|
||||
)
|
||||
.expect("turn environment"),
|
||||
);
|
||||
})
|
||||
.await;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user