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
This commit is contained in:
Eric Traut
2026-09-11 22:53:21 +00:00
committed by copyberry
parent 202d61c629
commit f3c4d082d9
13 changed files with 4 additions and 615 deletions

View File

@@ -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

View File

@@ -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<String, String> =
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())

View File

@@ -8,68 +8,6 @@ use super::*;
#[derive(Default)]
pub(super) struct WindowsSandboxState {
pub(super) setup_started_at: Option<Instant>,
// 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<AbsolutePathBuf>,
env_map: std::collections::HashMap<String, String>,
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 {

View File

@@ -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<String, String> = 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;
}

View File

@@ -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:?}"
);
}

View File

@@ -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<ApprovalPreset>,
profile_selection: Option<PermissionProfileSelection>,
/// Up to 3 sample world-writable directories to display in the warning.
sample_paths: Vec<String>,
/// 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<ReasoningEffort>),
/// 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,

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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 [

View File

@@ -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);

View File

@@ -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<String>, 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<String, String> = 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<String>, usize, bool)> {
None
}
#[cfg(any(target_os = "windows", test))]
pub(crate) fn open_world_writable_warning_confirmation(
&mut self,
preset: Option<ApprovalPreset>,
profile_selection: Option<PermissionProfileSelection>,
sample_paths: Vec<String>,
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<Box<dyn Renderable>> = 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<Line> = 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<SelectionAction> = 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<SelectionAction> = 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<ApprovalPreset>,
_profile_selection: Option<PermissionProfileSelection>,
_sample_paths: Vec<String>,
_extra_count: usize,
_failed_scan: bool,
) {
}
#[cfg(any(target_os = "windows", test))]
pub(crate) fn open_windows_sandbox_enable_prompt(
&mut self,

View File

@@ -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<usize>,
) {
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,