mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
Merge f0cadb0270 into sapling-pr-archive-bolinfest
This commit is contained in:
@@ -314,7 +314,11 @@ pub fn build_exec_request(
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_private_desktop,
|
||||
})
|
||||
.map(|request| ExecRequest::from_sandbox_exec_request(request, options))
|
||||
.map(|request| {
|
||||
let windows_sandbox_policy_cwd = AbsolutePathBuf::try_from(sandbox_cwd.to_path_buf())
|
||||
.unwrap_or_else(|_| request.cwd.clone());
|
||||
ExecRequest::from_sandbox_exec_request(request, options, windows_sandbox_policy_cwd)
|
||||
})
|
||||
.map_err(CodexErr::from)?;
|
||||
let use_windows_elevated_backend = windows_sandbox_uses_elevated_backend(
|
||||
exec_req.windows_sandbox_level,
|
||||
@@ -357,6 +361,7 @@ pub(crate) async fn execute_exec_request(
|
||||
expiration,
|
||||
capture_policy,
|
||||
sandbox,
|
||||
windows_sandbox_policy_cwd: _,
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_private_desktop,
|
||||
sandbox_policy,
|
||||
|
||||
@@ -49,6 +49,7 @@ pub struct ExecRequest {
|
||||
pub expiration: ExecExpiration,
|
||||
pub capture_policy: ExecCapturePolicy,
|
||||
pub sandbox: SandboxType,
|
||||
pub windows_sandbox_policy_cwd: AbsolutePathBuf,
|
||||
pub windows_sandbox_level: WindowsSandboxLevel,
|
||||
pub windows_sandbox_private_desktop: bool,
|
||||
pub sandbox_policy: SandboxPolicy,
|
||||
@@ -75,6 +76,7 @@ impl ExecRequest {
|
||||
network_sandbox_policy: NetworkSandboxPolicy,
|
||||
arg0: Option<String>,
|
||||
) -> Self {
|
||||
let windows_sandbox_policy_cwd = cwd.clone();
|
||||
Self {
|
||||
command,
|
||||
cwd,
|
||||
@@ -84,6 +86,7 @@ impl ExecRequest {
|
||||
expiration,
|
||||
capture_policy,
|
||||
sandbox,
|
||||
windows_sandbox_policy_cwd,
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_private_desktop,
|
||||
sandbox_policy,
|
||||
@@ -97,6 +100,7 @@ impl ExecRequest {
|
||||
pub(crate) fn from_sandbox_exec_request(
|
||||
request: SandboxExecRequest,
|
||||
options: ExecOptions,
|
||||
windows_sandbox_policy_cwd: AbsolutePathBuf,
|
||||
) -> Self {
|
||||
let SandboxExecRequest {
|
||||
command,
|
||||
@@ -134,6 +138,7 @@ impl ExecRequest {
|
||||
expiration,
|
||||
capture_policy,
|
||||
sandbox,
|
||||
windows_sandbox_policy_cwd,
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_private_desktop,
|
||||
sandbox_policy,
|
||||
|
||||
@@ -171,6 +171,7 @@ pub(crate) async fn execute_user_shell_command(
|
||||
expiration: USER_SHELL_TIMEOUT_MS.into(),
|
||||
capture_policy: ExecCapturePolicy::ShellTool,
|
||||
sandbox: SandboxType::None,
|
||||
windows_sandbox_policy_cwd: cwd.clone(),
|
||||
windows_sandbox_level: turn_context.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: turn_context
|
||||
.config
|
||||
|
||||
@@ -193,8 +193,7 @@ pub(super) async fn apply_granted_turn_permissions(
|
||||
);
|
||||
let permissions_preapproved = match (effective_permissions.as_ref(), granted_permissions) {
|
||||
(Some(effective_permissions), Some(granted_permissions)) => {
|
||||
intersect_permission_profiles(effective_permissions.clone(), granted_permissions, cwd)
|
||||
== *effective_permissions
|
||||
permissions_are_preapproved(effective_permissions, granted_permissions, cwd)
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
@@ -213,17 +212,37 @@ pub(super) async fn apply_granted_turn_permissions(
|
||||
}
|
||||
}
|
||||
|
||||
fn permissions_are_preapproved(
|
||||
effective_permissions: &PermissionProfile,
|
||||
granted_permissions: PermissionProfile,
|
||||
cwd: &Path,
|
||||
) -> bool {
|
||||
let materialized_effective_permissions = intersect_permission_profiles(
|
||||
effective_permissions.clone(),
|
||||
effective_permissions.clone(),
|
||||
cwd,
|
||||
);
|
||||
intersect_permission_profiles(effective_permissions.clone(), granted_permissions, cwd)
|
||||
== materialized_effective_permissions
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::EffectiveAdditionalPermissions;
|
||||
use super::implicit_granted_permissions;
|
||||
use super::normalize_and_validate_additional_permissions;
|
||||
use super::permissions_are_preapproved;
|
||||
use crate::sandboxing::SandboxPermissions;
|
||||
use codex_protocol::models::FileSystemPermissions;
|
||||
use codex_protocol::models::NetworkPermissions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||
use codex_protocol::protocol::AskForApproval;
|
||||
use codex_protocol::protocol::GranularApprovalConfig;
|
||||
use codex_sandboxing::policy_transforms::intersect_permission_profiles;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tempfile::tempdir;
|
||||
@@ -326,4 +345,40 @@ mod tests {
|
||||
|
||||
assert_eq!(implicit_permissions, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relative_deny_glob_grants_remain_preapproved_after_materialization() {
|
||||
let cwd = tempdir().expect("tempdir");
|
||||
let requested_permissions = PermissionProfile {
|
||||
file_system: Some(FileSystemPermissions {
|
||||
entries: vec![
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::Special {
|
||||
value: FileSystemSpecialPath::CurrentWorkingDirectory,
|
||||
},
|
||||
access: FileSystemAccessMode::Write,
|
||||
},
|
||||
FileSystemSandboxEntry {
|
||||
path: FileSystemPath::GlobPattern {
|
||||
pattern: "**/*.env".to_string(),
|
||||
},
|
||||
access: FileSystemAccessMode::None,
|
||||
},
|
||||
],
|
||||
glob_scan_max_depth: None,
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
let stored_grant = intersect_permission_profiles(
|
||||
requested_permissions.clone(),
|
||||
requested_permissions.clone(),
|
||||
cwd.path(),
|
||||
);
|
||||
|
||||
assert!(permissions_are_preapproved(
|
||||
&requested_permissions,
|
||||
stored_grant,
|
||||
cwd.path(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1091,7 +1091,11 @@ impl JsReplManager {
|
||||
.windows_sandbox_private_desktop,
|
||||
})
|
||||
.map(|request| {
|
||||
crate::sandboxing::ExecRequest::from_sandbox_exec_request(request, options)
|
||||
crate::sandboxing::ExecRequest::from_sandbox_exec_request(
|
||||
request,
|
||||
options,
|
||||
turn.cwd.clone(),
|
||||
)
|
||||
})
|
||||
.map_err(|err| format!("failed to configure sandbox for js_repl: {err}"))?;
|
||||
|
||||
|
||||
@@ -134,6 +134,7 @@ pub(super) async fn try_run_zsh_fork(
|
||||
expiration: _sandbox_expiration,
|
||||
capture_policy: _capture_policy,
|
||||
sandbox,
|
||||
windows_sandbox_policy_cwd: sandbox_policy_cwd,
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: _windows_sandbox_private_desktop,
|
||||
sandbox_policy,
|
||||
@@ -161,7 +162,7 @@ pub(super) async fn try_run_zsh_fork(
|
||||
network: sandbox_network,
|
||||
windows_sandbox_level,
|
||||
arg0,
|
||||
sandbox_policy_cwd: ctx.turn.cwd.clone(),
|
||||
sandbox_policy_cwd,
|
||||
codex_linux_sandbox_exe: ctx.turn.codex_linux_sandbox_exe.clone(),
|
||||
use_legacy_landlock: ctx.turn.features.use_legacy_landlock(),
|
||||
};
|
||||
@@ -789,6 +790,7 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
|
||||
expiration: ExecExpiration::Cancellation(cancel_rx),
|
||||
capture_policy: ExecCapturePolicy::ShellTool,
|
||||
sandbox: self.sandbox,
|
||||
windows_sandbox_policy_cwd: self.sandbox_policy_cwd.clone(),
|
||||
windows_sandbox_level: self.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: false,
|
||||
sandbox_policy: self.sandbox_policy.clone(),
|
||||
@@ -932,8 +934,11 @@ impl CoreShellCommandExecutor {
|
||||
windows_sandbox_level: self.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: false,
|
||||
})?;
|
||||
let mut exec_request =
|
||||
crate::sandboxing::ExecRequest::from_sandbox_exec_request(exec_request, options);
|
||||
let mut exec_request = crate::sandboxing::ExecRequest::from_sandbox_exec_request(
|
||||
exec_request,
|
||||
options,
|
||||
self.sandbox_policy_cwd.clone(),
|
||||
);
|
||||
if let Some(network) = exec_request.network.as_ref() {
|
||||
network.apply_to_env(&mut exec_request.env);
|
||||
}
|
||||
|
||||
@@ -376,7 +376,16 @@ impl<'a> SandboxAttempt<'a> {
|
||||
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
||||
})
|
||||
.map(|request| {
|
||||
crate::sandboxing::ExecRequest::from_sandbox_exec_request(request, options)
|
||||
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,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,6 +659,60 @@ impl UnifiedExecProcessManager {
|
||||
environment: &codex_exec_server::Environment,
|
||||
) -> Result<UnifiedExecProcess, UnifiedExecError> {
|
||||
let inherited_fds = spawn_lifecycle.inherited_fds();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
if request.sandbox == codex_sandboxing::SandboxType::WindowsRestrictedToken {
|
||||
let policy_json = serde_json::to_string(&request.sandbox_policy).map_err(|err| {
|
||||
UnifiedExecError::create_process(format!(
|
||||
"failed to serialize Windows sandbox policy: {err}"
|
||||
))
|
||||
})?;
|
||||
let codex_home = crate::config::find_codex_home().map_err(|err| {
|
||||
UnifiedExecError::create_process(format!(
|
||||
"windows sandbox: failed to resolve codex_home: {err}"
|
||||
))
|
||||
})?;
|
||||
let spawned = match request.windows_sandbox_level {
|
||||
codex_protocol::config_types::WindowsSandboxLevel::Elevated => {
|
||||
codex_windows_sandbox::spawn_windows_sandbox_session_elevated(
|
||||
policy_json.as_str(),
|
||||
request.windows_sandbox_policy_cwd.as_path(),
|
||||
codex_home.as_ref(),
|
||||
request.command.clone(),
|
||||
request.cwd.as_path(),
|
||||
request.env.clone(),
|
||||
None,
|
||||
tty,
|
||||
tty,
|
||||
request.windows_sandbox_private_desktop,
|
||||
)
|
||||
.await
|
||||
}
|
||||
codex_protocol::config_types::WindowsSandboxLevel::RestrictedToken
|
||||
| codex_protocol::config_types::WindowsSandboxLevel::Disabled => {
|
||||
codex_windows_sandbox::spawn_windows_sandbox_session_legacy(
|
||||
policy_json.as_str(),
|
||||
request.windows_sandbox_policy_cwd.as_path(),
|
||||
codex_home.as_ref(),
|
||||
request.command.clone(),
|
||||
request.cwd.as_path(),
|
||||
request.env.clone(),
|
||||
None,
|
||||
tty,
|
||||
tty,
|
||||
request.windows_sandbox_private_desktop,
|
||||
)
|
||||
.await
|
||||
}
|
||||
};
|
||||
spawn_lifecycle.after_spawn();
|
||||
return UnifiedExecProcess::from_spawned(
|
||||
spawned.map_err(|err| UnifiedExecError::create_process(err.to_string()))?,
|
||||
request.sandbox,
|
||||
spawn_lifecycle,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
if environment.is_remote() {
|
||||
if !inherited_fds.is_empty() {
|
||||
return Err(UnifiedExecError::create_process(
|
||||
|
||||
@@ -67,12 +67,13 @@ fn env_overlay_for_exec_server_keeps_runtime_changes_only() {
|
||||
|
||||
#[test]
|
||||
fn exec_server_params_use_env_policy_overlay_contract() {
|
||||
let cwd: codex_utils_absolute_path::AbsolutePathBuf = std::env::current_dir()
|
||||
.expect("current dir")
|
||||
.try_into()
|
||||
.expect("absolute path");
|
||||
let request = ExecRequest {
|
||||
command: vec!["bash".to_string(), "-lc".to_string(), "true".to_string()],
|
||||
cwd: std::env::current_dir()
|
||||
.expect("current dir")
|
||||
.try_into()
|
||||
.expect("absolute path"),
|
||||
cwd: cwd.clone(),
|
||||
env: HashMap::from([
|
||||
("HOME".to_string(), "/client-home".to_string()),
|
||||
("PATH".to_string(), "/sandbox-path".to_string()),
|
||||
@@ -95,6 +96,7 @@ fn exec_server_params_use_env_policy_overlay_contract() {
|
||||
expiration: crate::exec::ExecExpiration::DefaultTimeout,
|
||||
capture_policy: crate::exec::ExecCapturePolicy::ShellTool,
|
||||
sandbox: codex_sandboxing::SandboxType::None,
|
||||
windows_sandbox_policy_cwd: cwd,
|
||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess,
|
||||
|
||||
Reference in New Issue
Block a user