From f3c4d082d939cefe1a97c156d02ecee6d19cd207 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Fri, 11 Sep 2026 22:53:21 +0000 Subject: [PATCH] Remove Windows world-writable scans and warnings from the TUI (#44933) ## What changed Remove TUI-triggered world-writable scans at startup and during permission changes, along with their warning dialogs, acknowledgement handling, and scan telemetry. Permission selection and shortcuts no longer check for these warnings, and terminal color probing no longer waits for a startup scan. GitOrigin-RevId: a8d2fb5de7f891dcae5261286743fe3a125b82aa --- codex-rs/tui/src/app.rs | 12 -- codex-rs/tui/src/app/event_dispatch.rs | 111 +--------- codex-rs/tui/src/app/platform_actions.rs | 62 ------ codex-rs/tui/src/app/startup.rs | 34 --- codex-rs/tui/src/app/tests/startup.rs | 75 +------ codex-rs/tui/src/app_event.rs | 32 --- .../tui/src/chatwidget/permission_popups.rs | 14 -- .../src/chatwidget/permission_shortcuts.rs | 5 +- codex-rs/tui/src/chatwidget/settings.rs | 12 -- .../tests/permission_shortcuts_tests.rs | 1 - .../tui/src/chatwidget/tests/permissions.rs | 50 ----- .../src/chatwidget/windows_sandbox_prompts.rs | 193 ------------------ codex-rs/tui/src/windows_sandbox.rs | 18 -- 13 files changed, 4 insertions(+), 615 deletions(-) diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index a983f2734a..331d379dc7 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -153,8 +153,6 @@ use codex_otel::SessionTelemetry; use codex_otel::TelemetryAuthMode; use codex_protocol::ThreadId; use codex_protocol::config_types::Personality; -#[cfg(target_os = "windows")] -use codex_protocol::config_types::WindowsSandboxLevel; use codex_protocol::models::ActivePermissionProfile; use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_WORKSPACE; use codex_protocol::models::PermissionProfile; @@ -162,8 +160,6 @@ use codex_protocol::openai_models::ModelAvailabilityNux; use codex_protocol::openai_models::ModelPreset; use codex_protocol::openai_models::ModelUpgrade; use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig; -#[cfg(target_os = "windows")] -use codex_protocol::permissions::FileSystemSandboxKind; use codex_rollout::StateDbHandle; use codex_terminal_detection::user_agent; use codex_utils_absolute_path::AbsolutePathBuf; @@ -428,14 +424,6 @@ impl AutoReviewMode { } } -#[cfg(target_os = "windows")] -fn managed_filesystem_sandbox_is_restricted(permission_profile: &PermissionProfile) -> bool { - matches!( - permission_profile.file_system_sandbox_policy().kind, - FileSystemSandboxKind::Restricted - ) -} - /// Baseline cadence for periodic stream commit animation ticks. /// /// Smooth-mode streaming drains one line per tick, so this interval controls diff --git a/codex-rs/tui/src/app/event_dispatch.rs b/codex-rs/tui/src/app/event_dispatch.rs index 8eae5d0b63..30ced383a0 100644 --- a/codex-rs/tui/src/app/event_dispatch.rs +++ b/codex-rs/tui/src/app/event_dispatch.rs @@ -1909,24 +1909,6 @@ impl App { AppEvent::ApplyPermissionShortcut { thread_id, selection } => { self.apply_permission_shortcut(app_server, tui, thread_id, selection).await; } - AppEvent::OpenWorldWritableWarningConfirmation { - preset, - profile_selection, - sample_paths, - extra_count, - failed_scan, - } => { - self.chat_widget.open_world_writable_warning_confirmation( - preset, - profile_selection, - sample_paths, - extra_count, - failed_scan, - ); - } - AppEvent::StartupWorldWritableScanCompleted => { - self.windows_sandbox.startup_world_writable_scan_pending = false; - } AppEvent::OpenFeedbackNote { category, include_logs, @@ -2229,36 +2211,7 @@ impl App { ); let windows_sandbox_level = crate::windows_sandbox::level_from_config(&self.config); - if let Some((sample_paths, extra_count, failed_scan)) = - self.chat_widget.world_writable_warning_details() - { - self.app_event_tx.send(AppEvent::CodexOp( - AppCommand::override_turn_context( - /*cwd*/ None, - /*approval_policy*/ None, - /*approvals_reviewer*/ None, - /*permission_profile*/ None, - /*active_permission_profile*/ None, - #[cfg(target_os = "windows")] - Some(windows_sandbox_level), - /*model*/ None, - /*effort*/ None, - /*summary*/ None, - /*service_tier*/ None, - /*collaboration_mode*/ None, - /*personality*/ None, - ), - )); - self.app_event_tx.send( - AppEvent::OpenWorldWritableWarningConfirmation { - preset: Some(preset.clone()), - profile_selection: profile_selection.clone(), - sample_paths, - extra_count, - failed_scan, - }, - ); - } else if let Some(selection) = profile_selection { + if let Some(selection) = profile_selection { self.app_event_tx.send(AppEvent::CodexOp( AppCommand::override_turn_context( /*cwd*/ None, @@ -2504,9 +2457,6 @@ impl App { else { return Ok(AppRunControl::Continue); }; - #[cfg(target_os = "windows")] - let permission_profile_is_managed_restricted = - managed_filesystem_sandbox_is_restricted(&permission_profile); let permission_profile_for_chat = permission_profile.clone(); self.config = config; @@ -2529,42 +2479,6 @@ impl App { self.sync_active_thread_permission_settings_to_cached_session() .await; self.chat_widget.submit_initial_user_message_if_pending(); - - // If a managed filesystem sandbox is active, run the Windows - // world-writable scan. - #[cfg(target_os = "windows")] - { - // One-shot suppression if the user just confirmed continue. - if self.windows_sandbox.skip_world_writable_scan_once { - self.windows_sandbox.skip_world_writable_scan_once = false; - return Ok(AppRunControl::Continue); - } - - let should_check = crate::windows_sandbox::level_from_config(&self.config) - != WindowsSandboxLevel::Disabled - && permission_profile_is_managed_restricted - && !self.chat_widget.world_writable_warning_hidden(); - if should_check { - let cwd = self.config.cwd.clone(); - let workspace_roots = self.config.effective_workspace_roots(); - let env_map: std::collections::HashMap = - std::env::vars().collect(); - let tx = self.app_event_tx.clone(); - let logs_base_dir = self.config.codex_home.clone(); - let permission_profile = - self.config.permissions.effective_permission_profile(); - Self::spawn_world_writable_scan( - cwd, - workspace_roots, - env_map, - logs_base_dir, - permission_profile, - self.session_telemetry.clone(), - tx, - /*startup_scan*/ false, - ); - } - } } AppEvent::SelectPermissionProfile(selection) => { self.select_permission_profile(app_server, selection).await; @@ -2621,13 +2535,6 @@ impl App { AppEvent::ResetMemories => { self.reset_memories_with_app_server(app_server).await; } - AppEvent::SkipNextWorldWritableScan => { - self.windows_sandbox.skip_world_writable_scan_once = true; - } - AppEvent::UpdateWorldWritableWarningAcknowledged(ack) => { - self.chat_widget - .set_world_writable_warning_acknowledged(ack); - } AppEvent::UpdateRateLimitSwitchPromptHidden(hidden) => { self.chat_widget.set_rate_limit_switch_prompt_hidden(hidden); } @@ -2636,22 +2543,6 @@ impl App { self.sync_active_thread_plan_mode_reasoning_setting(app_server) .await; } - AppEvent::PersistWorldWritableWarningAcknowledged => { - self.local_settings.notices.hide_world_writable_warning = Some(true); - if let Err(err) = ConfigEditsBuilder::for_config_path(self.local_settings.user_config_path.as_path()) - .set_hide_world_writable_warning(/*acknowledged*/ true) - .apply() - .await - { - tracing::error!( - error = %err, - "failed to persist world-writable warning acknowledgement" - ); - self.chat_widget.add_error_message(format!( - "Failed to save Agent mode warning preference: {err}" - )); - } - } AppEvent::PersistRateLimitSwitchPromptHidden => { self.local_settings.notices.hide_rate_limit_model_nudge = Some(true); if let Err(err) = ConfigEditsBuilder::for_config_path(self.local_settings.user_config_path.as_path()) diff --git a/codex-rs/tui/src/app/platform_actions.rs b/codex-rs/tui/src/app/platform_actions.rs index bf84728fa6..32931b4d8f 100644 --- a/codex-rs/tui/src/app/platform_actions.rs +++ b/codex-rs/tui/src/app/platform_actions.rs @@ -8,68 +8,6 @@ use super::*; #[derive(Default)] pub(super) struct WindowsSandboxState { pub(super) setup_started_at: Option, - // One-shot suppression of the next world-writable scan after user confirmation. - pub(super) skip_world_writable_scan_once: bool, - /// A startup filesystem scan can still enqueue a protected warning after the app queue drains. - pub(super) startup_world_writable_scan_pending: bool, -} - -impl App { - #[cfg(target_os = "windows")] - #[allow(clippy::too_many_arguments)] - pub(super) fn spawn_world_writable_scan( - cwd: AbsolutePathBuf, - workspace_roots: Vec, - env_map: std::collections::HashMap, - logs_base_dir: AbsolutePathBuf, - permission_profile: PermissionProfile, - session_telemetry: SessionTelemetry, - tx: AppEventSender, - startup_scan: bool, - ) { - let Ok(permissions) = - codex_windows_sandbox::ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_workspace_roots( - &permission_profile, - workspace_roots.as_slice(), - ) - else { - if startup_scan { - tx.send(AppEvent::StartupWorldWritableScanCompleted); - } - return; - }; - - tokio::task::spawn_blocking(move || { - let logs_base_dir_path = logs_base_dir.as_path(); - let result = - codex_windows_sandbox::apply_world_writable_scan_and_denies_for_permissions( - logs_base_dir_path, - cwd.as_path(), - &env_map, - &permissions, - Some(logs_base_dir_path), - ); - crate::windows_sandbox::record_world_writable_scan_result(&session_telemetry, &result); - if result.is_err() { - // Scan failed: warn without examples. - send_world_writable_scan_failed(&tx); - } - if startup_scan { - tx.send(AppEvent::StartupWorldWritableScanCompleted); - } - }); - } -} - -#[cfg(target_os = "windows")] -fn send_world_writable_scan_failed(tx: &AppEventSender) { - tx.send(AppEvent::OpenWorldWritableWarningConfirmation { - preset: None, - profile_selection: None, - sample_paths: Vec::new(), - extra_count: 0usize, - failed_scan: true, - }); } pub(super) fn side_return_shortcut_matches(key_event: KeyEvent) -> bool { diff --git a/codex-rs/tui/src/app/startup.rs b/codex-rs/tui/src/app/startup.rs index 1faea715d7..c5ec00043a 100644 --- a/codex-rs/tui/src/app/startup.rs +++ b/codex-rs/tui/src/app/startup.rs @@ -128,7 +128,6 @@ impl App { pub(super) fn ready_for_terminal_color_probe(&self, has_pending_app_events: bool) -> bool { !has_pending_app_events && !self.chat_widget.has_active_view() - && !self.windows_sandbox.startup_world_writable_scan_pending && !self.startup_pending_protected_request && !self.has_queued_startup_protected_request() && !self.chat_widget.has_pending_protected_request() @@ -864,39 +863,6 @@ See the Codex keymap documentation for supported actions and examples." } let initial_session_ms = initial_session_started_at.elapsed().as_millis(); - // On startup, if a managed filesystem sandbox is active, warn about - // world-writable dirs on Windows. - #[cfg(target_os = "windows")] - { - let startup_permission_profile = app.config.permissions.effective_permission_profile(); - let should_check = crate::windows_sandbox::level_from_config(&app.config) - != WindowsSandboxLevel::Disabled - && managed_filesystem_sandbox_is_restricted(&startup_permission_profile) - && !app - .local_settings - .notices - .hide_world_writable_warning - .unwrap_or(false); - if should_check { - app.windows_sandbox.startup_world_writable_scan_pending = true; - let cwd = app.config.cwd.clone(); - let workspace_roots = app.config.effective_workspace_roots(); - let env_map: std::collections::HashMap = std::env::vars().collect(); - let tx = app.app_event_tx.clone(); - let logs_base_dir = app.config.codex_home.clone(); - Self::spawn_world_writable_scan( - cwd, - workspace_roots, - env_map, - logs_base_dir, - startup_permission_profile, - app.session_telemetry.clone(), - tx, - /*startup_scan*/ true, - ); - } - } - if let Err(err) = startup_draft.flush_pending_events(tui).await { return shutdown_on_startup_error(app_server, err).await; } diff --git a/codex-rs/tui/src/app/tests/startup.rs b/codex-rs/tui/src/app/tests/startup.rs index d217135021..9ef3aca8df 100644 --- a/codex-rs/tui/src/app/tests/startup.rs +++ b/codex-rs/tui/src/app/tests/startup.rs @@ -63,74 +63,6 @@ async fn terminal_color_probe_waits_for_startup_sandbox_choice() { assert!(app.ready_for_terminal_color_probe(/*has_pending_app_events*/ false)); } -#[tokio::test] -async fn terminal_color_probe_waits_for_delayed_world_writable_scan_failure() { - let (mut app, mut app_event_rx, _op_rx) = make_test_app_with_channels().await; - app.startup_protected_input_boundary = true; - app.windows_sandbox.startup_world_writable_scan_pending = true; - while app_event_rx.try_recv().is_ok() {} - - assert!(!app.ready_for_terminal_color_probe(/*has_pending_app_events*/ false)); - - app.app_event_tx - .send(AppEvent::OpenWorldWritableWarningConfirmation { - preset: None, - profile_selection: None, - sample_paths: Vec::new(), - extra_count: 0, - failed_scan: true, - }); - app.app_event_tx - .send(AppEvent::StartupWorldWritableScanCompleted); - assert!(!app.ready_for_terminal_color_probe(/*has_pending_app_events*/ true)); - - let warning = app_event_rx - .try_recv() - .expect("the delayed scan should queue its warning before completion"); - let AppEvent::OpenWorldWritableWarningConfirmation { - preset, - profile_selection, - sample_paths, - extra_count, - failed_scan, - } = warning - else { - panic!("the delayed scan should open a protected warning before completion"); - }; - app.chat_widget.open_world_writable_warning_confirmation( - preset, - profile_selection, - sample_paths, - extra_count, - failed_scan, - ); - assert!(matches!( - app_event_rx.try_recv(), - Ok(AppEvent::StartupWorldWritableScanCompleted) - )); - app.windows_sandbox.startup_world_writable_scan_pending = false; - - assert!(!app.windows_sandbox.startup_world_writable_scan_pending); - assert!(!app.ready_for_terminal_color_probe(/*has_pending_app_events*/ false)); - for character in "20;rgb:2222/ffff/ffff".chars() { - app.chat_widget - .handle_key_event(KeyEvent::new(KeyCode::Char(character), KeyModifiers::NONE)); - assert!(app_event_rx.try_recv().is_err()); - } - - app.chat_widget - .handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); - assert!(matches!( - app_event_rx.try_recv(), - Ok(AppEvent::UpdateWorldWritableWarningAcknowledged(true)) - )); - assert!(matches!( - app_event_rx.try_recv(), - Ok(AppEvent::PersistWorldWritableWarningAcknowledged) - )); - assert!(app.ready_for_terminal_color_probe(/*has_pending_app_events*/ false)); -} - #[test] fn startup_waiting_gate_is_only_for_fresh_or_exit_session_selection() { assert_eq!( @@ -907,12 +839,7 @@ async fn queued_startup_app_event_owns_protected_view_before_draft_restore() -> while let Ok(event) = app_event_rx.try_recv() { assert!( - !matches!( - event, - AppEvent::StartFileSearch(_) - | AppEvent::UpdateWorldWritableWarningAcknowledged(_) - | AppEvent::PersistWorldWritableWarningAcknowledged - ), + !matches!(event, AppEvent::StartFileSearch(_)), "protected startup app event must own input before draft side effects: {event:?}" ); } diff --git a/codex-rs/tui/src/app_event.rs b/codex-rs/tui/src/app_event.rs index d585e004cd..575ff2d8eb 100644 --- a/codex-rs/tui/src/app_event.rs +++ b/codex-rs/tui/src/app_event.rs @@ -1216,26 +1216,6 @@ pub(crate) enum AppEvent { selection: PermissionProfileSelection, }, - /// Open the Windows world-writable directories warning. - /// If `preset` is `Some`, the confirmation will apply the provided - /// approval/sandbox configuration on Continue; if `None`, it performs no - /// policy change and only acknowledges/dismisses the warning. - #[cfg_attr(not(target_os = "windows"), allow(dead_code))] - OpenWorldWritableWarningConfirmation { - preset: Option, - profile_selection: Option, - /// Up to 3 sample world-writable directories to display in the warning. - sample_paths: Vec, - /// If there are more than `sample_paths`, this carries the remaining count. - extra_count: usize, - /// True when the scan failed (e.g. ACL query error) and protections could not be verified. - failed_scan: bool, - }, - - /// The startup world-writable scan finished and queued any protected warning it requires. - #[cfg_attr(not(target_os = "windows"), allow(dead_code))] - StartupWorldWritableScanCompleted, - /// Prompt to enable the Windows sandbox feature before using Agent mode. #[cfg_attr(not(target_os = "windows"), allow(dead_code))] OpenWindowsSandboxEnablePrompt { @@ -1319,20 +1299,12 @@ pub(crate) enum AppEvent { /// Clear all persisted local memory artifacts via the app-server. ResetMemories, - /// Update whether the world-writable directories warning has been acknowledged. - #[cfg_attr(not(target_os = "windows"), allow(dead_code))] - UpdateWorldWritableWarningAcknowledged(bool), - /// Update whether the rate limit switch prompt has been acknowledged for the session. UpdateRateLimitSwitchPromptHidden(bool), /// Update the Plan-mode-specific reasoning effort in memory. UpdatePlanModeReasoningEffort(Option), - /// Persist the acknowledgement flag for the world-writable directories warning. - #[cfg_attr(not(target_os = "windows"), allow(dead_code))] - PersistWorldWritableWarningAcknowledged, - /// Persist the acknowledgement flag for the rate limit switch prompt. PersistRateLimitSwitchPromptHidden, @@ -1345,10 +1317,6 @@ pub(crate) enum AppEvent { to_model: String, }, - /// Skip the next world-writable scan (one-shot) after a user-confirmed continue. - #[cfg_attr(not(target_os = "windows"), allow(dead_code))] - SkipNextWorldWritableScan, - /// Re-open the approval presets popup. OpenApprovalsPopup, diff --git a/codex-rs/tui/src/chatwidget/permission_popups.rs b/codex-rs/tui/src/chatwidget/permission_popups.rs index 53525f7c6e..77fe534ab8 100644 --- a/codex-rs/tui/src/chatwidget/permission_popups.rs +++ b/codex-rs/tui/src/chatwidget/permission_popups.rs @@ -358,20 +358,6 @@ impl ChatWidget { }); })]; } - if let Some((sample_paths, extra_count, failed_scan)) = - self.world_writable_warning_details() - { - let preset = preset.clone(); - return vec![Box::new(move |tx| { - tx.send(AppEvent::OpenWorldWritableWarningConfirmation { - preset: Some(preset.clone()), - profile_selection: profile_selection.clone(), - sample_paths: sample_paths.clone(), - extra_count, - failed_scan, - }); - })]; - } } } apply_actions() diff --git a/codex-rs/tui/src/chatwidget/permission_shortcuts.rs b/codex-rs/tui/src/chatwidget/permission_shortcuts.rs index 499dbe4b90..879d89e1d4 100644 --- a/codex-rs/tui/src/chatwidget/permission_shortcuts.rs +++ b/codex-rs/tui/src/chatwidget/permission_shortcuts.rs @@ -55,13 +55,12 @@ impl ChatWidget { { continue; } - // These modes still need the explicit Windows setup/warning flow. + // Agent mode still needs explicit Windows setup. #[cfg(target_os = "windows")] if preset.id == "auto" && reviewer == ApprovalsReviewer::User - && (crate::windows_sandbox::level_from_config(&self.config) + && crate::windows_sandbox::level_from_config(&self.config) == WindowsSandboxLevel::Disabled - || self.world_writable_warning_details().is_some()) { continue; } diff --git a/codex-rs/tui/src/chatwidget/settings.rs b/codex-rs/tui/src/chatwidget/settings.rs index 7cef3753c5..edda2fdba3 100644 --- a/codex-rs/tui/src/chatwidget/settings.rs +++ b/codex-rs/tui/src/chatwidget/settings.rs @@ -140,18 +140,6 @@ impl ChatWidget { self.refresh_status_surfaces(); } - pub(crate) fn set_world_writable_warning_acknowledged(&mut self, acknowledged: bool) { - self.local_settings.notices.hide_world_writable_warning = Some(acknowledged); - } - - #[cfg_attr(not(target_os = "windows"), allow(dead_code))] - pub(crate) fn world_writable_warning_hidden(&self) -> bool { - self.local_settings - .notices - .hide_world_writable_warning - .unwrap_or(false) - } - /// Override the reasoning effort used when Plan mode is active. /// /// When the active mask is already Plan, the override is applied immediately diff --git a/codex-rs/tui/src/chatwidget/tests/permission_shortcuts_tests.rs b/codex-rs/tui/src/chatwidget/tests/permission_shortcuts_tests.rs index d4124662ac..f45405051d 100644 --- a/codex-rs/tui/src/chatwidget/tests/permission_shortcuts_tests.rs +++ b/codex-rs/tui/src/chatwidget/tests/permission_shortcuts_tests.rs @@ -14,7 +14,6 @@ async fn permission_shortcuts_cycle_builtin_modes() { chat.chat_keymap.previous_permission_mode = vec![crate::key_hint::plain(KeyCode::F(7))]; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } for (current, reviewer, key, expected, next_reviewer) in [ diff --git a/codex-rs/tui/src/chatwidget/tests/permissions.rs b/codex-rs/tui/src/chatwidget/tests/permissions.rs index 680a94ed14..f0f087bb63 100644 --- a/codex-rs/tui/src/chatwidget/tests/permissions.rs +++ b/codex-rs/tui/src/chatwidget/tests/permissions.rs @@ -684,45 +684,6 @@ async fn fragmented_terminal_response_cannot_select_non_admin_windows_sandbox() } } -#[tokio::test] -async fn fragmented_terminal_response_cannot_acknowledge_world_writable_warning() { - let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; - chat.open_world_writable_warning_confirmation( - /*preset*/ None, - /*profile_selection*/ None, - Vec::new(), - /*extra_count*/ 0, - /*failed_scan*/ true, - ); - - chat.handle_key_event(KeyEvent::new(KeyCode::Char('1'), KeyModifiers::NONE)); - assert!(chat.has_active_view()); - assert!(rx.try_recv().is_err()); - - for character in "20;rgb:2222/ffff/ffff".chars() { - chat.handle_key_event(KeyEvent::new(KeyCode::Char(character), KeyModifiers::NONE)); - assert!( - !matches!( - rx.try_recv(), - Ok(AppEvent::UpdateWorldWritableWarningAcknowledged(_) - | AppEvent::PersistWorldWritableWarningAcknowledged) - ), - "a fragmented terminal response must not acknowledge the world-writable warning" - ); - } - - assert!(chat.has_active_view()); - chat.handle_key_event(KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE)); - assert!(matches!( - rx.try_recv(), - Ok(AppEvent::UpdateWorldWritableWarningAcknowledged(true)) - )); - assert!(matches!( - rx.try_recv(), - Ok(AppEvent::PersistWorldWritableWarningAcknowledged) - )); -} - #[tokio::test] async fn windows_sandbox_setup_starts_a_fresh_status_clock() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; @@ -977,7 +938,6 @@ async fn permissions_selection_emits_history_cell_when_selection_changes() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } chat.set_feature_enabled(Feature::GuardianApproval, /*enabled*/ true); @@ -1003,7 +963,6 @@ async fn permissions_selection_history_snapshot_after_mode_switch() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } chat.set_feature_enabled(Feature::GuardianApproval, /*enabled*/ false); @@ -1040,7 +999,6 @@ async fn permissions_selection_history_snapshot_full_access_to_default() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } chat.config @@ -1082,7 +1040,6 @@ async fn permissions_selection_emits_history_cell_when_current_is_selected() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } chat.config @@ -1116,7 +1073,6 @@ async fn permissions_selection_hides_auto_review_when_feature_disabled() { let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } chat.set_feature_enabled(Feature::GuardianApproval, /*enabled*/ false); @@ -1135,7 +1091,6 @@ async fn permissions_selection_hides_auto_review_when_feature_disabled_even_if_a let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } chat.set_feature_enabled(Feature::GuardianApproval, /*enabled*/ false); @@ -1164,7 +1119,6 @@ async fn permissions_selection_marks_auto_review_current_after_session_configure let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } let _ = chat @@ -1209,7 +1163,6 @@ async fn permissions_selection_marks_auto_review_current_with_custom_workspace_w let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } let _ = chat @@ -1258,7 +1211,6 @@ async fn permissions_selection_can_disable_auto_review() { let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } chat.set_feature_enabled(Feature::GuardianApproval, /*enabled*/ true); @@ -1298,7 +1250,6 @@ async fn permissions_selection_sends_approvals_reviewer_in_override_turn_context let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } chat.set_feature_enabled(Feature::GuardianApproval, /*enabled*/ true); @@ -1379,7 +1330,6 @@ async fn permissions_full_access_history_cell_emitted_only_after_confirmation() let (mut chat, mut rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await; #[cfg(target_os = "windows")] { - chat.local_settings.notices.hide_world_writable_warning = Some(true); chat.set_windows_sandbox_mode(Some(WindowsSandboxModeToml::Unelevated)); } chat.set_feature_enabled(Feature::GuardianApproval, /*enabled*/ false); diff --git a/codex-rs/tui/src/chatwidget/windows_sandbox_prompts.rs b/codex-rs/tui/src/chatwidget/windows_sandbox_prompts.rs index 724f7a4f3c..a2c81ec667 100644 --- a/codex-rs/tui/src/chatwidget/windows_sandbox_prompts.rs +++ b/codex-rs/tui/src/chatwidget/windows_sandbox_prompts.rs @@ -26,199 +26,6 @@ impl ChatWidget { && !crate::windows_sandbox::sandbox_setup_is_complete(self.config.codex_home.as_path()) } - #[cfg(target_os = "windows")] - pub(crate) fn world_writable_warning_details(&self) -> Option<(Vec, usize, bool)> { - if self - .local_settings - .notices - .hide_world_writable_warning - .unwrap_or(false) - { - return None; - } - let cwd = self.config.cwd.clone(); - let workspace_roots = self.config.effective_workspace_roots(); - let env_map: std::collections::HashMap = std::env::vars().collect(); - let permission_profile = self.config.permissions.effective_permission_profile(); - let Ok(permissions) = - codex_windows_sandbox::ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_workspace_roots( - &permission_profile, - workspace_roots.as_slice(), - ) - else { - return None; - }; - let result = codex_windows_sandbox::apply_world_writable_scan_and_denies_for_permissions( - self.config.codex_home.as_path(), - cwd.as_path(), - &env_map, - &permissions, - Some(self.config.codex_home.as_path()), - ); - crate::windows_sandbox::record_world_writable_scan_result(&self.session_telemetry, &result); - match result { - Ok(_) => None, - Err(_) => Some((Vec::new(), 0, true)), - } - } - - #[cfg(not(target_os = "windows"))] - #[allow(dead_code)] - pub(crate) fn world_writable_warning_details(&self) -> Option<(Vec, usize, bool)> { - None - } - - #[cfg(any(target_os = "windows", test))] - pub(crate) fn open_world_writable_warning_confirmation( - &mut self, - preset: Option, - profile_selection: Option, - sample_paths: Vec, - extra_count: usize, - failed_scan: bool, - ) { - let (approval, permission_profile, active_permission_profile) = match &preset { - Some(p) => ( - Some(AskForApproval::from(p.approval)), - Some(p.permission_profile.clone()), - Some(p.active_permission_profile.clone()), - ), - None => (None, None, None), - }; - let mut header_children: Vec> = Vec::new(); - let describe_profile = |profile: &PermissionProfile| { - if matches!(profile, PermissionProfile::Disabled) { - "Full Access mode" - } else if profile - .file_system_sandbox_policy() - .can_write_local_path_with_cwd(self.config.cwd.as_path(), self.config.cwd.as_path()) - { - "Agent mode" - } else { - "Read-Only mode" - } - }; - let mode_label = preset - .as_ref() - .map(|p| describe_profile(&p.permission_profile)) - .unwrap_or_else(|| { - describe_profile(&self.config.permissions.effective_permission_profile()) - }); - let info_line = if failed_scan { - Line::from(vec![ - "We couldn't complete the world-writable scan, so protections cannot be verified. " - .into(), - format!("The Windows sandbox cannot guarantee protection in {mode_label}.").red(), - ]) - } else { - Line::from(vec![ - "The Windows sandbox cannot protect writes to folders that are writable by Everyone.".into(), - " Consider removing write access for Everyone from the following folders:".into(), - ]) - }; - header_children.push(Box::new( - Paragraph::new(vec![info_line]).wrap(Wrap { trim: false }), - )); - - if !sample_paths.is_empty() { - // Show up to three examples and optionally an "and X more" line. - let mut lines: Vec = Vec::new(); - lines.push(Line::from("")); - for p in &sample_paths { - lines.push(Line::from(format!(" - {p}"))); - } - if extra_count > 0 { - lines.push(Line::from(format!("and {extra_count} more"))); - } - header_children.push(Box::new(Paragraph::new(lines).wrap(Wrap { trim: false }))); - } - let header = ColumnRenderable::with(header_children); - - // Build actions ensuring acknowledgement happens before applying the - // new permission profile, so downstream policy-change hooks don't - // re-trigger the warning. - let mut accept_actions: Vec = Vec::new(); - // Suppress the immediate re-scan only when a preset will be applied via - // /permissions, to avoid duplicate warnings from the ensuing policy change. - if preset.is_some() { - accept_actions.push(Box::new(|tx| { - tx.send(AppEvent::SkipNextWorldWritableScan); - })); - } - if let Some(selection) = profile_selection.clone() { - accept_actions.extend(Self::permission_profile_selection_actions(selection)); - } else if let (Some(approval), Some(permission_profile), Some(active_permission_profile)) = ( - approval, - permission_profile.clone(), - active_permission_profile.clone(), - ) { - accept_actions.extend(Self::approval_preset_actions( - approval, - permission_profile, - active_permission_profile, - mode_label.to_string(), - ApprovalsReviewer::User, - )); - } - - let mut accept_and_remember_actions: Vec = Vec::new(); - accept_and_remember_actions.push(Box::new(|tx| { - tx.send(AppEvent::UpdateWorldWritableWarningAcknowledged(true)); - tx.send(AppEvent::PersistWorldWritableWarningAcknowledged); - })); - if let Some(selection) = profile_selection { - accept_and_remember_actions - .extend(Self::permission_profile_selection_actions(selection)); - } else if let (Some(approval), Some(permission_profile), Some(active_permission_profile)) = - (approval, permission_profile, active_permission_profile) - { - accept_and_remember_actions.extend(Self::approval_preset_actions( - approval, - permission_profile, - active_permission_profile, - mode_label.to_string(), - ApprovalsReviewer::User, - )); - } - - let items = vec![ - SelectionItem { - name: "Continue".to_string(), - description: Some(format!("Apply {mode_label} for this session")), - actions: accept_actions, - dismiss_on_select: true, - require_explicit_confirmation: true, - ..Default::default() - }, - SelectionItem { - name: "Continue and don't warn again".to_string(), - description: Some(format!("Enable {mode_label} and remember this choice")), - actions: accept_and_remember_actions, - dismiss_on_select: true, - require_explicit_confirmation: true, - ..Default::default() - }, - ]; - - self.bottom_pane.show_selection_view(SelectionViewParams { - footer_hint: Some(standard_popup_hint_line()), - items, - header: Box::new(header), - ..Default::default() - }); - } - - #[cfg(all(not(target_os = "windows"), not(test)))] - pub(crate) fn open_world_writable_warning_confirmation( - &mut self, - _preset: Option, - _profile_selection: Option, - _sample_paths: Vec, - _extra_count: usize, - _failed_scan: bool, - ) { - } - #[cfg(any(target_os = "windows", test))] pub(crate) fn open_windows_sandbox_enable_prompt( &mut self, diff --git a/codex-rs/tui/src/windows_sandbox.rs b/codex-rs/tui/src/windows_sandbox.rs index 0a4d9e8199..ef70225e21 100644 --- a/codex-rs/tui/src/windows_sandbox.rs +++ b/codex-rs/tui/src/windows_sandbox.rs @@ -8,8 +8,6 @@ use crate::legacy_core::config::Config; use codex_config::types::WindowsSandboxModeToml; use codex_features::Feature; -#[cfg(target_os = "windows")] -use codex_otel::SessionTelemetry; use codex_protocol::config_types::WindowsSandboxLevel; #[cfg(target_os = "windows")] use codex_protocol::models::PermissionProfile; @@ -19,22 +17,6 @@ use codex_utils_absolute_path::AbsolutePathBuf; use std::collections::HashMap; use std::path::Path; -#[cfg(target_os = "windows")] -pub(crate) fn record_world_writable_scan_result( - session_telemetry: &SessionTelemetry, - result: &anyhow::Result, -) { - let (flagged_count, result) = match result { - Ok(flagged_count) => (*flagged_count as i64, "success"), - Err(_) => (0, "error"), - }; - session_telemetry.histogram( - "codex.windows_sandbox.world_writable_scan_flagged_directories", - flagged_count, - &[("result", result)], - ); -} - pub(crate) fn level_from_config(config: &Config) -> WindowsSandboxLevel { match config.permissions.windows_sandbox_mode { Some(WindowsSandboxModeToml::Elevated) => WindowsSandboxLevel::Elevated,