Merge f878b75dbb into sapling-pr-archive-bolinfest

This commit is contained in:
Michael Bolin
2026-06-03 11:18:12 -07:00
committed by GitHub
13 changed files with 230 additions and 275 deletions
@@ -179,11 +179,8 @@ pub(crate) fn thread_response_sandbox_policy(
permission_profile: &codex_protocol::models::PermissionProfile,
cwd: &Path,
) -> codex_app_server_protocol::SandboxPolicy {
let file_system_policy = permission_profile.file_system_sandbox_policy();
let sandbox_policy = codex_sandboxing::compatibility_sandbox_policy_for_permission_profile(
permission_profile,
&file_system_policy,
permission_profile.network_sandbox_policy(),
cwd,
);
sandbox_policy.into()
@@ -524,7 +524,10 @@ impl TurnRequestProcessor {
// `thread/settings/update` only acknowledges that the update was queued.
// Clients that send dependent partial updates should wait for
// `thread/settings/updated` or combine the fields in one request.
let snapshot = if permissions.is_some() || runtime_workspace_roots_request.is_some() {
let snapshot = if sandbox_policy.is_some()
|| permissions.is_some()
|| runtime_workspace_roots_request.is_some()
{
Some(thread.config_snapshot().await)
} else {
None
@@ -564,7 +567,7 @@ impl TurnRequestProcessor {
let approvals_reviewer =
approvals_reviewer.map(codex_app_server_protocol::ApprovalsReviewer::to_core);
let sandbox_policy = sandbox_policy.map(|policy| policy.to_core());
let (permission_profile, active_permission_profile, profile_workspace_roots) =
let (permission_profile, active_permission_profile, profile_workspace_roots) = {
if let Some(permissions) = permissions {
let Some(snapshot) = snapshot.as_ref() else {
return Err(internal_error(format!(
@@ -611,9 +614,32 @@ impl TurnRequestProcessor {
config.permissions.active_permission_profile(),
Some(config.permissions.profile_workspace_roots().to_vec()),
)
} else if let Some(sandbox_policy) = sandbox_policy.as_ref() {
let Some(snapshot) = snapshot.as_ref() else {
return Err(internal_error(format!(
"{method} sandbox policy missing thread snapshot"
)));
};
let sandbox_cwd = cwd
.as_ref()
.map(|cwd| {
AbsolutePathBuf::resolve_path_against_base(cwd, snapshot.cwd.as_path())
})
.unwrap_or_else(|| snapshot.cwd.clone());
(
Some(
codex_protocol::models::PermissionProfile::from_legacy_sandbox_policy_for_cwd(
sandbox_policy,
sandbox_cwd.as_path(),
),
),
None,
None,
)
} else {
(None, None, None)
};
}
};
let effort = effort.map(Some);
if has_any_overrides {
@@ -623,7 +649,6 @@ impl TurnRequestProcessor {
workspace_roots: runtime_workspace_roots.clone(),
approval_policy,
approvals_reviewer,
sandbox_policy: sandbox_policy.clone(),
permission_profile: permission_profile.clone(),
active_permission_profile: active_permission_profile.clone(),
profile_workspace_roots: profile_workspace_roots.clone(),
@@ -647,7 +672,7 @@ impl TurnRequestProcessor {
profile_workspace_roots,
approval_policy,
approvals_reviewer,
sandbox_policy,
sandbox_policy: None,
permission_profile,
active_permission_profile,
windows_sandbox_level: None,
-6
View File
@@ -76,11 +76,8 @@ pub struct ThreadConfigSnapshot {
impl ThreadConfigSnapshot {
pub fn sandbox_policy(&self) -> SandboxPolicy {
let file_system_sandbox_policy = self.permission_profile.file_system_sandbox_policy();
codex_sandboxing::compatibility_sandbox_policy_for_permission_profile(
&self.permission_profile,
&file_system_sandbox_policy,
self.permission_profile.network_sandbox_policy(),
self.cwd.as_path(),
)
}
@@ -94,7 +91,6 @@ pub struct CodexThreadSettingsOverrides {
pub profile_workspace_roots: Option<Vec<AbsolutePathBuf>>,
pub approval_policy: Option<AskForApproval>,
pub approvals_reviewer: Option<ApprovalsReviewer>,
pub sandbox_policy: Option<SandboxPolicy>,
pub permission_profile: Option<PermissionProfile>,
pub active_permission_profile: Option<ActivePermissionProfile>,
pub windows_sandbox_level: Option<WindowsSandboxLevel>,
@@ -321,7 +317,6 @@ impl CodexThread {
profile_workspace_roots,
approval_policy,
approvals_reviewer,
sandbox_policy,
permission_profile,
active_permission_profile,
windows_sandbox_level,
@@ -348,7 +343,6 @@ impl CodexThread {
profile_workspace_roots,
approval_policy,
approvals_reviewer,
sandbox_policy,
permission_profile,
active_permission_profile,
windows_sandbox_level,
+1 -7
View File
@@ -444,13 +444,7 @@ impl Permissions {
/// Legacy compatibility projection derived from the canonical profile.
pub fn legacy_sandbox_policy(&self, cwd: &Path) -> SandboxPolicy {
let permission_profile = self.materialized_permission_profile();
let file_system_sandbox_policy = permission_profile.file_system_sandbox_policy();
compatibility_sandbox_policy_for_permission_profile(
&permission_profile,
&file_system_sandbox_policy,
permission_profile.network_sandbox_policy(),
cwd,
)
compatibility_sandbox_policy_for_permission_profile(&permission_profile, cwd)
}
/// Check whether a legacy sandbox policy can be applied to this permission
+52 -57
View File
@@ -31,19 +31,18 @@ use codex_protocol::error::SandboxErr;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::exec_output::StreamOutput;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemSandboxKind;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::Event;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::ExecCommandOutputDeltaEvent;
use codex_protocol::protocol::ExecOutputStream;
use codex_protocol::protocol::SandboxPolicy;
use codex_sandboxing::SandboxCommand;
use codex_sandboxing::SandboxManager;
use codex_sandboxing::SandboxTransformRequest;
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_pty::DEFAULT_OUTPUT_BYTES_CAP;
use codex_utils_pty::process_group::kill_child_process_group;
@@ -419,22 +418,17 @@ pub fn build_exec_request(
exec_req.windows_sandbox_level,
exec_req.network.is_some(),
);
let sandbox_policy = exec_req.compatibility_sandbox_policy();
exec_req.windows_sandbox_filesystem_overrides = if use_windows_elevated_backend {
resolve_windows_elevated_filesystem_overrides(
exec_req.sandbox,
&sandbox_policy,
&exec_req.file_system_sandbox_policy,
exec_req.network_sandbox_policy,
&exec_req.permission_profile,
sandbox_cwd,
use_windows_elevated_backend,
)
} else {
resolve_windows_restricted_token_filesystem_overrides(
exec_req.sandbox,
&sandbox_policy,
&exec_req.file_system_sandbox_policy,
exec_req.network_sandbox_policy,
&exec_req.permission_profile,
sandbox_cwd,
exec_req.windows_sandbox_level,
)
@@ -1004,34 +998,28 @@ async fn exec(
}
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
fn should_use_windows_restricted_token_sandbox(
sandbox: SandboxType,
sandbox_policy: &SandboxPolicy,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
fn permission_profile_supports_windows_restricted_token_sandbox(
permission_profile: &PermissionProfile,
) -> bool {
sandbox == SandboxType::WindowsRestrictedToken
&& file_system_sandbox_policy.kind == FileSystemSandboxKind::Restricted
&& !matches!(
sandbox_policy,
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. }
)
match permission_profile {
PermissionProfile::Managed { file_system, .. } => {
!file_system.to_sandbox_policy().has_full_disk_write_access()
}
PermissionProfile::Disabled | PermissionProfile::External { .. } => false,
}
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) fn unsupported_windows_restricted_token_sandbox_reason(
sandbox: SandboxType,
sandbox_policy: &SandboxPolicy,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
permission_profile: &PermissionProfile,
sandbox_policy_cwd: &AbsolutePathBuf,
windows_sandbox_level: WindowsSandboxLevel,
) -> Option<String> {
if windows_sandbox_level == WindowsSandboxLevel::Elevated {
resolve_windows_elevated_filesystem_overrides(
sandbox,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
permission_profile,
sandbox_policy_cwd,
windows_sandbox_level == WindowsSandboxLevel::Elevated,
)
@@ -1039,9 +1027,7 @@ pub(crate) fn unsupported_windows_restricted_token_sandbox_reason(
} else {
resolve_windows_restricted_token_filesystem_overrides(
sandbox,
sandbox_policy,
file_system_sandbox_policy,
network_sandbox_policy,
permission_profile,
sandbox_policy_cwd,
windows_sandbox_level,
)
@@ -1051,9 +1037,7 @@ pub(crate) fn unsupported_windows_restricted_token_sandbox_reason(
pub(crate) fn resolve_windows_restricted_token_filesystem_overrides(
sandbox: SandboxType,
sandbox_policy: &SandboxPolicy,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
permission_profile: &PermissionProfile,
sandbox_policy_cwd: &AbsolutePathBuf,
windows_sandbox_level: WindowsSandboxLevel,
) -> std::result::Result<Option<WindowsSandboxFilesystemOverrides>, String> {
@@ -1063,25 +1047,22 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides(
return Ok(None);
}
let (file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
let needs_direct_runtime_enforcement = file_system_sandbox_policy
.needs_direct_runtime_enforcement(network_sandbox_policy, sandbox_policy_cwd);
if should_use_windows_restricted_token_sandbox(
sandbox,
sandbox_policy,
file_system_sandbox_policy,
) && !needs_direct_runtime_enforcement
if permission_profile_supports_windows_restricted_token_sandbox(permission_profile)
&& !needs_direct_runtime_enforcement
{
return Ok(None);
}
if !should_use_windows_restricted_token_sandbox(
sandbox,
sandbox_policy,
file_system_sandbox_policy,
) {
if !permission_profile_supports_windows_restricted_token_sandbox(permission_profile) {
let permission_profile_name = permission_profile_display_name(permission_profile);
return Err(format!(
"windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, legacy_policy={sandbox_policy:?}; refusing to run unsandboxed",
"windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, permission_profile={permission_profile_name}; refusing to run unsandboxed",
file_system_sandbox_policy.kind,
));
}
@@ -1090,7 +1071,7 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides(
// but its WRITE_RESTRICTED token does not make capability SID deny-read ACEs
// participate in read access checks. Read restrictions therefore require the
// elevated backend, even when the filesystem root remains readable.
if !windows_policy_has_root_read_access(file_system_sandbox_policy, sandbox_policy_cwd) {
if !windows_policy_has_root_read_access(&file_system_sandbox_policy, sandbox_policy_cwd) {
return Err(
"windows unelevated restricted-token sandbox cannot enforce split filesystem read restrictions directly; refusing to run unsandboxed"
.to_string(),
@@ -1098,7 +1079,7 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides(
}
let additional_deny_read_paths = codex_windows_sandbox::resolve_windows_deny_read_paths(
file_system_sandbox_policy,
&file_system_sandbox_policy,
sandbox_policy_cwd,
)?;
if !additional_deny_read_paths.is_empty() {
@@ -1108,7 +1089,11 @@ pub(crate) fn resolve_windows_restricted_token_filesystem_overrides(
);
}
let legacy_writable_roots = sandbox_policy.get_writable_roots_with_cwd(sandbox_policy_cwd);
let legacy_projection = compatibility_sandbox_policy_for_permission_profile(
permission_profile,
sandbox_policy_cwd.as_path(),
);
let legacy_writable_roots = legacy_projection.get_writable_roots_with_cwd(sandbox_policy_cwd);
let split_writable_roots =
file_system_sandbox_policy.get_writable_roots_with_cwd(sandbox_policy_cwd);
let legacy_root_paths: BTreeSet<PathBuf> = legacy_writable_roots
@@ -1204,9 +1189,7 @@ fn windows_policy_has_root_read_access(
pub(crate) fn resolve_windows_elevated_filesystem_overrides(
sandbox: SandboxType,
sandbox_policy: &SandboxPolicy,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
network_sandbox_policy: NetworkSandboxPolicy,
permission_profile: &PermissionProfile,
sandbox_policy_cwd: &AbsolutePathBuf,
use_windows_elevated_backend: bool,
) -> std::result::Result<Option<WindowsSandboxFilesystemOverrides>, String> {
@@ -1214,19 +1197,19 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides(
return Ok(None);
}
if !should_use_windows_restricted_token_sandbox(
sandbox,
sandbox_policy,
file_system_sandbox_policy,
) {
let (file_system_sandbox_policy, network_sandbox_policy) =
permission_profile.to_runtime_permissions();
if !permission_profile_supports_windows_restricted_token_sandbox(permission_profile) {
let permission_profile_name = permission_profile_display_name(permission_profile);
return Err(format!(
"windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, legacy_policy={sandbox_policy:?}; refusing to run unsandboxed",
"windows sandbox backend cannot enforce file_system={:?}, network={network_sandbox_policy:?}, permission_profile={permission_profile_name}; refusing to run unsandboxed",
file_system_sandbox_policy.kind,
));
}
let additional_deny_read_paths = codex_windows_sandbox::resolve_windows_deny_read_paths(
file_system_sandbox_policy,
&file_system_sandbox_policy,
sandbox_policy_cwd,
)?;
@@ -1242,7 +1225,11 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides(
let needs_direct_runtime_enforcement = file_system_sandbox_policy
.needs_direct_runtime_enforcement(network_sandbox_policy, sandbox_policy_cwd);
let normalize_path = |path: PathBuf| dunce::canonicalize(&path).unwrap_or(path);
let legacy_writable_roots = sandbox_policy.get_writable_roots_with_cwd(sandbox_policy_cwd);
let legacy_projection = compatibility_sandbox_policy_for_permission_profile(
permission_profile,
sandbox_policy_cwd.as_path(),
);
let legacy_writable_roots = legacy_projection.get_writable_roots_with_cwd(sandbox_policy_cwd);
let legacy_root_paths: BTreeSet<PathBuf> = legacy_writable_roots
.iter()
.map(|root| normalize_path(root.root.to_path_buf()))
@@ -1264,7 +1251,7 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides(
// whether the baseline still reads from the filesystem root and only needs
// additional deny ACLs layered on top.
let split_has_root_read_access =
windows_policy_has_root_read_access(file_system_sandbox_policy, sandbox_policy_cwd);
windows_policy_has_root_read_access(&file_system_sandbox_policy, sandbox_policy_cwd);
let read_roots_override = if split_has_root_read_access {
None
} else {
@@ -1329,6 +1316,14 @@ pub(crate) fn resolve_windows_elevated_filesystem_overrides(
}))
}
fn permission_profile_display_name(permission_profile: &PermissionProfile) -> &'static str {
match permission_profile {
PermissionProfile::Managed { .. } => "Managed",
PermissionProfile::Disabled => "Disabled",
PermissionProfile::External { .. } => "External",
}
}
fn has_reopened_writable_descendant(
writable_roots: &[codex_protocol::protocol::WritableRoot],
) -> bool {
+106 -133
View File
@@ -379,34 +379,18 @@ async fn process_exec_tool_call_preserves_full_buffer_capture_policy() -> Result
#[test]
fn windows_restricted_token_skips_external_sandbox_policies() {
let policy = SandboxPolicy::ExternalSandbox {
network_access: codex_protocol::protocol::NetworkAccess::Restricted,
let permission_profile = PermissionProfile::External {
network: NetworkSandboxPolicy::Restricted,
};
let file_system_policy = FileSystemSandboxPolicy::from(&policy);
assert_eq!(
should_use_windows_restricted_token_sandbox(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
),
false
);
assert!(!permission_profile_supports_windows_restricted_token_sandbox(&permission_profile));
}
#[test]
fn windows_restricted_token_runs_for_legacy_restricted_policies() {
let policy = SandboxPolicy::new_read_only_policy();
let file_system_policy = FileSystemSandboxPolicy::from(&policy);
fn windows_restricted_token_supports_read_only_profiles() {
let permission_profile = PermissionProfile::read_only();
assert_eq!(
should_use_windows_restricted_token_sandbox(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
),
true
);
assert!(permission_profile_supports_windows_restricted_token_sandbox(&permission_profile));
}
#[test]
@@ -427,39 +411,64 @@ fn windows_proxy_enforcement_uses_elevated_backend() {
#[test]
fn windows_restricted_token_rejects_network_only_restrictions() {
let policy = SandboxPolicy::ExternalSandbox {
network_access: codex_protocol::protocol::NetworkAccess::Restricted,
};
let file_system_policy = FileSystemSandboxPolicy::unrestricted();
let permission_profile = PermissionProfile::from_runtime_permissions(
&FileSystemSandboxPolicy::unrestricted(),
NetworkSandboxPolicy::Restricted,
);
let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd");
assert_eq!(
unsupported_windows_restricted_token_sandbox_reason(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&sandbox_policy_cwd,
WindowsSandboxLevel::RestrictedToken,
),
Some(
"windows sandbox backend cannot enforce file_system=Unrestricted, network=Restricted, legacy_policy=ExternalSandbox { network_access: Restricted }; refusing to run unsandboxed".to_string()
"windows sandbox backend cannot enforce file_system=Unrestricted, network=Restricted, permission_profile=Managed; refusing to run unsandboxed".to_string()
)
);
}
#[test]
fn windows_restricted_token_allows_legacy_restricted_policies() {
let policy = SandboxPolicy::new_read_only_policy();
let file_system_policy = FileSystemSandboxPolicy::from(&policy);
fn windows_restricted_token_rejects_managed_root_write_profiles() {
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
value: codex_protocol::permissions::FileSystemSpecialPath::Root,
},
access: codex_protocol::permissions::FileSystemAccessMode::Write,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd");
assert_eq!(
unsupported_windows_restricted_token_sandbox_reason(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&sandbox_policy_cwd,
WindowsSandboxLevel::RestrictedToken,
),
Some(
"windows sandbox backend cannot enforce file_system=Restricted, network=Restricted, permission_profile=Managed; refusing to run unsandboxed"
.to_string()
)
);
}
#[test]
fn windows_restricted_token_allows_read_only_profiles() {
let permission_profile = PermissionProfile::read_only();
let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd");
assert_eq!(
unsupported_windows_restricted_token_sandbox_reason(
SandboxType::WindowsRestrictedToken,
&permission_profile,
&sandbox_policy_cwd,
WindowsSandboxLevel::RestrictedToken,
),
@@ -468,22 +477,19 @@ fn windows_restricted_token_allows_legacy_restricted_policies() {
}
#[test]
fn windows_restricted_token_allows_legacy_workspace_write_policies() {
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let file_system_policy = FileSystemSandboxPolicy::from(&policy);
fn windows_restricted_token_allows_workspace_write_profiles() {
let permission_profile = PermissionProfile::workspace_write_with(
&[],
NetworkSandboxPolicy::Restricted,
/*exclude_tmpdir_env_var*/ true,
/*exclude_slash_tmp*/ true,
);
let sandbox_policy_cwd = AbsolutePathBuf::current_dir().expect("cwd");
assert_eq!(
unsupported_windows_restricted_token_sandbox_reason(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&sandbox_policy_cwd,
WindowsSandboxLevel::RestrictedToken,
),
@@ -499,22 +505,21 @@ fn windows_elevated_allows_split_restricted_read_policies() {
)
.expect("absolute docs");
std::fs::create_dir_all(docs.as_path()).expect("create docs");
let policy = SandboxPolicy::ReadOnly {
network_access: false,
};
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path { path: docs },
access: codex_protocol::permissions::FileSystemAccessMode::Read,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
unsupported_windows_restricted_token_sandbox_reason(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&temp_dir.path().abs(),
WindowsSandboxLevel::Elevated,
),
@@ -527,12 +532,6 @@ fn windows_restricted_token_rejects_split_only_filesystem_policies() {
let temp_dir = tempfile::TempDir::new().expect("tempdir");
let docs = temp_dir.path().join("docs");
std::fs::create_dir_all(&docs).expect("create docs");
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -550,13 +549,15 @@ fn windows_restricted_token_rejects_split_only_filesystem_policies() {
access: codex_protocol::permissions::FileSystemAccessMode::Read,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
unsupported_windows_restricted_token_sandbox_reason(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&temp_dir.path().abs(),
WindowsSandboxLevel::RestrictedToken,
),
@@ -572,12 +573,6 @@ fn windows_restricted_token_rejects_root_write_read_only_carveouts() {
let temp_dir = tempfile::TempDir::new().expect("tempdir");
let docs = temp_dir.path().join("docs");
std::fs::create_dir_all(&docs).expect("create docs");
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -593,13 +588,15 @@ fn windows_restricted_token_rejects_root_write_read_only_carveouts() {
access: codex_protocol::permissions::FileSystemAccessMode::Read,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
unsupported_windows_restricted_token_sandbox_reason(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&temp_dir.path().abs(),
WindowsSandboxLevel::RestrictedToken,
),
@@ -618,12 +615,6 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() {
.abs();
let docs = cwd.join("docs");
std::fs::create_dir_all(docs.as_path()).expect("create docs");
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -644,17 +635,20 @@ fn windows_restricted_token_supports_full_read_split_write_read_carveouts() {
access: codex_protocol::permissions::FileSystemAccessMode::Read,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
// The legacy workspace-write root already protects top-level `.codex`, so
// the restricted-token overlay only needs the extra read-only docs carveout.
// The workspace-write compatibility projection already protects top-level
// `.codex`, so the restricted-token overlay only needs the extra read-only
// docs carveout.
let expected_deny_write_paths = vec![docs];
assert_eq!(
resolve_windows_restricted_token_filesystem_overrides(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&cwd,
WindowsSandboxLevel::RestrictedToken,
),
@@ -676,12 +670,6 @@ fn windows_restricted_token_rejects_unreadable_split_carveouts() {
.abs();
let blocked = cwd.join("blocked");
std::fs::create_dir_all(blocked.as_path()).expect("create blocked");
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -702,13 +690,15 @@ fn windows_restricted_token_rejects_unreadable_split_carveouts() {
access: codex_protocol::permissions::FileSystemAccessMode::Deny,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
resolve_windows_restricted_token_filesystem_overrides(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&cwd,
WindowsSandboxLevel::RestrictedToken,
),
@@ -725,9 +715,6 @@ fn windows_elevated_supports_split_restricted_read_roots() {
let docs = temp_dir.path().join("docs");
std::fs::create_dir_all(&docs).expect("create docs");
let expected_docs = dunce::canonicalize(&docs).expect("canonical docs");
let policy = SandboxPolicy::ReadOnly {
network_access: false,
};
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Path {
@@ -737,13 +724,15 @@ fn windows_elevated_supports_split_restricted_read_roots() {
access: codex_protocol::permissions::FileSystemAccessMode::Read,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
resolve_windows_elevated_filesystem_overrides(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&temp_dir.path().abs(),
/*use_windows_elevated_backend*/ true,
),
@@ -763,12 +752,6 @@ fn windows_elevated_supports_split_write_read_carveouts() {
let docs = temp_dir.path().join("docs");
std::fs::create_dir_all(&docs).expect("create docs");
let expected_docs = dunce::canonicalize(&docs).expect("canonical docs");
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -792,13 +775,15 @@ fn windows_elevated_supports_split_write_read_carveouts() {
access: codex_protocol::permissions::FileSystemAccessMode::Read,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
resolve_windows_elevated_filesystem_overrides(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&temp_dir.path().abs(),
/*use_windows_elevated_backend*/ true,
),
@@ -821,12 +806,6 @@ fn windows_elevated_supports_unreadable_split_carveouts() {
let blocked = temp_dir.path().join("blocked");
std::fs::create_dir_all(&blocked).expect("create blocked");
let expected_blocked = dunce::canonicalize(&blocked).expect("canonical blocked");
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -850,13 +829,15 @@ fn windows_elevated_supports_unreadable_split_carveouts() {
access: codex_protocol::permissions::FileSystemAccessMode::Deny,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
resolve_windows_elevated_filesystem_overrides(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&temp_dir.path().abs(),
/*use_windows_elevated_backend*/ true,
),
@@ -884,12 +865,6 @@ fn windows_elevated_supports_unreadable_globs() {
let secret = temp_dir.path().join("app").join(".env");
std::fs::create_dir_all(secret.parent().expect("parent")).expect("create parent");
std::fs::write(&secret, "secret").expect("write secret");
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -912,13 +887,15 @@ fn windows_elevated_supports_unreadable_globs() {
access: codex_protocol::permissions::FileSystemAccessMode::Deny,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
resolve_windows_elevated_filesystem_overrides(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&temp_dir.path().abs(),
/*use_windows_elevated_backend*/ true,
),
@@ -941,12 +918,6 @@ fn windows_elevated_rejects_reopened_writable_descendants() {
let docs = temp_dir.path().join("docs");
let nested = docs.join("nested");
std::fs::create_dir_all(&nested).expect("create nested");
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![
codex_protocol::permissions::FileSystemSandboxEntry {
path: codex_protocol::permissions::FileSystemPath::Special {
@@ -977,13 +948,15 @@ fn windows_elevated_rejects_reopened_writable_descendants() {
access: codex_protocol::permissions::FileSystemAccessMode::Write,
},
]);
let permission_profile = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
assert_eq!(
unsupported_windows_restricted_token_sandbox_reason(
SandboxType::WindowsRestrictedToken,
&policy,
&file_system_policy,
NetworkSandboxPolicy::Restricted,
&permission_profile,
&temp_dir.path().abs(),
WindowsSandboxLevel::Elevated,
),
-11
View File
@@ -22,10 +22,8 @@ use codex_protocol::models::PermissionProfile;
pub use codex_protocol::models::SandboxPermissions;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::SandboxPolicy;
use codex_sandboxing::SandboxExecRequest;
use codex_sandboxing::SandboxType;
use codex_sandboxing::compatibility_sandbox_policy_for_permission_profile;
use codex_utils_absolute_path::AbsolutePathBuf;
use std::collections::HashMap;
@@ -102,15 +100,6 @@ impl ExecRequest {
}
}
pub(crate) fn compatibility_sandbox_policy(&self) -> SandboxPolicy {
compatibility_sandbox_policy_for_permission_profile(
&self.permission_profile,
&self.file_system_sandbox_policy,
self.network_sandbox_policy,
self.windows_sandbox_policy_cwd.as_path(),
)
}
pub(crate) fn from_sandbox_exec_request(
request: SandboxExecRequest,
options: ExecOptions,
+19 -1
View File
@@ -26,6 +26,7 @@ use crate::tasks::UserShellCommandMode;
use crate::tasks::UserShellCommandTask;
use crate::tasks::execute_user_shell_command;
use codex_protocol::models::ContentItem;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::ResponseInputItem;
use codex_protocol::models::ResponseItem;
use codex_protocol::protocol::CodexErrorInfo;
@@ -138,6 +139,24 @@ async fn thread_settings_update(
collaboration_mode,
personality,
} = thread_settings;
let (permission_profile, active_permission_profile) =
if let Some(permission_profile) = permission_profile {
(Some(permission_profile), active_permission_profile)
} else if let Some(sandbox_policy) = sandbox_policy {
let state = sess.state.lock().await;
let update_cwd = state
.session_configuration
.resolved_cwd_for_update(cwd.as_ref());
(
Some(PermissionProfile::from_legacy_sandbox_policy_for_cwd(
&sandbox_policy,
update_cwd.as_path(),
)),
None,
)
} else {
(None, active_permission_profile)
};
let collaboration_mode = match collaboration_mode {
Some(collaboration_mode) => collaboration_mode,
None => {
@@ -156,7 +175,6 @@ async fn thread_settings_update(
profile_workspace_roots,
approval_policy,
approvals_reviewer,
sandbox_policy,
permission_profile,
active_permission_profile,
windows_sandbox_level,
+17 -42
View File
@@ -150,17 +150,11 @@ impl SessionConfiguration {
}
pub(super) fn sandbox_policy(&self) -> SandboxPolicy {
self.permission_profile()
.to_legacy_sandbox_policy(&self.cwd)
.unwrap_or_else(|_| {
let file_system_sandbox_policy = self.file_system_sandbox_policy();
codex_sandboxing::compatibility_sandbox_policy_for_permission_profile(
self.permission_profile_state.permission_profile(),
&file_system_sandbox_policy,
self.network_sandbox_policy(),
&self.cwd,
)
})
let permission_profile = self.permission_profile();
codex_sandboxing::compatibility_sandbox_policy_for_permission_profile(
&permission_profile,
&self.cwd,
)
}
pub(super) fn file_system_sandbox_policy(&self) -> FileSystemSandboxPolicy {
@@ -254,19 +248,7 @@ impl SessionConfiguration {
next_configuration.windows_sandbox_level = windows_sandbox_level;
}
let absolute_cwd = updates
.cwd
.as_ref()
.map(|cwd| {
AbsolutePathBuf::relative_to_current_dir(normalize_for_native_workdir(
cwd.as_path(),
))
.unwrap_or_else(|e| {
warn!("failed to normalize update cwd: {cwd:?}: {e}");
self.cwd.clone()
})
})
.unwrap_or_else(|| self.cwd.clone());
let absolute_cwd = self.resolved_cwd_for_update(updates.cwd.as_ref());
let cwd_changed = absolute_cwd.as_path() != self.cwd.as_path();
next_configuration.cwd = absolute_cwd;
@@ -330,23 +312,6 @@ impl SessionConfiguration {
)?;
next_configuration.original_config_do_not_use = Arc::new(config);
}
} else if let Some(sandbox_policy) = updates.sandbox_policy.clone() {
let file_system_sandbox_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy_preserving_deny_entries(
&sandbox_policy,
&next_configuration.cwd,
&current_file_system_sandbox_policy,
);
let network_sandbox_policy = NetworkSandboxPolicy::from(&sandbox_policy);
next_configuration
.permission_profile_state
.set_legacy_permission_profile(
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&sandbox_policy),
&file_system_sandbox_policy,
network_sandbox_policy,
),
)?;
} else if cwd_changed
&& file_system_policy_matches_legacy
&& file_system_policy_has_rebindable_project_root_write
@@ -379,6 +344,17 @@ impl SessionConfiguration {
Ok(next_configuration)
}
pub(crate) fn resolved_cwd_for_update(&self, cwd: Option<&PathBuf>) -> AbsolutePathBuf {
cwd.map(|cwd| {
AbsolutePathBuf::relative_to_current_dir(normalize_for_native_workdir(cwd.as_path()))
.unwrap_or_else(|e| {
warn!("failed to normalize update cwd: {cwd:?}: {e}");
self.cwd.clone()
})
})
.unwrap_or_else(|| self.cwd.clone())
}
fn set_permission_profile_projection(
&mut self,
permission_profile: PermissionProfile,
@@ -423,7 +399,6 @@ pub(crate) struct SessionSettingsUpdate {
pub(crate) profile_workspace_roots: Option<Vec<AbsolutePathBuf>>,
pub(crate) approval_policy: Option<AskForApproval>,
pub(crate) approvals_reviewer: Option<ApprovalsReviewer>,
pub(crate) sandbox_policy: Option<SandboxPolicy>,
pub(crate) permission_profile: Option<PermissionProfile>,
pub(crate) active_permission_profile: Option<ActivePermissionProfile>,
pub(crate) windows_sandbox_level: Option<WindowsSandboxLevel>,
+2 -2
View File
@@ -916,9 +916,9 @@ async fn new_turn_refreshes_managed_network_proxy_for_sandbox_change() -> anyhow
session
.new_turn_with_sub_id(
"sandbox-policy-change".to_string(),
"permission-profile-change".to_string(),
SessionSettingsUpdate {
sandbox_policy: Some(SandboxPolicy::DangerFullAccess),
permission_profile: Some(PermissionProfile::Disabled),
..Default::default()
},
)
@@ -124,12 +124,8 @@ impl TurnContext {
}
pub(crate) fn sandbox_policy(&self) -> SandboxPolicy {
let file_system_sandbox_policy = self.file_system_sandbox_policy();
let network_sandbox_policy = self.network_sandbox_policy();
compatibility_sandbox_policy_for_permission_profile(
&self.permission_profile,
&file_system_sandbox_policy,
network_sandbox_policy,
#[allow(deprecated)]
&self.cwd,
)
+1 -1
View File
@@ -30,7 +30,7 @@ pub enum StdioPolicy {
Inherit,
}
/// Spawns the appropriate child process for the ExecParams and SandboxPolicy,
/// Spawns the appropriate child process for the exec params and sandbox settings,
/// ensuring the args and environment variables used to create the `Command`
/// (and `Child`) honor the configuration.
///
+2 -3
View File
@@ -290,19 +290,18 @@ impl SandboxManager {
pub fn compatibility_sandbox_policy_for_permission_profile(
permissions: &PermissionProfile,
file_system_policy: &FileSystemSandboxPolicy,
network_policy: NetworkSandboxPolicy,
cwd: &Path,
) -> SandboxPolicy {
permissions
.to_legacy_sandbox_policy(cwd)
.unwrap_or_else(|_| {
let (file_system_policy, network_policy) = permissions.to_runtime_permissions();
compatibility_workspace_write_policy(file_system_policy, network_policy, cwd)
})
}
fn compatibility_workspace_write_policy(
file_system_policy: &FileSystemSandboxPolicy,
file_system_policy: FileSystemSandboxPolicy,
network_policy: NetworkSandboxPolicy,
cwd: &Path,
) -> SandboxPolicy {