From 8b8caf5df72d0a727003ce4fec9777f8cb8734de Mon Sep 17 00:00:00 2001 From: Drew Hintz Date: Wed, 1 Jul 2026 07:22:09 +0000 Subject: [PATCH 1/5] sanitize exec config summary values --- codex-rs/exec/src/event_processor_with_human_output.rs | 9 +++++++++ .../exec/src/event_processor_with_human_output_tests.rs | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs index a667cdd53d7d..b37fbeeee484 100644 --- a/codex-rs/exec/src/event_processor_with_human_output.rs +++ b/codex-rs/exec/src/event_processor_with_human_output.rs @@ -217,6 +217,7 @@ impl EventProcessor for EventProcessorWithHumanOutput { const VERSION: &str = env!("CARGO_PKG_VERSION"); eprintln!("OpenAI Codex v{VERSION}\n--------"); for (key, value) in config_summary_entries(config, session_configured_event) { + let value = sanitize_config_summary_value(&value); eprintln!("{} {}", format!("{key}:").style(self.bold), value); } eprintln!("--------"); @@ -416,6 +417,14 @@ impl EventProcessor for EventProcessorWithHumanOutput { } } +/// Converts a config-summary value into printable single-line terminal text. +fn sanitize_config_summary_value(value: &str) -> String { + value + .chars() + .map(|ch| if ch.is_control() { '�' } else { ch }) + .collect() +} + fn config_summary_entries( config: &Config, session_configured_event: &SessionConfiguredEvent, diff --git a/codex-rs/exec/src/event_processor_with_human_output_tests.rs b/codex-rs/exec/src/event_processor_with_human_output_tests.rs index 5e89d48c7f93..0fc77af018d8 100644 --- a/codex-rs/exec/src/event_processor_with_human_output_tests.rs +++ b/codex-rs/exec/src/event_processor_with_human_output_tests.rs @@ -23,6 +23,7 @@ use super::EventProcessorWithHumanOutput; use super::config_summary_entries; use super::final_message_from_turn_items; use super::reasoning_text; +use super::sanitize_config_summary_value; use super::should_print_final_message_to_stdout; use super::should_print_final_message_to_tty; use crate::event_processor::EventProcessor; @@ -174,6 +175,14 @@ fn summarizes_managed_read_only_permission_profile() { ); } +#[test] +fn config_summary_values_are_single_line_terminal_text() { + assert_eq!( + sanitize_config_summary_value("repo\nmodel: other\r\x1b[2J\u{0007}"), + "repo�model: other��[2J�" + ); +} + #[tokio::test] async fn config_summary_entries_include_runtime_workspace_roots() { let codex_home = tempfile::tempdir().expect("create codex home"); From a985a390c288316663dbb9551af2b597d20345d8 Mon Sep 17 00:00:00 2001 From: Drew Hintz Date: Thu, 2 Jul 2026 07:25:10 +0000 Subject: [PATCH 2/5] tighten exec config summary sanitization --- .../src/event_processor_with_human_output.rs | 49 +++++++++++++++++-- ...event_processor_with_human_output_tests.rs | 6 ++- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs index b37fbeeee484..9a688f55c52c 100644 --- a/codex-rs/exec/src/event_processor_with_human_output.rs +++ b/codex-rs/exec/src/event_processor_with_human_output.rs @@ -419,10 +419,51 @@ impl EventProcessor for EventProcessorWithHumanOutput { /// Converts a config-summary value into printable single-line terminal text. fn sanitize_config_summary_value(value: &str) -> String { - value - .chars() - .map(|ch| if ch.is_control() { '�' } else { ch }) - .collect() + let mut sanitized = String::new(); + let mut pending_space = false; + + for ch in value.chars() { + if ch.is_whitespace() { + pending_space = !sanitized.is_empty(); + continue; + } + + if is_disallowed_config_summary_char(ch) { + continue; + } + + if pending_space { + sanitized.push(' '); + pending_space = false; + } + + sanitized.push(ch); + } + + sanitized +} + +/// Returns whether `ch` should be dropped from config-summary output. +fn is_disallowed_config_summary_char(ch: char) -> bool { + if ch.is_control() { + return true; + } + + matches!( + ch, + '\u{00AD}' + | '\u{034F}' + | '\u{061C}' + | '\u{180E}' + | '\u{200B}'..='\u{200F}' + | '\u{202A}'..='\u{202E}' + | '\u{2060}'..='\u{206F}' + | '\u{FE00}'..='\u{FE0F}' + | '\u{FEFF}' + | '\u{FFF9}'..='\u{FFFB}' + | '\u{1BCA0}'..='\u{1BCA3}' + | '\u{E0100}'..='\u{E01EF}' + ) } fn config_summary_entries( diff --git a/codex-rs/exec/src/event_processor_with_human_output_tests.rs b/codex-rs/exec/src/event_processor_with_human_output_tests.rs index 0fc77af018d8..71ffb305f2fc 100644 --- a/codex-rs/exec/src/event_processor_with_human_output_tests.rs +++ b/codex-rs/exec/src/event_processor_with_human_output_tests.rs @@ -178,8 +178,10 @@ fn summarizes_managed_read_only_permission_profile() { #[test] fn config_summary_values_are_single_line_terminal_text() { assert_eq!( - sanitize_config_summary_value("repo\nmodel: other\r\x1b[2J\u{0007}"), - "repo�model: other��[2J�" + sanitize_config_summary_value( + " repo\t|\nWorking\x1b\u{0007}\u{009D}\u{009C} | \u{202E}T\u{2066}ext " + ), + "repo | Working | Text" ); } From d00cfdd29cdf7dac6adc489a3b8f7b55c74f83e8 Mon Sep 17 00:00:00 2001 From: Drew Hintz Date: Thu, 2 Jul 2026 19:06:34 +0000 Subject: [PATCH 3/5] remove redundant sanitizer comments --- codex-rs/exec/src/event_processor_with_human_output.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs index 9a688f55c52c..9c3839eabfaa 100644 --- a/codex-rs/exec/src/event_processor_with_human_output.rs +++ b/codex-rs/exec/src/event_processor_with_human_output.rs @@ -417,7 +417,6 @@ impl EventProcessor for EventProcessorWithHumanOutput { } } -/// Converts a config-summary value into printable single-line terminal text. fn sanitize_config_summary_value(value: &str) -> String { let mut sanitized = String::new(); let mut pending_space = false; @@ -443,7 +442,6 @@ fn sanitize_config_summary_value(value: &str) -> String { sanitized } -/// Returns whether `ch` should be dropped from config-summary output. fn is_disallowed_config_summary_char(ch: char) -> bool { if ch.is_control() { return true; From fb0ffd6371a436e074f83e182459d4fe4ad6ed47 Mon Sep 17 00:00:00 2001 From: Drew Hintz Date: Thu, 2 Jul 2026 23:37:59 +0000 Subject: [PATCH 4/5] simplify exec config summary sanitization --- .../src/event_processor_with_human_output.rs | 62 ++++++------------- ...event_processor_with_human_output_tests.rs | 6 +- 2 files changed, 20 insertions(+), 48 deletions(-) diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs index 9c3839eabfaa..2db64561537d 100644 --- a/codex-rs/exec/src/event_processor_with_human_output.rs +++ b/codex-rs/exec/src/event_processor_with_human_output.rs @@ -418,50 +418,24 @@ impl EventProcessor for EventProcessorWithHumanOutput { } fn sanitize_config_summary_value(value: &str) -> String { - let mut sanitized = String::new(); - let mut pending_space = false; - - for ch in value.chars() { - if ch.is_whitespace() { - pending_space = !sanitized.is_empty(); - continue; - } - - if is_disallowed_config_summary_char(ch) { - continue; - } - - if pending_space { - sanitized.push(' '); - pending_space = false; - } - - sanitized.push(ch); - } - - sanitized -} - -fn is_disallowed_config_summary_char(ch: char) -> bool { - if ch.is_control() { - return true; - } - - matches!( - ch, - '\u{00AD}' - | '\u{034F}' - | '\u{061C}' - | '\u{180E}' - | '\u{200B}'..='\u{200F}' - | '\u{202A}'..='\u{202E}' - | '\u{2060}'..='\u{206F}' - | '\u{FE00}'..='\u{FE0F}' - | '\u{FEFF}' - | '\u{FFF9}'..='\u{FFFB}' - | '\u{1BCA0}'..='\u{1BCA3}' - | '\u{E0100}'..='\u{E01EF}' - ) + value + .chars() + .map(|ch| { + if ch.is_control() + || matches!( + ch, + '\u{061C}' + | '\u{200E}'..='\u{200F}' + | '\u{202A}'..='\u{202E}' + | '\u{2066}'..='\u{2069}' + ) + { + '�' + } else { + ch + } + }) + .collect() } fn config_summary_entries( diff --git a/codex-rs/exec/src/event_processor_with_human_output_tests.rs b/codex-rs/exec/src/event_processor_with_human_output_tests.rs index 71ffb305f2fc..95df404ab36a 100644 --- a/codex-rs/exec/src/event_processor_with_human_output_tests.rs +++ b/codex-rs/exec/src/event_processor_with_human_output_tests.rs @@ -178,10 +178,8 @@ fn summarizes_managed_read_only_permission_profile() { #[test] fn config_summary_values_are_single_line_terminal_text() { assert_eq!( - sanitize_config_summary_value( - " repo\t|\nWorking\x1b\u{0007}\u{009D}\u{009C} | \u{202E}T\u{2066}ext " - ), - "repo | Working | Text" + sanitize_config_summary_value("repo\nmodel: other\r\x1b[2J\u{0007}\u{202E}rtl\u{2066}"), + "repo�model: other��[2J��rtl�" ); } From b879b4927f62838b832b39f4850103eb9df7d835 Mon Sep 17 00:00:00 2001 From: Drew Hintz Date: Thu, 2 Jul 2026 23:55:38 +0000 Subject: [PATCH 5/5] document filtered bidi controls --- .../src/event_processor_with_human_output.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/codex-rs/exec/src/event_processor_with_human_output.rs b/codex-rs/exec/src/event_processor_with_human_output.rs index 2db64561537d..c4c18d155d2c 100644 --- a/codex-rs/exec/src/event_processor_with_human_output.rs +++ b/codex-rs/exec/src/event_processor_with_human_output.rs @@ -421,13 +421,21 @@ fn sanitize_config_summary_value(value: &str) -> String { value .chars() .map(|ch| { - if ch.is_control() + if ch.is_control() // C0/C1 controls, including newlines and escape. || matches!( ch, - '\u{061C}' - | '\u{200E}'..='\u{200F}' - | '\u{202A}'..='\u{202E}' - | '\u{2066}'..='\u{2069}' + '\u{061C}' // Arabic letter mark. + | '\u{200E}' // Left-to-right mark. + | '\u{200F}' // Right-to-left mark. + | '\u{202A}' // Left-to-right embedding. + | '\u{202B}' // Right-to-left embedding. + | '\u{202C}' // Pop directional formatting. + | '\u{202D}' // Left-to-right override. + | '\u{202E}' // Right-to-left override. + | '\u{2066}' // Left-to-right isolate. + | '\u{2067}' // Right-to-left isolate. + | '\u{2068}' // First strong isolate. + | '\u{2069}' // Pop directional isolate. ) { '�'