From cc35a0c8847b7f2ce90f15ab443dbc1e6097f58f Mon Sep 17 00:00:00 2001 From: Chris Bookholt Date: Fri, 3 Jul 2026 20:10:31 -0700 Subject: [PATCH] fix(git): accept empty config count --- .../git-utils/src/git_config_environment.rs | 13 +++++-- .../src/git_config_environment_tests.rs | 38 +++++++++++++++++++ 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/codex-rs/git-utils/src/git_config_environment.rs b/codex-rs/git-utils/src/git_config_environment.rs index f386cfda39..1cd718c13e 100644 --- a/codex-rs/git-utils/src/git_config_environment.rs +++ b/codex-rs/git-utils/src/git_config_environment.rs @@ -61,11 +61,16 @@ impl GitConfigEnvironmentSnapshot { .collect::>(); if let Some(raw_count) = value_for(OsStr::new("GIT_CONFIG_COUNT")) { - let count = raw_count + let raw_count = raw_count .to_str() - .ok_or_else(|| invalid_environment("non-UTF-8 GIT_CONFIG_COUNT"))? - .parse::() - .map_err(|_| invalid_environment("invalid GIT_CONFIG_COUNT"))?; + .ok_or_else(|| invalid_environment("non-UTF-8 GIT_CONFIG_COUNT"))?; + let count = if raw_count.is_empty() { + 0 + } else { + raw_count + .parse::() + .map_err(|_| invalid_environment("invalid GIT_CONFIG_COUNT"))? + }; if count > MAX_CONFIG_ENVIRONMENT_ENTRIES { return Err(invalid_environment( "GIT_CONFIG_COUNT exceeds the supported safety bound", diff --git a/codex-rs/git-utils/src/git_config_environment_tests.rs b/codex-rs/git-utils/src/git_config_environment_tests.rs index 3cdec9a790..54e4cd5766 100644 --- a/codex-rs/git-utils/src/git_config_environment_tests.rs +++ b/codex-rs/git-utils/src/git_config_environment_tests.rs @@ -40,10 +40,15 @@ fn snapshot_binds_present_empty_and_absent_fixed_and_indexed_values() { snapshot.value("GIT_CONFIG_PARAMETERS"), Some(OsStr::new("'safe.parameter'='present'")) ); + assert_eq!(snapshot.value("GIT_CONFIG_COUNT"), Some(OsStr::new("2"))); assert_eq!( snapshot.value("GIT_CONFIG_KEY_0"), Some(OsStr::new("safe.one")) ); + assert_eq!( + snapshot.value("GIT_CONFIG_VALUE_0"), + Some(OsStr::new("present")) + ); assert_eq!(snapshot.value("GIT_CONFIG_VALUE_1"), None); let mut command = Command::new("git"); @@ -80,6 +85,39 @@ fn snapshot_rejects_malformed_or_unbounded_command_config_count() { } } +#[test] +fn snapshot_treats_empty_command_config_count_as_zero() { + let values = BTreeMap::from([ + (OsString::from("GIT_CONFIG_COUNT"), OsString::from("")), + ( + OsString::from("GIT_CONFIG_KEY_0"), + OsString::from("untrusted.key"), + ), + ( + OsString::from("GIT_CONFIG_VALUE_0"), + OsString::from("untrusted-value"), + ), + ]); + let snapshot = GitConfigEnvironmentSnapshot::capture_from(|name| values.get(name).cloned()) + .expect("capture empty count"); + + assert_eq!(snapshot.value("GIT_CONFIG_COUNT"), Some(OsStr::new(""))); + assert_eq!(snapshot.value("GIT_CONFIG_KEY_0"), None); + assert_eq!(snapshot.value("GIT_CONFIG_VALUE_0"), None); + + let mut command = Command::new("git"); + command.env("GIT_CONFIG_COUNT", "1"); + snapshot.apply_to(&mut command); + let child_environment = command + .get_envs() + .map(|(name, value)| (name.to_owned(), value.map(OsStr::to_owned))) + .collect::>(); + assert_eq!( + child_environment.get(OsStr::new("GIT_CONFIG_COUNT")), + Some(&Some(OsString::from(""))) + ); +} + #[cfg(unix)] #[test] fn snapshot_preserves_non_utf8_config_path_bytes() {