From c1963a7f2efdf67bd46f53860a43e1e6c21d5c90 Mon Sep 17 00:00:00 2001 From: Cooper Gamble Date: Wed, 3 Jun 2026 04:23:45 +0000 Subject: [PATCH] [codex] seed empty native HTTP state from responses [ci changed_files] --- codex-rs/http-state/src/lib.rs | 4 ++ .../src/bearer_auth_provider.rs | 59 +++++++++++++++---- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/codex-rs/http-state/src/lib.rs b/codex-rs/http-state/src/lib.rs index 7c6d3600e9..96f3ea398c 100644 --- a/codex-rs/http-state/src/lib.rs +++ b/codex-rs/http-state/src/lib.rs @@ -176,6 +176,10 @@ impl HttpStateContext { self.store.get(surface) } + pub fn set_for_surface(&self, surface: HttpStateSurface, state: String) -> io::Result<()> { + self.store.set(surface, state) + } + pub fn compare_and_set_for_surface( &self, surface: HttpStateSurface, diff --git a/codex-rs/model-provider/src/bearer_auth_provider.rs b/codex-rs/model-provider/src/bearer_auth_provider.rs index f971b18473..88c771af78 100644 --- a/codex-rs/model-provider/src/bearer_auth_provider.rs +++ b/codex-rs/model-provider/src/bearer_auth_provider.rs @@ -106,12 +106,6 @@ impl AuthProvider for NativeIntegrityAuthProvider { return; } - let Some(expected_state) = request_headers - .get(INTEGRITY_STATE_HEADER_NAME) - .and_then(|value| value.to_str().ok()) - else { - return; - }; let Some(next_state) = response_headers .get(INTEGRITY_STATE_UPDATE_HEADER_NAME) .and_then(|value| value.to_str().ok()) @@ -119,11 +113,21 @@ impl AuthProvider for NativeIntegrityAuthProvider { return; }; - if let Err(error) = self.state.compare_and_set_for_surface( - self.surface, - expected_state, - next_state.to_string(), - ) { + let expected_state = request_headers + .get(INTEGRITY_STATE_HEADER_NAME) + .and_then(|value| value.to_str().ok()); + let result = match expected_state { + Some(expected_state) => self.state.compare_and_set_for_surface( + self.surface, + expected_state, + next_state.to_string(), + ), + None => self + .state + .set_for_surface(self.surface, next_state.to_string()) + .map(|()| true), + }; + if let Err(error) = result { tracing::warn!("failed to rotate native integrity state: {error}"); } } @@ -264,4 +268,37 @@ mod tests { ); assert!(!external_headers.contains_key(INTEGRITY_STATE_HEADER_NAME)); } + + #[test] + fn native_integrity_provider_seeds_empty_surface_from_response() { + let codex_home = TempDir::new().expect("tempdir"); + let context = HttpStateContext::new( + codex_home.path().to_path_buf(), + HttpStateSurface::CodexDesktop, + ); + let store = HttpStateStore::new(codex_home.path().to_path_buf()); + let provider = NativeIntegrityAuthProvider::new( + Arc::new(BearerAuthProvider::new("access-token".to_string())), + context, + ); + let mut response_headers = HeaderMap::new(); + response_headers.insert( + INTEGRITY_STATE_UPDATE_HEADER_NAME, + HeaderValue::from_static("ois1.seeded.nonce.ciphertext"), + ); + + provider.observe_response_headers( + "https://chatgpt.com/backend-api/codex/responses", + &HeaderMap::new(), + &response_headers, + ); + + assert_eq!( + store + .get(HttpStateSurface::CodexDesktop) + .expect("state should load") + .expect("state should exist"), + "ois1.seeded.nonce.ciphertext" + ); + } }