mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
windows-sandbox: add profile-native elevated APIs (#23714)
## Why This is the next step after #23167 in the Windows sandbox `PermissionProfile` migration. The elevated Windows backend still exposed policy-string entry points, which forced callers to pass a compatibility `SandboxPolicy` before the command-runner IPC could receive a profile. Adding profile-native APIs first keeps the core switch in the next PR small: reviewers can see that the Windows crate can prepare elevated setup, capability SIDs, and runner IPC from a resolved `PermissionProfile` without changing core behavior yet. ## What - Adds `ElevatedSandboxProfileCaptureRequest` and `run_windows_sandbox_capture_for_permission_profile_elevated` for one-shot elevated capture. - Adds `spawn_windows_sandbox_session_elevated_for_permission_profile` for unified exec sessions. - Factors elevated spawn prep through `prepare_elevated_spawn_context_for_permissions`, so both new APIs operate from `ResolvedWindowsSandboxPermissions` directly. - Keeps the existing legacy policy-string APIs as adapters for callers that have not moved yet. ## Verification - `cargo test -p codex-windows-sandbox` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23714). * #23715 * __->__ #23714
This commit is contained in:
1 parent
a27d3847b5
commit
c9ff067e31
6 files changed
+268
-66
No files matched your search
@@ -1,3 +1,4 @@
|
|||||||
|
use codex_protocol::models::PermissionProfile;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
@@ -20,8 +21,26 @@ pub struct ElevatedSandboxCaptureRequest<'a> {
|
|||||||
pub deny_write_paths_override: &'a [AbsolutePathBuf],
|
pub deny_write_paths_override: &'a [AbsolutePathBuf],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ElevatedSandboxProfileCaptureRequest<'a> {
|
||||||
|
pub permission_profile: &'a PermissionProfile,
|
||||||
|
pub permission_profile_cwd: &'a Path,
|
||||||
|
pub codex_home: &'a Path,
|
||||||
|
pub command: Vec<String>,
|
||||||
|
pub cwd: &'a Path,
|
||||||
|
pub env_map: HashMap<String, String>,
|
||||||
|
pub timeout_ms: Option<u64>,
|
||||||
|
pub use_private_desktop: bool,
|
||||||
|
pub proxy_enforced: bool,
|
||||||
|
pub read_roots_override: Option<&'a [PathBuf]>,
|
||||||
|
pub read_roots_include_platform_defaults: bool,
|
||||||
|
pub write_roots_override: Option<&'a [PathBuf]>,
|
||||||
|
pub deny_read_paths_override: &'a [AbsolutePathBuf],
|
||||||
|
pub deny_write_paths_override: &'a [AbsolutePathBuf],
|
||||||
|
}
|
||||||
|
|
||||||
mod windows_impl {
|
mod windows_impl {
|
||||||
use super::ElevatedSandboxCaptureRequest;
|
use super::ElevatedSandboxCaptureRequest;
|
||||||
|
use super::ElevatedSandboxProfileCaptureRequest;
|
||||||
use crate::acl::allow_null_device;
|
use crate::acl::allow_null_device;
|
||||||
use crate::cap::load_or_create_cap_sids;
|
use crate::cap::load_or_create_cap_sids;
|
||||||
use crate::cap::workspace_write_cap_sid_for_root;
|
use crate::cap::workspace_write_cap_sid_for_root;
|
||||||
@@ -37,7 +56,6 @@ mod windows_impl {
|
|||||||
use crate::logging::log_failure;
|
use crate::logging::log_failure;
|
||||||
use crate::logging::log_start;
|
use crate::logging::log_start;
|
||||||
use crate::logging::log_success;
|
use crate::logging::log_success;
|
||||||
use crate::policy::SandboxPolicy;
|
|
||||||
use crate::policy::parse_policy;
|
use crate::policy::parse_policy;
|
||||||
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
|
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
|
||||||
use crate::runner_client::spawn_runner_transport;
|
use crate::runner_client::spawn_runner_transport;
|
||||||
@@ -54,12 +72,12 @@ mod windows_impl {
|
|||||||
|
|
||||||
/// Launches the command runner under the sandbox user and captures its output.
|
/// Launches the command runner under the sandbox user and captures its output.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn run_windows_sandbox_capture(
|
pub fn run_windows_sandbox_capture_for_permission_profile(
|
||||||
request: ElevatedSandboxCaptureRequest<'_>,
|
request: ElevatedSandboxProfileCaptureRequest<'_>,
|
||||||
) -> Result<CaptureResult> {
|
) -> Result<CaptureResult> {
|
||||||
let ElevatedSandboxCaptureRequest {
|
let ElevatedSandboxProfileCaptureRequest {
|
||||||
policy_json_or_preset,
|
permission_profile,
|
||||||
sandbox_policy_cwd,
|
permission_profile_cwd,
|
||||||
codex_home,
|
codex_home,
|
||||||
command,
|
command,
|
||||||
cwd,
|
cwd,
|
||||||
@@ -73,6 +91,10 @@ mod windows_impl {
|
|||||||
deny_read_paths_override,
|
deny_read_paths_override,
|
||||||
deny_write_paths_override,
|
deny_write_paths_override,
|
||||||
} = request;
|
} = request;
|
||||||
|
let permissions = ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_cwd(
|
||||||
|
permission_profile,
|
||||||
|
permission_profile_cwd,
|
||||||
|
)?;
|
||||||
let deny_read_paths_override = deny_read_paths_override
|
let deny_read_paths_override = deny_read_paths_override
|
||||||
.iter()
|
.iter()
|
||||||
.map(AbsolutePathBuf::to_path_buf)
|
.map(AbsolutePathBuf::to_path_buf)
|
||||||
@@ -81,11 +103,6 @@ mod windows_impl {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(AbsolutePathBuf::to_path_buf)
|
.map(AbsolutePathBuf::to_path_buf)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let policy = parse_policy(policy_json_or_preset)?;
|
|
||||||
let permissions = ResolvedWindowsSandboxPermissions::from_legacy_policy_for_cwd(
|
|
||||||
&policy,
|
|
||||||
sandbox_policy_cwd,
|
|
||||||
);
|
|
||||||
normalize_null_device_env(&mut env_map);
|
normalize_null_device_env(&mut env_map);
|
||||||
ensure_non_interactive_pager(&mut env_map);
|
ensure_non_interactive_pager(&mut env_map);
|
||||||
inherit_path_env(&mut env_map);
|
inherit_path_env(&mut env_map);
|
||||||
@@ -109,12 +126,6 @@ mod windows_impl {
|
|||||||
proxy_enforced,
|
proxy_enforced,
|
||||||
)?;
|
)?;
|
||||||
// Build capability SID for ACL grants.
|
// Build capability SID for ACL grants.
|
||||||
if matches!(
|
|
||||||
&policy,
|
|
||||||
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. }
|
|
||||||
) {
|
|
||||||
anyhow::bail!("DangerFullAccess and ExternalSandbox are not supported for sandboxing")
|
|
||||||
}
|
|
||||||
let caps = load_or_create_cap_sids(codex_home)?;
|
let caps = load_or_create_cap_sids(codex_home)?;
|
||||||
let (sid_for_null, cap_sids) = if permissions.uses_write_capabilities_for_cwd(cwd, &env_map)
|
let (sid_for_null, cap_sids) = if permissions.uses_write_capabilities_for_cwd(cwd, &env_map)
|
||||||
{
|
{
|
||||||
@@ -143,14 +154,12 @@ mod windows_impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
(|| -> Result<CaptureResult> {
|
(|| -> Result<CaptureResult> {
|
||||||
let permission_profile =
|
|
||||||
PermissionProfile::from_legacy_sandbox_policy_for_cwd(&policy, sandbox_policy_cwd);
|
|
||||||
let spawn_request = SpawnRequest {
|
let spawn_request = SpawnRequest {
|
||||||
command: command.clone(),
|
command: command.clone(),
|
||||||
cwd: cwd.to_path_buf(),
|
cwd: cwd.to_path_buf(),
|
||||||
env: env_map.clone(),
|
env: env_map.clone(),
|
||||||
permission_profile,
|
permission_profile: permission_profile.clone(),
|
||||||
permission_profile_cwd: sandbox_policy_cwd.to_path_buf(),
|
permission_profile_cwd: permission_profile_cwd.to_path_buf(),
|
||||||
codex_home: sandbox_base.clone(),
|
codex_home: sandbox_base.clone(),
|
||||||
real_codex_home: codex_home.to_path_buf(),
|
real_codex_home: codex_home.to_path_buf(),
|
||||||
cap_sids,
|
cap_sids,
|
||||||
@@ -210,6 +219,48 @@ mod windows_impl {
|
|||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Legacy policy-string adapter for callers that have not moved to permission profiles yet.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn run_windows_sandbox_capture(
|
||||||
|
request: ElevatedSandboxCaptureRequest<'_>,
|
||||||
|
) -> Result<CaptureResult> {
|
||||||
|
let ElevatedSandboxCaptureRequest {
|
||||||
|
policy_json_or_preset,
|
||||||
|
sandbox_policy_cwd,
|
||||||
|
codex_home,
|
||||||
|
command,
|
||||||
|
cwd,
|
||||||
|
env_map,
|
||||||
|
timeout_ms,
|
||||||
|
use_private_desktop,
|
||||||
|
proxy_enforced,
|
||||||
|
read_roots_override,
|
||||||
|
read_roots_include_platform_defaults,
|
||||||
|
write_roots_override,
|
||||||
|
deny_read_paths_override,
|
||||||
|
deny_write_paths_override,
|
||||||
|
} = request;
|
||||||
|
let policy = parse_policy(policy_json_or_preset)?;
|
||||||
|
let permission_profile =
|
||||||
|
PermissionProfile::from_legacy_sandbox_policy_for_cwd(&policy, sandbox_policy_cwd);
|
||||||
|
run_windows_sandbox_capture_for_permission_profile(ElevatedSandboxProfileCaptureRequest {
|
||||||
|
permission_profile: &permission_profile,
|
||||||
|
permission_profile_cwd: sandbox_policy_cwd,
|
||||||
|
codex_home,
|
||||||
|
command,
|
||||||
|
cwd,
|
||||||
|
env_map,
|
||||||
|
timeout_ms,
|
||||||
|
use_private_desktop,
|
||||||
|
proxy_enforced,
|
||||||
|
read_roots_override,
|
||||||
|
read_roots_include_platform_defaults,
|
||||||
|
write_roots_override,
|
||||||
|
deny_read_paths_override,
|
||||||
|
deny_write_paths_override,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::policy::SandboxPolicy;
|
use crate::policy::SandboxPolicy;
|
||||||
@@ -242,10 +293,13 @@ mod windows_impl {
|
|||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use windows_impl::run_windows_sandbox_capture;
|
pub use windows_impl::run_windows_sandbox_capture;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use windows_impl::run_windows_sandbox_capture_for_permission_profile;
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
mod stub {
|
mod stub {
|
||||||
use super::ElevatedSandboxCaptureRequest;
|
use super::ElevatedSandboxCaptureRequest;
|
||||||
|
use super::ElevatedSandboxProfileCaptureRequest;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
|
|
||||||
@@ -264,7 +318,17 @@ mod stub {
|
|||||||
) -> Result<CaptureResult> {
|
) -> Result<CaptureResult> {
|
||||||
bail!("Windows sandbox is only available on Windows")
|
bail!("Windows sandbox is only available on Windows")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Stub implementation for non-Windows targets; sandboxing only works on Windows.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn run_windows_sandbox_capture_for_permission_profile(
|
||||||
|
_request: ElevatedSandboxProfileCaptureRequest<'_>,
|
||||||
|
) -> Result<CaptureResult> {
|
||||||
|
bail!("Windows sandbox is only available on Windows")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
pub use stub::run_windows_sandbox_capture;
|
pub use stub::run_windows_sandbox_capture;
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub use stub::run_windows_sandbox_capture_for_permission_profile;
|
||||||
@@ -139,8 +139,12 @@ pub use dpapi::unprotect as dpapi_unprotect;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use elevated_impl::ElevatedSandboxCaptureRequest;
|
pub use elevated_impl::ElevatedSandboxCaptureRequest;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use elevated_impl::ElevatedSandboxProfileCaptureRequest;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub use elevated_impl::run_windows_sandbox_capture as run_windows_sandbox_capture_elevated;
|
pub use elevated_impl::run_windows_sandbox_capture as run_windows_sandbox_capture_elevated;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use elevated_impl::run_windows_sandbox_capture_for_permission_profile as run_windows_sandbox_capture_for_permission_profile_elevated;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub use helper_materialization::resolve_current_exe_for_launch;
|
pub use helper_materialization::resolve_current_exe_for_launch;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use hide_users::hide_current_user_profile_dir;
|
pub use hide_users::hide_current_user_profile_dir;
|
||||||
@@ -258,6 +262,8 @@ pub use token::get_current_token_for_restriction;
|
|||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use unified_exec::spawn_windows_sandbox_session_elevated;
|
pub use unified_exec::spawn_windows_sandbox_session_elevated;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
pub use unified_exec::spawn_windows_sandbox_session_elevated_for_permission_profile;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
pub use unified_exec::spawn_windows_sandbox_session_legacy;
|
pub use unified_exec::spawn_windows_sandbox_session_legacy;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub use wfp::install_wfp_filters_for_account;
|
pub use wfp::install_wfp_filters_for_account;
|
||||||
|
|||||||
@@ -41,8 +41,10 @@ pub fn token_mode_for_permission_profile(
|
|||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
env_map: &HashMap<String, String>,
|
env_map: &HashMap<String, String>,
|
||||||
) -> Result<WindowsSandboxTokenMode> {
|
) -> Result<WindowsSandboxTokenMode> {
|
||||||
let permissions =
|
let permissions = ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_cwd(
|
||||||
ResolvedWindowsSandboxPermissions::try_from_permission_profile(permission_profile)?;
|
permission_profile,
|
||||||
|
cwd,
|
||||||
|
)?;
|
||||||
if permissions.file_system.has_full_disk_write_access() {
|
if permissions.file_system.has_full_disk_write_access() {
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"permission profile requests full-disk filesystem writes, which cannot be enforced by the Windows sandbox"
|
"permission profile requests full-disk filesystem writes, which cannot be enforced by the Windows sandbox"
|
||||||
@@ -82,6 +84,19 @@ impl ResolvedWindowsSandboxPermissions {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolves a managed permission profile and binds symbolic `:workspace_roots`
|
||||||
|
/// entries to the permission root supplied by the caller.
|
||||||
|
pub fn try_from_permission_profile_for_cwd(
|
||||||
|
permission_profile: &PermissionProfile,
|
||||||
|
cwd: &Path,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let mut permissions = Self::try_from_permission_profile(permission_profile)?;
|
||||||
|
permissions.file_system = permissions
|
||||||
|
.file_system
|
||||||
|
.materialize_project_roots_with_cwd(cwd);
|
||||||
|
Ok(permissions)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn should_apply_network_block(&self) -> bool {
|
pub(crate) fn should_apply_network_block(&self) -> bool {
|
||||||
!self.network.is_enabled()
|
!self.network.is_enabled()
|
||||||
}
|
}
|
||||||
@@ -259,6 +274,43 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn permission_profile_workspace_root_stays_bound_to_profile_cwd() {
|
||||||
|
let tmp = TempDir::new().expect("tempdir");
|
||||||
|
let profile_cwd = tmp.path().join("workspace");
|
||||||
|
let command_cwd = profile_cwd.join("subdir");
|
||||||
|
std::fs::create_dir_all(&command_cwd).expect("create command cwd");
|
||||||
|
|
||||||
|
let permission_profile = PermissionProfile::Managed {
|
||||||
|
file_system: ManagedFileSystemPermissions::Restricted {
|
||||||
|
entries: vec![FileSystemSandboxEntry {
|
||||||
|
path: FileSystemPath::Special {
|
||||||
|
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
|
||||||
|
},
|
||||||
|
access: FileSystemAccessMode::Write,
|
||||||
|
}],
|
||||||
|
glob_scan_max_depth: None,
|
||||||
|
},
|
||||||
|
network: NetworkSandboxPolicy::Restricted,
|
||||||
|
};
|
||||||
|
let permissions = ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_cwd(
|
||||||
|
&permission_profile,
|
||||||
|
&profile_cwd,
|
||||||
|
)
|
||||||
|
.expect("managed permission profile");
|
||||||
|
|
||||||
|
let roots = permissions
|
||||||
|
.writable_roots_for_cwd(&command_cwd, &HashMap::new())
|
||||||
|
.into_iter()
|
||||||
|
.map(|root| root.root)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
roots,
|
||||||
|
vec![dunce::canonicalize(&profile_cwd).expect("canonical profile cwd")]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn token_mode_for_profile_without_writable_roots_uses_readonly_capability() {
|
fn token_mode_for_profile_without_writable_roots_uses_readonly_capability() {
|
||||||
let tmp = TempDir::new().expect("tempdir");
|
let tmp = TempDir::new().expect("tempdir");
|
||||||
|
|||||||
@@ -44,13 +44,13 @@ pub(crate) struct SpawnContext {
|
|||||||
pub(crate) policy: SandboxPolicy,
|
pub(crate) policy: SandboxPolicy,
|
||||||
pub(crate) permissions: ResolvedWindowsSandboxPermissions,
|
pub(crate) permissions: ResolvedWindowsSandboxPermissions,
|
||||||
pub(crate) current_dir: PathBuf,
|
pub(crate) current_dir: PathBuf,
|
||||||
pub(crate) sandbox_base: PathBuf,
|
|
||||||
pub(crate) logs_base_dir: Option<PathBuf>,
|
pub(crate) logs_base_dir: Option<PathBuf>,
|
||||||
pub(crate) uses_write_capabilities: bool,
|
pub(crate) uses_write_capabilities: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct ElevatedSpawnContext {
|
pub(crate) struct ElevatedSpawnContext {
|
||||||
pub(crate) common: SpawnContext,
|
pub(crate) sandbox_base: PathBuf,
|
||||||
|
pub(crate) logs_base_dir: Option<PathBuf>,
|
||||||
pub(crate) sandbox_creds: SandboxCreds,
|
pub(crate) sandbox_creds: SandboxCreds,
|
||||||
pub(crate) cap_sids: Vec<String>,
|
pub(crate) cap_sids: Vec<String>,
|
||||||
}
|
}
|
||||||
@@ -109,7 +109,7 @@ fn prepare_spawn_context_common(
|
|||||||
ensure_codex_home_exists(codex_home)?;
|
ensure_codex_home_exists(codex_home)?;
|
||||||
let sandbox_base = codex_home.join(".sandbox");
|
let sandbox_base = codex_home.join(".sandbox");
|
||||||
std::fs::create_dir_all(&sandbox_base)?;
|
std::fs::create_dir_all(&sandbox_base)?;
|
||||||
let logs_base_dir = Some(sandbox_base.clone());
|
let logs_base_dir = Some(sandbox_base);
|
||||||
log_start(command, logs_base_dir.as_deref());
|
log_start(command, logs_base_dir.as_deref());
|
||||||
|
|
||||||
let permissions =
|
let permissions =
|
||||||
@@ -120,7 +120,6 @@ fn prepare_spawn_context_common(
|
|||||||
policy,
|
policy,
|
||||||
permissions,
|
permissions,
|
||||||
current_dir: cwd.to_path_buf(),
|
current_dir: cwd.to_path_buf(),
|
||||||
sandbox_base,
|
|
||||||
logs_base_dir,
|
logs_base_dir,
|
||||||
uses_write_capabilities,
|
uses_write_capabilities,
|
||||||
})
|
})
|
||||||
@@ -362,9 +361,8 @@ pub(crate) fn apply_legacy_session_acl_rules(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn prepare_elevated_spawn_context(
|
pub(crate) fn prepare_elevated_spawn_context_for_permissions(
|
||||||
policy_json_or_preset: &str,
|
permissions: ResolvedWindowsSandboxPermissions,
|
||||||
sandbox_policy_cwd: &Path,
|
|
||||||
codex_home: &Path,
|
codex_home: &Path,
|
||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
env_map: &mut HashMap<String, String>,
|
env_map: &mut HashMap<String, String>,
|
||||||
@@ -375,33 +373,33 @@ pub(crate) fn prepare_elevated_spawn_context(
|
|||||||
deny_read_paths_override: &[PathBuf],
|
deny_read_paths_override: &[PathBuf],
|
||||||
deny_write_paths_override: &[PathBuf],
|
deny_write_paths_override: &[PathBuf],
|
||||||
) -> Result<ElevatedSpawnContext> {
|
) -> Result<ElevatedSpawnContext> {
|
||||||
let common = prepare_spawn_context_common(
|
normalize_null_device_env(env_map);
|
||||||
policy_json_or_preset,
|
ensure_non_interactive_pager(env_map);
|
||||||
sandbox_policy_cwd,
|
inherit_path_env(env_map);
|
||||||
codex_home,
|
inject_git_safe_directory(env_map, cwd);
|
||||||
cwd,
|
|
||||||
env_map,
|
// Use a temp-based log dir that the sandbox user can write.
|
||||||
command,
|
let sandbox_base = codex_home.join(".sandbox");
|
||||||
SpawnPrepOptions {
|
ensure_codex_home_exists(&sandbox_base)?;
|
||||||
inherit_path: true,
|
let logs_base_dir = Some(sandbox_base.clone());
|
||||||
add_git_safe_directory: true,
|
log_start(command, logs_base_dir.as_deref());
|
||||||
},
|
|
||||||
)?;
|
let uses_write_capabilities = permissions.uses_write_capabilities_for_cwd(cwd, env_map);
|
||||||
|
|
||||||
let AllowDenyPaths { allow, deny } =
|
let AllowDenyPaths { allow, deny } =
|
||||||
compute_allow_paths_for_permissions(&common.permissions, &common.current_dir, env_map);
|
compute_allow_paths_for_permissions(&permissions, cwd, env_map);
|
||||||
let write_roots: Vec<PathBuf> = allow.into_iter().collect();
|
let write_roots: Vec<PathBuf> = allow.into_iter().collect();
|
||||||
let deny_write_paths: Vec<PathBuf> = deny.into_iter().collect();
|
let deny_write_paths: Vec<PathBuf> = deny.into_iter().collect();
|
||||||
let computed_write_roots_override = if common.uses_write_capabilities {
|
let computed_write_roots_override = if uses_write_capabilities {
|
||||||
Some(write_roots.as_slice())
|
Some(write_roots.as_slice())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let write_roots_for_setup = write_roots_override.or(computed_write_roots_override);
|
let write_roots_for_setup = write_roots_override.or(computed_write_roots_override);
|
||||||
let effective_write_roots = if common.uses_write_capabilities {
|
let effective_write_roots = if uses_write_capabilities {
|
||||||
effective_write_roots_for_permissions(
|
effective_write_roots_for_permissions(
|
||||||
&common.permissions,
|
&permissions,
|
||||||
&common.current_dir,
|
cwd,
|
||||||
env_map,
|
env_map,
|
||||||
codex_home,
|
codex_home,
|
||||||
write_roots_for_setup,
|
write_roots_for_setup,
|
||||||
@@ -409,13 +407,13 @@ pub(crate) fn prepare_elevated_spawn_context(
|
|||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
let setup_write_roots_override = if common.uses_write_capabilities {
|
let setup_write_roots_override = if uses_write_capabilities {
|
||||||
Some(effective_write_roots.as_slice())
|
Some(effective_write_roots.as_slice())
|
||||||
} else {
|
} else {
|
||||||
write_roots_override
|
write_roots_override
|
||||||
};
|
};
|
||||||
let sandbox_creds = require_logon_sandbox_creds(
|
let sandbox_creds = require_logon_sandbox_creds(
|
||||||
&common.permissions,
|
&permissions,
|
||||||
cwd,
|
cwd,
|
||||||
env_map,
|
env_map,
|
||||||
codex_home,
|
codex_home,
|
||||||
@@ -431,7 +429,7 @@ pub(crate) fn prepare_elevated_spawn_context(
|
|||||||
/*proxy_enforced*/ false,
|
/*proxy_enforced*/ false,
|
||||||
)?;
|
)?;
|
||||||
let caps = load_or_create_cap_sids(codex_home)?;
|
let caps = load_or_create_cap_sids(codex_home)?;
|
||||||
let (psid_to_use, cap_sids) = if common.uses_write_capabilities {
|
let (psid_to_use, cap_sids) = if uses_write_capabilities {
|
||||||
let cap_sids = root_capability_sids(codex_home, cwd, effective_write_roots)?
|
let cap_sids = root_capability_sids(codex_home, cwd, effective_write_roots)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|root_sid| root_sid.sid_str)
|
.map(|root_sid| root_sid.sid_str)
|
||||||
@@ -452,7 +450,8 @@ pub(crate) fn prepare_elevated_spawn_context(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Ok(ElevatedSpawnContext {
|
Ok(ElevatedSpawnContext {
|
||||||
common,
|
sandbox_base,
|
||||||
|
logs_base_dir,
|
||||||
sandbox_creds,
|
sandbox_creds,
|
||||||
cap_sids,
|
cap_sids,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,8 +8,10 @@ use crate::ipc_framed::FramedMessage;
|
|||||||
use crate::ipc_framed::IPC_PROTOCOL_VERSION;
|
use crate::ipc_framed::IPC_PROTOCOL_VERSION;
|
||||||
use crate::ipc_framed::Message;
|
use crate::ipc_framed::Message;
|
||||||
use crate::ipc_framed::SpawnRequest;
|
use crate::ipc_framed::SpawnRequest;
|
||||||
|
use crate::policy::parse_policy;
|
||||||
|
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
|
||||||
use crate::runner_client::spawn_runner_transport;
|
use crate::runner_client::spawn_runner_transport;
|
||||||
use crate::spawn_prep::prepare_elevated_spawn_context;
|
use crate::spawn_prep::prepare_elevated_spawn_context_for_permissions;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
@@ -23,9 +25,9 @@ use tokio::sync::mpsc;
|
|||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) async fn spawn_windows_sandbox_session_elevated(
|
pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profile(
|
||||||
policy_json_or_preset: &str,
|
permission_profile: &PermissionProfile,
|
||||||
sandbox_policy_cwd: &Path,
|
permission_profile_cwd: &Path,
|
||||||
codex_home: &Path,
|
codex_home: &Path,
|
||||||
command: Vec<String>,
|
command: Vec<String>,
|
||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
@@ -48,9 +50,12 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
|
|||||||
.iter()
|
.iter()
|
||||||
.map(AbsolutePathBuf::to_path_buf)
|
.map(AbsolutePathBuf::to_path_buf)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let elevated = prepare_elevated_spawn_context(
|
let permissions = ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_cwd(
|
||||||
policy_json_or_preset,
|
permission_profile,
|
||||||
sandbox_policy_cwd,
|
permission_profile_cwd,
|
||||||
|
)?;
|
||||||
|
let elevated = prepare_elevated_spawn_context_for_permissions(
|
||||||
|
permissions,
|
||||||
codex_home,
|
codex_home,
|
||||||
cwd,
|
cwd,
|
||||||
&mut env_map,
|
&mut env_map,
|
||||||
@@ -62,17 +67,13 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
|
|||||||
&deny_write_paths_override,
|
&deny_write_paths_override,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let permission_profile = PermissionProfile::from_legacy_sandbox_policy_for_cwd(
|
|
||||||
&elevated.common.policy,
|
|
||||||
sandbox_policy_cwd,
|
|
||||||
);
|
|
||||||
let spawn_request = SpawnRequest {
|
let spawn_request = SpawnRequest {
|
||||||
command: command.clone(),
|
command: command.clone(),
|
||||||
cwd: cwd.to_path_buf(),
|
cwd: cwd.to_path_buf(),
|
||||||
env: env_map.clone(),
|
env: env_map.clone(),
|
||||||
permission_profile,
|
permission_profile: permission_profile.clone(),
|
||||||
permission_profile_cwd: sandbox_policy_cwd.to_path_buf(),
|
permission_profile_cwd: permission_profile_cwd.to_path_buf(),
|
||||||
codex_home: elevated.common.sandbox_base.clone(),
|
codex_home: elevated.sandbox_base.clone(),
|
||||||
real_codex_home: codex_home.to_path_buf(),
|
real_codex_home: codex_home.to_path_buf(),
|
||||||
cap_sids: elevated.cap_sids.clone(),
|
cap_sids: elevated.cap_sids.clone(),
|
||||||
timeout_ms,
|
timeout_ms,
|
||||||
@@ -83,7 +84,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
|
|||||||
let codex_home = codex_home.to_path_buf();
|
let codex_home = codex_home.to_path_buf();
|
||||||
let cwd = cwd.to_path_buf();
|
let cwd = cwd.to_path_buf();
|
||||||
let sandbox_creds = elevated.sandbox_creds.clone();
|
let sandbox_creds = elevated.sandbox_creds.clone();
|
||||||
let logs_base_dir = elevated.common.logs_base_dir.clone();
|
let logs_base_dir = elevated.logs_base_dir.clone();
|
||||||
let transport = tokio::task::spawn_blocking(move || -> Result<_> {
|
let transport = tokio::task::spawn_blocking(move || -> Result<_> {
|
||||||
spawn_runner_transport(
|
spawn_runner_transport(
|
||||||
&codex_home,
|
&codex_home,
|
||||||
@@ -144,3 +145,44 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
|
|||||||
stdin_open,
|
stdin_open,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub(crate) async fn spawn_windows_sandbox_session_elevated(
|
||||||
|
policy_json_or_preset: &str,
|
||||||
|
sandbox_policy_cwd: &Path,
|
||||||
|
codex_home: &Path,
|
||||||
|
command: Vec<String>,
|
||||||
|
cwd: &Path,
|
||||||
|
env_map: HashMap<String, String>,
|
||||||
|
timeout_ms: Option<u64>,
|
||||||
|
read_roots_override: Option<&[PathBuf]>,
|
||||||
|
read_roots_include_platform_defaults: bool,
|
||||||
|
write_roots_override: Option<&[PathBuf]>,
|
||||||
|
deny_read_paths_override: &[AbsolutePathBuf],
|
||||||
|
deny_write_paths_override: &[AbsolutePathBuf],
|
||||||
|
tty: bool,
|
||||||
|
stdin_open: bool,
|
||||||
|
use_private_desktop: bool,
|
||||||
|
) -> Result<SpawnedProcess> {
|
||||||
|
let policy = parse_policy(policy_json_or_preset)?;
|
||||||
|
let permission_profile =
|
||||||
|
PermissionProfile::from_legacy_sandbox_policy_for_cwd(&policy, sandbox_policy_cwd);
|
||||||
|
spawn_windows_sandbox_session_elevated_for_permission_profile(
|
||||||
|
&permission_profile,
|
||||||
|
sandbox_policy_cwd,
|
||||||
|
codex_home,
|
||||||
|
command,
|
||||||
|
cwd,
|
||||||
|
env_map,
|
||||||
|
timeout_ms,
|
||||||
|
read_roots_override,
|
||||||
|
read_roots_include_platform_defaults,
|
||||||
|
write_roots_override,
|
||||||
|
deny_read_paths_override,
|
||||||
|
deny_write_paths_override,
|
||||||
|
tty,
|
||||||
|
stdin_open,
|
||||||
|
use_private_desktop,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
mod backends;
|
mod backends;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use codex_protocol::models::PermissionProfile;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
use codex_utils_pty::SpawnedProcess;
|
use codex_utils_pty::SpawnedProcess;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -48,6 +49,44 @@ pub async fn spawn_windows_sandbox_session_legacy(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub async fn spawn_windows_sandbox_session_elevated_for_permission_profile(
|
||||||
|
permission_profile: &PermissionProfile,
|
||||||
|
permission_profile_cwd: &Path,
|
||||||
|
codex_home: &Path,
|
||||||
|
command: Vec<String>,
|
||||||
|
cwd: &Path,
|
||||||
|
env_map: HashMap<String, String>,
|
||||||
|
timeout_ms: Option<u64>,
|
||||||
|
read_roots_override: Option<&[PathBuf]>,
|
||||||
|
read_roots_include_platform_defaults: bool,
|
||||||
|
write_roots_override: Option<&[PathBuf]>,
|
||||||
|
deny_read_paths_override: &[AbsolutePathBuf],
|
||||||
|
deny_write_paths_override: &[AbsolutePathBuf],
|
||||||
|
tty: bool,
|
||||||
|
stdin_open: bool,
|
||||||
|
use_private_desktop: bool,
|
||||||
|
) -> Result<SpawnedProcess> {
|
||||||
|
backends::elevated::spawn_windows_sandbox_session_elevated_for_permission_profile(
|
||||||
|
permission_profile,
|
||||||
|
permission_profile_cwd,
|
||||||
|
codex_home,
|
||||||
|
command,
|
||||||
|
cwd,
|
||||||
|
env_map,
|
||||||
|
timeout_ms,
|
||||||
|
read_roots_override,
|
||||||
|
read_roots_include_platform_defaults,
|
||||||
|
write_roots_override,
|
||||||
|
deny_read_paths_override,
|
||||||
|
deny_write_paths_override,
|
||||||
|
tty,
|
||||||
|
stdin_open,
|
||||||
|
use_private_desktop,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn spawn_windows_sandbox_session_elevated(
|
pub async fn spawn_windows_sandbox_session_elevated(
|
||||||
policy_json_or_preset: &str,
|
policy_json_or_preset: &str,
|
||||||
|
|||||||
Reference in new issue
Block a user