mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
permissions: make runtime config profile-backed
This commit is contained in:
@@ -72,15 +72,15 @@ impl App {
|
||||
"Failed to carry forward approval policy override: {err}"
|
||||
));
|
||||
}
|
||||
if let Some(policy) = self.runtime_sandbox_policy_override.as_ref() {
|
||||
if let Err(err) = config.permissions.sandbox_policy.set(policy.clone()) {
|
||||
tracing::warn!(%err, "failed to carry forward sandbox policy override");
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to carry forward sandbox policy override: {err}"
|
||||
));
|
||||
} else {
|
||||
sync_runtime_permissions_from_legacy_sandbox_policy(config);
|
||||
}
|
||||
if let Some(policy) = self.runtime_sandbox_policy_override.as_ref()
|
||||
&& let Err(err) = config
|
||||
.permissions
|
||||
.set_legacy_sandbox_policy(policy.clone(), config.cwd.as_path())
|
||||
{
|
||||
tracing::warn!(%err, "failed to carry forward sandbox policy override");
|
||||
self.chat_widget.add_error_message(format!(
|
||||
"Failed to carry forward sandbox policy override: {err}"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,13 +113,15 @@ impl App {
|
||||
user_message_prefix: &str,
|
||||
log_message: &str,
|
||||
) -> bool {
|
||||
if let Err(err) = config.permissions.sandbox_policy.set(policy) {
|
||||
if let Err(err) = config
|
||||
.permissions
|
||||
.set_legacy_sandbox_policy(policy, config.cwd.as_path())
|
||||
{
|
||||
tracing::warn!(error = %err, "{log_message}");
|
||||
self.chat_widget
|
||||
.add_error_message(format!("{user_message_prefix}: {err}"));
|
||||
return false;
|
||||
}
|
||||
sync_runtime_permissions_from_legacy_sandbox_policy(config);
|
||||
|
||||
true
|
||||
}
|
||||
@@ -543,17 +545,6 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
fn sync_runtime_permissions_from_legacy_sandbox_policy(config: &mut Config) {
|
||||
let sandbox_policy = config.permissions.sandbox_policy.get();
|
||||
config.permissions.file_system_sandbox_policy =
|
||||
codex_protocol::permissions::FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
|
||||
sandbox_policy,
|
||||
&config.cwd,
|
||||
);
|
||||
config.permissions.network_sandbox_policy =
|
||||
codex_protocol::permissions::NetworkSandboxPolicy::from(sandbox_policy);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -2216,9 +2216,7 @@ async fn inactive_thread_approval_bubbles_into_active_view() -> Result<()> {
|
||||
ThreadSessionState {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy: SandboxPolicy::new_workspace_write_policy(),
|
||||
permission_profile: Some(PermissionProfile::from_legacy_sandbox_policy(
|
||||
&SandboxPolicy::new_workspace_write_policy(),
|
||||
)),
|
||||
permission_profile: Some(PermissionProfile::workspace_write()),
|
||||
rollout_path: Some(test_path_buf("/tmp/agent-rollout.jsonl")),
|
||||
..test_thread_session(agent_thread_id, test_path_buf("/tmp/agent"))
|
||||
},
|
||||
@@ -2378,9 +2376,7 @@ async fn side_defers_subagent_approval_overlay_until_side_exits() -> Result<()>
|
||||
ThreadSessionState {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy: SandboxPolicy::new_workspace_write_policy(),
|
||||
permission_profile: Some(PermissionProfile::from_legacy_sandbox_policy(
|
||||
&SandboxPolicy::new_workspace_write_policy(),
|
||||
)),
|
||||
permission_profile: Some(PermissionProfile::workspace_write()),
|
||||
rollout_path: Some(test_path_buf("/tmp/agent-rollout.jsonl")),
|
||||
..test_thread_session(agent_thread_id, test_path_buf("/tmp/agent"))
|
||||
},
|
||||
@@ -2603,9 +2599,7 @@ async fn inactive_thread_approval_badge_clears_after_turn_completion_notificatio
|
||||
ThreadSessionState {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy: SandboxPolicy::new_workspace_write_policy(),
|
||||
permission_profile: Some(PermissionProfile::from_legacy_sandbox_policy(
|
||||
&SandboxPolicy::new_workspace_write_policy(),
|
||||
)),
|
||||
permission_profile: Some(PermissionProfile::workspace_write()),
|
||||
rollout_path: Some(test_path_buf("/tmp/agent-rollout.jsonl")),
|
||||
..test_thread_session(agent_thread_id, test_path_buf("/tmp/agent"))
|
||||
},
|
||||
@@ -2659,9 +2653,7 @@ async fn inactive_thread_started_notification_initializes_replay_session() -> Re
|
||||
let primary_session = ThreadSessionState {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy: SandboxPolicy::new_workspace_write_policy(),
|
||||
permission_profile: Some(PermissionProfile::from_legacy_sandbox_policy(
|
||||
&SandboxPolicy::new_workspace_write_policy(),
|
||||
)),
|
||||
permission_profile: Some(PermissionProfile::workspace_write()),
|
||||
..test_thread_session(main_thread_id, test_path_buf("/tmp/main"))
|
||||
};
|
||||
|
||||
@@ -2774,9 +2766,7 @@ async fn inactive_thread_started_notification_preserves_primary_model_when_path_
|
||||
let primary_session = ThreadSessionState {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy: SandboxPolicy::new_workspace_write_policy(),
|
||||
permission_profile: Some(PermissionProfile::from_legacy_sandbox_policy(
|
||||
&SandboxPolicy::new_workspace_write_policy(),
|
||||
)),
|
||||
permission_profile: Some(PermissionProfile::workspace_write()),
|
||||
..test_thread_session(main_thread_id, test_path_buf("/tmp/main"))
|
||||
};
|
||||
|
||||
@@ -2845,9 +2835,7 @@ async fn thread_read_session_state_does_not_reuse_primary_permission_profile() {
|
||||
let primary_session = ThreadSessionState {
|
||||
approval_policy: AskForApproval::OnRequest,
|
||||
sandbox_policy: SandboxPolicy::new_workspace_write_policy(),
|
||||
permission_profile: Some(PermissionProfile::from_legacy_sandbox_policy(
|
||||
&SandboxPolicy::new_workspace_write_policy(),
|
||||
)),
|
||||
permission_profile: Some(PermissionProfile::workspace_write()),
|
||||
..test_thread_session(main_thread_id, test_path_buf("/tmp/main"))
|
||||
};
|
||||
app.primary_session_configured = Some(primary_session);
|
||||
@@ -3746,9 +3734,7 @@ fn test_thread_session(thread_id: ThreadId, cwd: PathBuf) -> ThreadSessionState
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
permission_profile: Some(PermissionProfile::from_legacy_sandbox_policy(
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
)),
|
||||
permission_profile: Some(PermissionProfile::read_only()),
|
||||
cwd: cwd.abs(),
|
||||
instruction_source_paths: Vec::new(),
|
||||
reasoning_effort: None,
|
||||
|
||||
@@ -303,9 +303,7 @@ mod tests {
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||
permission_profile: Some(PermissionProfile::from_legacy_sandbox_policy(
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
)),
|
||||
permission_profile: Some(PermissionProfile::read_only()),
|
||||
cwd: cwd.abs(),
|
||||
instruction_source_paths: Vec::new(),
|
||||
reasoning_effort: None,
|
||||
|
||||
@@ -1776,9 +1776,7 @@ mod tests {
|
||||
AskForApproval::Never,
|
||||
codex_protocol::config_types::ApprovalsReviewer::User,
|
||||
SandboxPolicy::new_read_only_policy(),
|
||||
Some(PermissionProfile::from_legacy_sandbox_policy(
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
)),
|
||||
Some(PermissionProfile::read_only()),
|
||||
test_path_buf("/tmp/project").abs(),
|
||||
Vec::new(),
|
||||
/*reasoning_effort*/ None,
|
||||
@@ -1809,9 +1807,7 @@ mod tests {
|
||||
AskForApproval::Never,
|
||||
codex_protocol::config_types::ApprovalsReviewer::User,
|
||||
SandboxPolicy::new_read_only_policy(),
|
||||
Some(PermissionProfile::from_legacy_sandbox_policy(
|
||||
&SandboxPolicy::new_read_only_policy(),
|
||||
)),
|
||||
Some(PermissionProfile::read_only()),
|
||||
test_path_buf("/tmp/project").abs(),
|
||||
Vec::new(),
|
||||
/*reasoning_effort*/ None,
|
||||
|
||||
@@ -2320,20 +2320,6 @@ impl ChatWidget {
|
||||
display: SessionConfiguredDisplay,
|
||||
fork_parent_title: Option<String>,
|
||||
) {
|
||||
let (file_system_sandbox_policy, network_sandbox_policy) = match event
|
||||
.permission_profile
|
||||
.as_ref()
|
||||
{
|
||||
Some(permission_profile) => permission_profile.to_runtime_permissions(),
|
||||
None => (
|
||||
codex_protocol::permissions::FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
|
||||
&event.sandbox_policy,
|
||||
&event.cwd,
|
||||
),
|
||||
codex_protocol::permissions::NetworkSandboxPolicy::from(&event.sandbox_policy),
|
||||
),
|
||||
};
|
||||
|
||||
self.last_agent_markdown = None;
|
||||
self.agent_turn_markdowns.clear();
|
||||
self.visible_user_turn_count = 0;
|
||||
@@ -2366,18 +2352,52 @@ impl ChatWidget {
|
||||
self.config.permissions.approval_policy =
|
||||
Constrained::allow_only(event.approval_policy);
|
||||
}
|
||||
if let Err(err) = self
|
||||
.config
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.set(event.sandbox_policy.clone())
|
||||
{
|
||||
tracing::warn!(%err, "failed to sync sandbox_policy from SessionConfigured");
|
||||
let permission_sync = match event.permission_profile.clone() {
|
||||
Some(permission_profile) => self
|
||||
.config
|
||||
.permissions
|
||||
.set_permission_profile(permission_profile, event.cwd.as_path()),
|
||||
None => self
|
||||
.config
|
||||
.permissions
|
||||
.set_legacy_sandbox_policy(event.sandbox_policy.clone(), event.cwd.as_path()),
|
||||
};
|
||||
if let Err(err) = permission_sync {
|
||||
tracing::warn!(%err, "failed to sync permissions from SessionConfigured");
|
||||
self.config.permissions.sandbox_policy =
|
||||
Constrained::allow_only(event.sandbox_policy.clone());
|
||||
match event.permission_profile.clone() {
|
||||
Some(permission_profile) => {
|
||||
let (file_system_sandbox_policy, network_sandbox_policy) =
|
||||
permission_profile.to_runtime_permissions();
|
||||
self.config.permissions.permission_profile =
|
||||
Constrained::allow_only(permission_profile);
|
||||
self.config.permissions.file_system_sandbox_policy = file_system_sandbox_policy;
|
||||
self.config.permissions.network_sandbox_policy = network_sandbox_policy;
|
||||
}
|
||||
None => {
|
||||
self.config.permissions.file_system_sandbox_policy =
|
||||
codex_protocol::permissions::FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
|
||||
&event.sandbox_policy,
|
||||
&event.cwd,
|
||||
);
|
||||
self.config.permissions.network_sandbox_policy =
|
||||
codex_protocol::permissions::NetworkSandboxPolicy::from(
|
||||
&event.sandbox_policy,
|
||||
);
|
||||
let permission_profile =
|
||||
codex_protocol::models::PermissionProfile::from_runtime_permissions_with_enforcement(
|
||||
codex_protocol::models::SandboxEnforcement::from_legacy_sandbox_policy(
|
||||
&event.sandbox_policy,
|
||||
),
|
||||
&self.config.permissions.file_system_sandbox_policy,
|
||||
self.config.permissions.network_sandbox_policy,
|
||||
);
|
||||
self.config.permissions.permission_profile =
|
||||
Constrained::allow_only(permission_profile);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.config.permissions.file_system_sandbox_policy = file_system_sandbox_policy;
|
||||
self.config.permissions.network_sandbox_policy = network_sandbox_policy;
|
||||
self.config.approvals_reviewer = event.approvals_reviewer;
|
||||
self.status_line_project_root_name_cache = None;
|
||||
let forked_from_id = event.forked_from_id;
|
||||
@@ -10259,16 +10279,9 @@ impl ChatWidget {
|
||||
/// Set the sandbox policy in the widget's config copy.
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
pub(crate) fn set_sandbox_policy(&mut self, policy: SandboxPolicy) -> ConstraintResult<()> {
|
||||
self.config.permissions.sandbox_policy.set(policy)?;
|
||||
let sandbox_policy = self.config.permissions.sandbox_policy.get();
|
||||
self.config.permissions.file_system_sandbox_policy =
|
||||
codex_protocol::permissions::FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
|
||||
sandbox_policy,
|
||||
&self.config.cwd,
|
||||
);
|
||||
self.config.permissions.network_sandbox_policy =
|
||||
codex_protocol::permissions::NetworkSandboxPolicy::from(sandbox_policy);
|
||||
Ok(())
|
||||
self.config
|
||||
.permissions
|
||||
.set_legacy_sandbox_policy(policy, self.config.cwd.as_path())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
|
||||
@@ -258,7 +258,7 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
|
||||
.expect("set sandbox policy");
|
||||
chat.config.cwd = test_path_buf("/home/user/main").abs();
|
||||
|
||||
let expected_sandbox = SandboxPolicy::new_read_only_policy();
|
||||
let legacy_fallback_sandbox = SandboxPolicy::new_read_only_policy();
|
||||
let expected_cwd = test_path_buf("/home/user/sub-agent").abs();
|
||||
let expected_file_system_policy = FileSystemSandboxPolicy::restricted(vec![
|
||||
FileSystemSandboxEntry {
|
||||
@@ -279,6 +279,9 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
|
||||
&expected_file_system_policy,
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
);
|
||||
let expected_sandbox = expected_permission_profile
|
||||
.to_legacy_sandbox_policy(expected_cwd.as_path())
|
||||
.expect("permission profile should project to legacy sandbox policy");
|
||||
let configured = codex_protocol::protocol::SessionConfiguredEvent {
|
||||
session_id: ThreadId::new(),
|
||||
forked_from_id: None,
|
||||
@@ -288,7 +291,7 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
|
||||
service_tier: None,
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: ApprovalsReviewer::User,
|
||||
sandbox_policy: expected_sandbox.clone(),
|
||||
sandbox_policy: legacy_fallback_sandbox,
|
||||
permission_profile: Some(expected_permission_profile.clone()),
|
||||
cwd: expected_cwd.clone(),
|
||||
reasoning_effort: Some(ReasoningEffortConfig::default()),
|
||||
|
||||
Reference in New Issue
Block a user