mirror of
https://github.com/openai/codex.git
synced 2026-10-30 19:18:49 +00:00
Move workspace roots onto thread/session state and stop using active permission profile modifications as an overlay for writable roots. Existing app-server threads now preserve their persisted PermissionProfile value across resume, fork, and turn updates; permissions requests on existing threads only update the active named profile after validating it exists. Workspace roots can be updated independently, and SandboxPolicy::WorkspaceWrite no longer stores its own writable_roots.
This commit is contained in:
@@ -97,6 +97,7 @@ pub struct SandboxTransformRequest<'a> {
|
||||
// to make shared ownership explicit across runtime/sandbox plumbing.
|
||||
pub network: Option<&'a NetworkProxy>,
|
||||
pub sandbox_policy_cwd: &'a Path,
|
||||
pub workspace_roots: &'a [AbsolutePathBuf],
|
||||
pub codex_linux_sandbox_exe: Option<&'a Path>,
|
||||
pub use_legacy_landlock: bool,
|
||||
pub windows_sandbox_level: WindowsSandboxLevel,
|
||||
@@ -176,6 +177,7 @@ impl SandboxManager {
|
||||
enforce_managed_network,
|
||||
network,
|
||||
sandbox_policy_cwd,
|
||||
workspace_roots,
|
||||
codex_linux_sandbox_exe,
|
||||
use_legacy_landlock,
|
||||
windows_sandbox_level,
|
||||
@@ -183,7 +185,8 @@ impl SandboxManager {
|
||||
} = request;
|
||||
let additional_permissions = command.additional_permissions.take();
|
||||
let effective_permission_profile =
|
||||
effective_permission_profile(permissions, additional_permissions.as_ref());
|
||||
effective_permission_profile(permissions, additional_permissions.as_ref())
|
||||
.materialize_project_roots_with_workspace_roots(workspace_roots);
|
||||
let (effective_file_system_policy, effective_network_policy) =
|
||||
effective_permission_profile.to_runtime_permissions();
|
||||
let mut argv = Vec::with_capacity(1 + command.args.len());
|
||||
@@ -278,13 +281,6 @@ fn compatibility_workspace_write_policy(
|
||||
network_policy: NetworkSandboxPolicy,
|
||||
cwd: &Path,
|
||||
) -> SandboxPolicy {
|
||||
let cwd_abs = AbsolutePathBuf::from_absolute_path(cwd).ok();
|
||||
let writable_roots = file_system_policy
|
||||
.get_writable_roots_with_cwd(cwd)
|
||||
.into_iter()
|
||||
.map(|root| root.root)
|
||||
.filter(|root| cwd_abs.as_ref() != Some(root))
|
||||
.collect();
|
||||
let tmpdir_writable = std::env::var_os("TMPDIR")
|
||||
.filter(|tmpdir| !tmpdir.is_empty())
|
||||
.and_then(|tmpdir| {
|
||||
@@ -297,7 +293,6 @@ fn compatibility_workspace_write_policy(
|
||||
&& file_system_policy.can_write_path_with_cwd(slash_tmp, cwd);
|
||||
|
||||
SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots,
|
||||
network_access: network_policy.is_enabled(),
|
||||
exclude_tmpdir_env_var: !tmpdir_writable,
|
||||
exclude_slash_tmp: !slash_tmp_writable,
|
||||
|
||||
@@ -91,6 +91,7 @@ fn transform_preserves_unrestricted_file_system_policy_for_restricted_network()
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
sandbox_policy_cwd: cwd.as_path(),
|
||||
workspace_roots: std::slice::from_ref(&cwd),
|
||||
codex_linux_sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
@@ -142,6 +143,7 @@ fn transform_additional_permissions_enable_network_for_external_sandbox() {
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
sandbox_policy_cwd: cwd.as_path(),
|
||||
workspace_roots: std::slice::from_ref(&cwd),
|
||||
codex_linux_sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
@@ -210,6 +212,7 @@ fn transform_additional_permissions_preserves_denied_entries() {
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
sandbox_policy_cwd: cwd.as_path(),
|
||||
workspace_roots: std::slice::from_ref(&cwd),
|
||||
codex_linux_sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
@@ -263,6 +266,7 @@ fn transform_linux_seccomp_request(
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
sandbox_policy_cwd: cwd.as_path(),
|
||||
workspace_roots: std::slice::from_ref(&cwd),
|
||||
codex_linux_sandbox_exe: Some(codex_linux_sandbox_exe),
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
|
||||
@@ -164,7 +164,6 @@ fn create_seatbelt_args_routes_network_through_proxy_ports() {
|
||||
fn dynamic_network_policy_allows_tls_without_darwin_user_cache_write() {
|
||||
let policy = dynamic_network_policy(
|
||||
&SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
network_access: true,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
@@ -437,7 +436,6 @@ fn create_seatbelt_args_allows_local_binding_when_explicitly_enabled() {
|
||||
fn dynamic_network_policy_preserves_restricted_policy_when_proxy_config_without_ports() {
|
||||
let policy = dynamic_network_policy(
|
||||
&SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
network_access: true,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
@@ -473,7 +471,6 @@ fn dynamic_network_policy_preserves_restricted_policy_when_proxy_config_without_
|
||||
fn dynamic_network_policy_blocks_dns_when_local_binding_has_no_proxy_ports() {
|
||||
let policy = dynamic_network_policy(
|
||||
&SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
network_access: true,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
@@ -501,7 +498,6 @@ fn dynamic_network_policy_blocks_dns_when_local_binding_has_no_proxy_ports() {
|
||||
fn dynamic_network_policy_preserves_restricted_policy_for_managed_network_without_proxy_config() {
|
||||
let policy = dynamic_network_policy(
|
||||
&SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
network_access: true,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
@@ -791,7 +787,6 @@ fn create_seatbelt_args_allows_all_unix_sockets_when_enabled() {
|
||||
fn create_seatbelt_args_full_network_with_proxy_is_still_proxy_only() {
|
||||
let policy = dynamic_network_policy(
|
||||
&SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
network_access: true,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
@@ -838,15 +833,15 @@ fn create_seatbelt_args_with_read_only_git_and_codex_subpaths() {
|
||||
|
||||
// Build a policy that only includes the two test roots as writable and
|
||||
// does not automatically include defaults TMPDIR or /tmp.
|
||||
let policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![vulnerable_root, empty_root]
|
||||
.into_iter()
|
||||
.map(|p| p.try_into().unwrap())
|
||||
.collect(),
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: true,
|
||||
exclude_slash_tmp: true,
|
||||
};
|
||||
let writable_roots = vec![vulnerable_root, empty_root]
|
||||
.into_iter()
|
||||
.map(|path| AbsolutePathBuf::from_absolute_path(path).expect("absolute writable root"))
|
||||
.collect::<Vec<_>>();
|
||||
let file_system_sandbox_policy = FileSystemSandboxPolicy::workspace_write(
|
||||
&writable_roots,
|
||||
/*exclude_tmpdir_env_var*/ true,
|
||||
/*exclude_slash_tmp*/ true,
|
||||
);
|
||||
|
||||
// Create the Seatbelt command to wrap a shell command that tries to
|
||||
// write to .codex/config.toml in the vulnerable root.
|
||||
@@ -863,13 +858,15 @@ fn create_seatbelt_args_with_read_only_git_and_codex_subpaths() {
|
||||
.iter()
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect();
|
||||
let args = create_seatbelt_command_args_for_legacy_policy(
|
||||
shell_command.clone(),
|
||||
&policy,
|
||||
&cwd,
|
||||
/*enforce_managed_network*/ false,
|
||||
/*network*/ None,
|
||||
);
|
||||
let args = create_seatbelt_command_args(CreateSeatbeltCommandArgsParams {
|
||||
command: shell_command.clone(),
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy::Restricted,
|
||||
sandbox_policy_cwd: &cwd,
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
extra_allow_unix_sockets: &[],
|
||||
});
|
||||
|
||||
let policy_text = seatbelt_policy_arg(&args);
|
||||
assert!(
|
||||
@@ -1002,13 +999,15 @@ fn create_seatbelt_args_with_read_only_git_and_codex_subpaths() {
|
||||
.iter()
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect();
|
||||
let write_hooks_file_args = create_seatbelt_command_args_for_legacy_policy(
|
||||
shell_command_git,
|
||||
&policy,
|
||||
&cwd,
|
||||
/*enforce_managed_network*/ false,
|
||||
/*network*/ None,
|
||||
);
|
||||
let write_hooks_file_args = create_seatbelt_command_args(CreateSeatbeltCommandArgsParams {
|
||||
command: shell_command_git,
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy::Restricted,
|
||||
sandbox_policy_cwd: &cwd,
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
extra_allow_unix_sockets: &[],
|
||||
});
|
||||
let output = Command::new(MACOS_PATH_TO_SEATBELT_EXECUTABLE)
|
||||
.args(&write_hooks_file_args)
|
||||
.current_dir(&cwd)
|
||||
@@ -1038,13 +1037,15 @@ fn create_seatbelt_args_with_read_only_git_and_codex_subpaths() {
|
||||
.iter()
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect();
|
||||
let write_allowed_file_args = create_seatbelt_command_args_for_legacy_policy(
|
||||
shell_command_allowed,
|
||||
&policy,
|
||||
&cwd,
|
||||
/*enforce_managed_network*/ false,
|
||||
/*network*/ None,
|
||||
);
|
||||
let write_allowed_file_args = create_seatbelt_command_args(CreateSeatbeltCommandArgsParams {
|
||||
command: shell_command_allowed,
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy::Restricted,
|
||||
sandbox_policy_cwd: &cwd,
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
extra_allow_unix_sockets: &[],
|
||||
});
|
||||
let output = Command::new(MACOS_PATH_TO_SEATBELT_EXECUTABLE)
|
||||
.args(&write_allowed_file_args)
|
||||
.current_dir(&cwd)
|
||||
@@ -1085,7 +1086,6 @@ fn create_seatbelt_args_block_first_time_dot_codex_creation_with_metadata_name_r
|
||||
let dot_codex = repo_root.join(".codex");
|
||||
let config_toml = dot_codex.join("config.toml");
|
||||
let policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![repo_root.as_path().try_into().expect("absolute repo root")],
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: true,
|
||||
exclude_slash_tmp: true,
|
||||
@@ -1137,12 +1137,13 @@ fn create_seatbelt_args_with_read_only_git_pointer_file() {
|
||||
let cwd = tmp.path().join("cwd");
|
||||
fs::create_dir_all(&cwd).expect("create cwd");
|
||||
|
||||
let policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![worktree_root.try_into().expect("worktree_root is absolute")],
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: true,
|
||||
exclude_slash_tmp: true,
|
||||
};
|
||||
let worktree_root =
|
||||
AbsolutePathBuf::from_absolute_path(worktree_root).expect("worktree_root is absolute");
|
||||
let file_system_sandbox_policy = FileSystemSandboxPolicy::workspace_write(
|
||||
std::slice::from_ref(&worktree_root),
|
||||
/*exclude_tmpdir_env_var*/ true,
|
||||
/*exclude_slash_tmp*/ true,
|
||||
);
|
||||
|
||||
let shell_command: Vec<String> = [
|
||||
"bash",
|
||||
@@ -1154,13 +1155,15 @@ fn create_seatbelt_args_with_read_only_git_pointer_file() {
|
||||
.iter()
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect();
|
||||
let args = create_seatbelt_command_args_for_legacy_policy(
|
||||
shell_command,
|
||||
&policy,
|
||||
&cwd,
|
||||
/*enforce_managed_network*/ false,
|
||||
/*network*/ None,
|
||||
);
|
||||
let args = create_seatbelt_command_args(CreateSeatbeltCommandArgsParams {
|
||||
command: shell_command,
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy::Restricted,
|
||||
sandbox_policy_cwd: &cwd,
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
extra_allow_unix_sockets: &[],
|
||||
});
|
||||
|
||||
let output = Command::new(MACOS_PATH_TO_SEATBELT_EXECUTABLE)
|
||||
.args(&args)
|
||||
@@ -1190,13 +1193,15 @@ fn create_seatbelt_args_with_read_only_git_pointer_file() {
|
||||
.iter()
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect();
|
||||
let gitdir_args = create_seatbelt_command_args_for_legacy_policy(
|
||||
shell_command_gitdir,
|
||||
&policy,
|
||||
&cwd,
|
||||
/*enforce_managed_network*/ false,
|
||||
/*network*/ None,
|
||||
);
|
||||
let gitdir_args = create_seatbelt_command_args(CreateSeatbeltCommandArgsParams {
|
||||
command: shell_command_gitdir,
|
||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||
network_sandbox_policy: NetworkSandboxPolicy::Restricted,
|
||||
sandbox_policy_cwd: &cwd,
|
||||
enforce_managed_network: false,
|
||||
network: None,
|
||||
extra_allow_unix_sockets: &[],
|
||||
});
|
||||
let output = Command::new(MACOS_PATH_TO_SEATBELT_EXECUTABLE)
|
||||
.args(&gitdir_args)
|
||||
.current_dir(&cwd)
|
||||
@@ -1234,7 +1239,6 @@ fn create_seatbelt_args_for_cwd_as_git_repo() {
|
||||
// use the default ones (cwd and TMPDIR) and verifies the protected
|
||||
// metadata checks are done properly for cwd.
|
||||
let policy = SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: vec![],
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
|
||||
Reference in New Issue
Block a user