mirror of
https://github.com/openai/codex.git
synced 2026-09-05 15:18:41 +00:00
[codex] wire per-surface integrity state transport [ci changed_files]
This commit is contained in:
2
codex-rs/Cargo.lock
generated
2
codex-rs/Cargo.lock
generated
@@ -2539,6 +2539,7 @@ dependencies = [
|
||||
"codex-feedback",
|
||||
"codex-git-utils",
|
||||
"codex-hooks",
|
||||
"codex-http-state",
|
||||
"codex-install-context",
|
||||
"codex-login",
|
||||
"codex-mcp",
|
||||
@@ -3311,6 +3312,7 @@ dependencies = [
|
||||
"codex-aws-auth",
|
||||
"codex-client",
|
||||
"codex-feedback",
|
||||
"codex-http-state",
|
||||
"codex-login",
|
||||
"codex-model-provider-info",
|
||||
"codex-models-manager",
|
||||
|
||||
@@ -10,6 +10,18 @@ pub fn is_allowed_chatgpt_host(host: &str) -> bool {
|
||||
.any(|suffix| host.ends_with(suffix))
|
||||
}
|
||||
|
||||
/// Returns whether `url` is an HTTPS or secure WebSocket ChatGPT URL that Codex
|
||||
/// may treat as first-party traffic.
|
||||
pub fn is_allowed_chatgpt_request_url(url: &str) -> bool {
|
||||
let Ok(url) = reqwest::Url::parse(url) else {
|
||||
return false;
|
||||
};
|
||||
if !matches!(url.scheme(), "https" | "wss") {
|
||||
return false;
|
||||
}
|
||||
url.host_str().is_some_and(is_allowed_chatgpt_host)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -36,4 +48,25 @@ mod tests {
|
||||
assert!(!is_allowed_chatgpt_host(host));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recognizes_secure_chatgpt_request_urls() {
|
||||
for url in [
|
||||
"https://chatgpt.com/backend-api/codex/responses",
|
||||
"https://preview.chatgpt.com/backend-api/codex/models",
|
||||
"wss://chatgpt-staging.com/backend-api/codex/responses",
|
||||
] {
|
||||
assert!(is_allowed_chatgpt_request_url(url));
|
||||
}
|
||||
|
||||
for url in [
|
||||
"http://chatgpt.com/backend-api/codex/responses",
|
||||
"ws://chatgpt.com/backend-api/codex/responses",
|
||||
"https://api.openai.com/v1/responses",
|
||||
"https://chatgpt.com.evil.example/backend-api",
|
||||
"not a url",
|
||||
] {
|
||||
assert!(!is_allowed_chatgpt_request_url(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use crate::INTEGRITY_STATE_HEADER_NAME;
|
||||
use crate::INTEGRITY_STATE_UPDATE_HEADER_NAME;
|
||||
use http::Error as HttpError;
|
||||
use http::HeaderMap;
|
||||
use http::HeaderName;
|
||||
@@ -115,11 +117,12 @@ impl CodexRequestBuilder {
|
||||
|
||||
match self.builder.headers(headers).send().await {
|
||||
Ok(response) => {
|
||||
let headers = redacted_headers_for_logging(response.headers());
|
||||
tracing::debug!(
|
||||
method = %self.method,
|
||||
url = %self.url,
|
||||
status = %response.status(),
|
||||
headers = ?response.headers(),
|
||||
headers = ?headers,
|
||||
version = ?response.version(),
|
||||
"Request completed"
|
||||
);
|
||||
@@ -141,6 +144,19 @@ impl CodexRequestBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
fn redacted_headers_for_logging(headers: &HeaderMap) -> HeaderMap {
|
||||
let mut headers = headers.clone();
|
||||
for name in [
|
||||
INTEGRITY_STATE_HEADER_NAME,
|
||||
INTEGRITY_STATE_UPDATE_HEADER_NAME,
|
||||
] {
|
||||
if headers.contains_key(name) {
|
||||
headers.insert(name, HeaderValue::from_static("<redacted>"));
|
||||
}
|
||||
}
|
||||
headers
|
||||
}
|
||||
|
||||
struct HeaderMapInjector<'a>(&'a mut HeaderMap);
|
||||
|
||||
impl<'a> Injector for HeaderMapInjector<'a> {
|
||||
@@ -174,10 +190,39 @@ mod tests {
|
||||
use opentelemetry::trace::TracerProvider;
|
||||
use opentelemetry_sdk::propagation::TraceContextPropagator;
|
||||
use opentelemetry_sdk::trace::SdkTracerProvider;
|
||||
use pretty_assertions::assert_eq;
|
||||
use tracing::trace_span;
|
||||
use tracing_subscriber::layer::SubscriberExt;
|
||||
use tracing_subscriber::util::SubscriberInitExt;
|
||||
|
||||
#[test]
|
||||
fn redacted_headers_for_logging_hides_integrity_state() {
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert(
|
||||
INTEGRITY_STATE_HEADER_NAME,
|
||||
HeaderValue::from_static("ois1.request.nonce.ciphertext"),
|
||||
);
|
||||
headers.insert(
|
||||
INTEGRITY_STATE_UPDATE_HEADER_NAME,
|
||||
HeaderValue::from_static("ois1.response.nonce.ciphertext"),
|
||||
);
|
||||
headers.insert("x-request-id", HeaderValue::from_static("request-id"));
|
||||
|
||||
let redacted = redacted_headers_for_logging(&headers);
|
||||
|
||||
let mut expected = HeaderMap::new();
|
||||
expected.insert(
|
||||
INTEGRITY_STATE_HEADER_NAME,
|
||||
HeaderValue::from_static("<redacted>"),
|
||||
);
|
||||
expected.insert(
|
||||
INTEGRITY_STATE_UPDATE_HEADER_NAME,
|
||||
HeaderValue::from_static("<redacted>"),
|
||||
);
|
||||
expected.insert("x-request-id", HeaderValue::from_static("request-id"));
|
||||
assert_eq!(redacted, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inject_trace_headers_uses_current_span_context() {
|
||||
global::set_text_map_propagator(TraceContextPropagator::new());
|
||||
|
||||
@@ -11,6 +11,7 @@ mod transport;
|
||||
|
||||
pub use crate::chatgpt_cloudflare_cookies::with_chatgpt_cloudflare_cookie_store;
|
||||
pub use crate::chatgpt_hosts::is_allowed_chatgpt_host;
|
||||
pub use crate::chatgpt_hosts::is_allowed_chatgpt_request_url;
|
||||
pub use crate::custom_ca::BuildCustomCaTransportError;
|
||||
/// Test-only subprocess hook for custom CA coverage.
|
||||
///
|
||||
@@ -40,3 +41,6 @@ pub use crate::transport::ByteStream;
|
||||
pub use crate::transport::HttpTransport;
|
||||
pub use crate::transport::ReqwestTransport;
|
||||
pub use crate::transport::StreamResponse;
|
||||
|
||||
pub const INTEGRITY_STATE_HEADER_NAME: &str = "x-oai-is";
|
||||
pub const INTEGRITY_STATE_UPDATE_HEADER_NAME: &str = "x-oai-is-update";
|
||||
|
||||
@@ -48,6 +48,7 @@ codex-shell-command = { workspace = true }
|
||||
codex-execpolicy = { workspace = true }
|
||||
codex-git-utils = { workspace = true }
|
||||
codex-hooks = { workspace = true }
|
||||
codex-http-state = { workspace = true }
|
||||
codex-install-context = { workspace = true }
|
||||
codex-network-proxy = { workspace = true }
|
||||
codex-otel = { workspace = true }
|
||||
|
||||
@@ -312,7 +312,7 @@ impl AgentControl {
|
||||
.session
|
||||
.services
|
||||
.model_client
|
||||
.native_integrity_surface()
|
||||
.http_state_surface()
|
||||
{
|
||||
new_thread
|
||||
.thread
|
||||
@@ -320,7 +320,7 @@ impl AgentControl {
|
||||
.session
|
||||
.services
|
||||
.model_client
|
||||
.set_native_integrity_surface(surface);
|
||||
.set_http_state_surface(surface);
|
||||
}
|
||||
parent_thread
|
||||
.codex
|
||||
|
||||
@@ -10,8 +10,8 @@ use crate::context::ContextualUserFragment;
|
||||
use crate::context::SubagentNotification;
|
||||
use crate::init_state_db;
|
||||
use assert_matches::assert_matches;
|
||||
use codex_client::NativeIntegritySurface;
|
||||
use codex_features::Feature;
|
||||
use codex_http_state::HttpStateSurface;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_protocol::AgentPath;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
@@ -1586,7 +1586,7 @@ async fn spawn_child_completion_notifies_parent_history() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn spawn_thread_subagent_inherits_parent_native_integrity_surface() {
|
||||
async fn spawn_thread_subagent_inherits_parent_http_state_surface() {
|
||||
let harness = AgentControlHarness::new().await;
|
||||
let (parent_thread_id, parent_thread) = harness.start_thread().await;
|
||||
parent_thread
|
||||
@@ -1625,8 +1625,8 @@ async fn spawn_thread_subagent_inherits_parent_native_integrity_surface() {
|
||||
.session
|
||||
.services
|
||||
.model_client
|
||||
.native_integrity_surface(),
|
||||
Some(NativeIntegritySurface::CodexDesktop)
|
||||
.http_state_surface(),
|
||||
Some(HttpStateSurface::CodexDesktop)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,8 +62,8 @@ use codex_api::build_session_headers;
|
||||
use codex_api::create_text_param_for_request;
|
||||
use codex_api::response_create_client_metadata;
|
||||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_client::NativeIntegrityStateContext;
|
||||
use codex_client::NativeIntegritySurface;
|
||||
use codex_http_state::HttpStateContext;
|
||||
use codex_http_state::HttpStateSurface;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::RefreshTokenError;
|
||||
@@ -183,7 +183,7 @@ struct ModelClientState {
|
||||
beta_features_header: Option<String>,
|
||||
include_attestation: bool,
|
||||
attestation_provider: Option<Arc<dyn AttestationProvider>>,
|
||||
native_integrity_state: Option<NativeIntegrityStateContext>,
|
||||
http_state: Option<HttpStateContext>,
|
||||
disable_websockets: AtomicBool,
|
||||
cached_websocket_session: StdMutex<WebsocketSession>,
|
||||
}
|
||||
@@ -333,10 +333,10 @@ impl ModelClient {
|
||||
beta_features_header: Option<String>,
|
||||
attestation_provider: Option<Arc<dyn AttestationProvider>>,
|
||||
) -> Self {
|
||||
let native_integrity_state = auth_manager.as_ref().map(|auth_manager| {
|
||||
NativeIntegrityStateContext::new(
|
||||
let http_state = auth_manager.as_ref().map(|auth_manager| {
|
||||
HttpStateContext::new(
|
||||
auth_manager.codex_home().to_path_buf(),
|
||||
native_integrity_surface_for_session_source(&session_source),
|
||||
http_state_surface_for_session_source(&session_source),
|
||||
)
|
||||
});
|
||||
let model_provider = create_model_provider(provider_info, auth_manager);
|
||||
@@ -363,7 +363,7 @@ impl ModelClient {
|
||||
beta_features_header,
|
||||
include_attestation,
|
||||
attestation_provider,
|
||||
native_integrity_state,
|
||||
http_state,
|
||||
disable_websockets: AtomicBool::new(false),
|
||||
cached_websocket_session: StdMutex::new(WebsocketSession::default()),
|
||||
}),
|
||||
@@ -403,26 +403,26 @@ impl ModelClient {
|
||||
|
||||
pub(crate) fn set_app_server_client_name(&self, client_name: Option<&str>) {
|
||||
let surface = client_name
|
||||
.map(NativeIntegritySurface::from_app_server_client_name)
|
||||
.unwrap_or(NativeIntegritySurface::CodexCli);
|
||||
self.set_native_integrity_surface(surface);
|
||||
.map(HttpStateSurface::from_app_server_client_name)
|
||||
.unwrap_or(HttpStateSurface::CodexCli);
|
||||
self.set_http_state_surface(surface);
|
||||
}
|
||||
|
||||
pub(crate) fn native_integrity_surface(&self) -> Option<NativeIntegritySurface> {
|
||||
pub(crate) fn http_state_surface(&self) -> Option<HttpStateSurface> {
|
||||
self.state
|
||||
.native_integrity_state
|
||||
.http_state
|
||||
.as_ref()
|
||||
.map(NativeIntegrityStateContext::surface)
|
||||
.map(HttpStateContext::surface)
|
||||
}
|
||||
|
||||
pub(crate) fn native_integrity_state_context(&self) -> Option<NativeIntegrityStateContext> {
|
||||
self.state.native_integrity_state.clone()
|
||||
pub(crate) fn http_state_context(&self) -> Option<HttpStateContext> {
|
||||
self.state.http_state.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn set_native_integrity_surface(&self, surface: NativeIntegritySurface) {
|
||||
pub(crate) fn set_http_state_surface(&self, surface: HttpStateSurface) {
|
||||
if self
|
||||
.state
|
||||
.native_integrity_state
|
||||
.http_state
|
||||
.as_ref()
|
||||
.is_some_and(|state| state.set_surface(surface))
|
||||
{
|
||||
@@ -855,7 +855,7 @@ impl ModelClient {
|
||||
let api_auth = with_native_integrity_state(
|
||||
self.state.provider.api_auth().await?,
|
||||
auth.as_ref(),
|
||||
self.state.native_integrity_state.clone(),
|
||||
self.state.http_state.clone(),
|
||||
);
|
||||
Ok(CurrentClientSetup {
|
||||
auth,
|
||||
@@ -1780,18 +1780,16 @@ fn subagent_header_value(session_source: &SessionSource) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
fn native_integrity_surface_for_session_source(
|
||||
session_source: &SessionSource,
|
||||
) -> NativeIntegritySurface {
|
||||
fn http_state_surface_for_session_source(session_source: &SessionSource) -> HttpStateSurface {
|
||||
match session_source {
|
||||
SessionSource::Cli => NativeIntegritySurface::CodexTui,
|
||||
SessionSource::Exec => NativeIntegritySurface::CodexExec,
|
||||
SessionSource::VSCode => NativeIntegritySurface::CodexVscode,
|
||||
SessionSource::Cli => HttpStateSurface::CodexTui,
|
||||
SessionSource::Exec => HttpStateSurface::CodexExec,
|
||||
SessionSource::VSCode => HttpStateSurface::CodexVscode,
|
||||
SessionSource::SubAgent(_)
|
||||
| SessionSource::Internal(_)
|
||||
| SessionSource::Mcp
|
||||
| SessionSource::Custom(_)
|
||||
| SessionSource::Unknown => NativeIntegritySurface::CodexCli,
|
||||
| SessionSource::Unknown => HttpStateSurface::CodexCli,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,16 +110,12 @@ pub(crate) async fn run_codex_thread_interactive(
|
||||
}))
|
||||
.or_cancel(&cancel_token)
|
||||
.await??;
|
||||
if let Some(surface) = parent_session
|
||||
.services
|
||||
.model_client
|
||||
.native_integrity_surface()
|
||||
{
|
||||
if let Some(surface) = parent_session.services.model_client.http_state_surface() {
|
||||
codex
|
||||
.session
|
||||
.services
|
||||
.model_client
|
||||
.set_native_integrity_surface(surface);
|
||||
.set_http_state_surface(surface);
|
||||
}
|
||||
let thread_config = codex.thread_config_snapshot().await;
|
||||
let client_metadata = parent_session.app_server_client_metadata().await;
|
||||
|
||||
@@ -2,7 +2,7 @@ use super::*;
|
||||
use crate::mcp_tool_call::MCP_TOOL_APPROVAL_DECLINE_SYNTHETIC;
|
||||
use crate::mcp_tool_call::MCP_TOOL_APPROVAL_QUESTION_ID_PREFIX;
|
||||
use async_channel::bounded;
|
||||
use codex_client::NativeIntegritySurface;
|
||||
use codex_http_state::HttpStateSurface;
|
||||
use codex_mcp::CODEX_APPS_MCP_SERVER_NAME;
|
||||
use codex_protocol::config_types::ApprovalsReviewer;
|
||||
use codex_protocol::models::NetworkPermissions;
|
||||
@@ -183,13 +183,13 @@ async fn run_codex_thread_interactive_respects_pre_cancelled_spawn() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn run_codex_thread_interactive_inherits_parent_native_integrity_surface() {
|
||||
async fn run_codex_thread_interactive_inherits_parent_http_state_surface() {
|
||||
let (parent_session, parent_ctx, _rx_events) =
|
||||
crate::session::tests::make_session_and_context_with_rx().await;
|
||||
parent_session
|
||||
.services
|
||||
.model_client
|
||||
.set_native_integrity_surface(NativeIntegritySurface::CodexDesktop);
|
||||
.set_http_state_surface(HttpStateSurface::CodexDesktop);
|
||||
let cancel_token = CancellationToken::new();
|
||||
|
||||
let delegated = run_codex_thread_interactive(
|
||||
@@ -206,12 +206,8 @@ async fn run_codex_thread_interactive_inherits_parent_native_integrity_surface()
|
||||
.expect("delegate should spawn");
|
||||
|
||||
assert_eq!(
|
||||
delegated
|
||||
.session
|
||||
.services
|
||||
.model_client
|
||||
.native_integrity_surface(),
|
||||
Some(NativeIntegritySurface::CodexDesktop)
|
||||
delegated.session.services.model_client.http_state_surface(),
|
||||
Some(HttpStateSurface::CodexDesktop)
|
||||
);
|
||||
cancel_token.cancel();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
use crate::session::session::Session;
|
||||
use crate::session::turn_context::TurnContext;
|
||||
use codex_api::upload_local_file;
|
||||
use codex_client::NativeIntegrityStateContext;
|
||||
use codex_http_state::HttpStateContext;
|
||||
use codex_login::CodexAuth;
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
@@ -34,7 +34,7 @@ pub(crate) async fn rewrite_mcp_tool_arguments_for_openai_files(
|
||||
return Ok(Some(arguments_value));
|
||||
};
|
||||
let auth = sess.services.auth_manager.auth().await;
|
||||
let native_integrity_state = sess.services.model_client.native_integrity_state_context();
|
||||
let http_state = sess.services.model_client.http_state_context();
|
||||
let mut rewritten_arguments = arguments.clone();
|
||||
|
||||
for field_name in openai_file_input_params {
|
||||
@@ -44,7 +44,7 @@ pub(crate) async fn rewrite_mcp_tool_arguments_for_openai_files(
|
||||
let Some(uploaded_value) = rewrite_argument_value_for_openai_files(
|
||||
turn_context,
|
||||
auth.as_ref(),
|
||||
native_integrity_state.clone(),
|
||||
http_state.clone(),
|
||||
field_name,
|
||||
value,
|
||||
)
|
||||
@@ -65,7 +65,7 @@ pub(crate) async fn rewrite_mcp_tool_arguments_for_openai_files(
|
||||
async fn rewrite_argument_value_for_openai_files(
|
||||
turn_context: &TurnContext,
|
||||
auth: Option<&CodexAuth>,
|
||||
native_integrity_state: Option<NativeIntegrityStateContext>,
|
||||
http_state: Option<HttpStateContext>,
|
||||
field_name: &str,
|
||||
value: &JsonValue,
|
||||
) -> Result<Option<JsonValue>, String> {
|
||||
@@ -74,7 +74,7 @@ async fn rewrite_argument_value_for_openai_files(
|
||||
let rewritten = build_uploaded_local_argument_value(
|
||||
turn_context,
|
||||
auth,
|
||||
native_integrity_state,
|
||||
http_state,
|
||||
field_name,
|
||||
/*index*/ None,
|
||||
path_or_file_ref,
|
||||
@@ -91,7 +91,7 @@ async fn rewrite_argument_value_for_openai_files(
|
||||
let rewritten = build_uploaded_local_argument_value(
|
||||
turn_context,
|
||||
auth,
|
||||
native_integrity_state.clone(),
|
||||
http_state.clone(),
|
||||
field_name,
|
||||
Some(index),
|
||||
path_or_file_ref,
|
||||
@@ -108,7 +108,7 @@ async fn rewrite_argument_value_for_openai_files(
|
||||
async fn build_uploaded_local_argument_value(
|
||||
turn_context: &TurnContext,
|
||||
auth: Option<&CodexAuth>,
|
||||
native_integrity_state: Option<NativeIntegrityStateContext>,
|
||||
http_state: Option<HttpStateContext>,
|
||||
field_name: &str,
|
||||
index: Option<usize>,
|
||||
file_path: &str,
|
||||
@@ -128,7 +128,7 @@ async fn build_uploaded_local_argument_value(
|
||||
let upload_auth = codex_model_provider::with_native_integrity_state(
|
||||
codex_model_provider::auth_provider_from_auth(auth),
|
||||
Some(auth),
|
||||
native_integrity_state,
|
||||
http_state,
|
||||
);
|
||||
let uploaded = upload_local_file(
|
||||
turn_context.config.chatgpt_base_url.trim_end_matches('/'),
|
||||
@@ -244,7 +244,7 @@ mod tests {
|
||||
let rewritten = build_uploaded_local_argument_value(
|
||||
&turn_context,
|
||||
Some(&auth),
|
||||
/*native_integrity_state*/ None,
|
||||
/*http_state*/ None,
|
||||
"file",
|
||||
/*index*/ None,
|
||||
"file_report.csv",
|
||||
@@ -328,7 +328,7 @@ mod tests {
|
||||
let rewritten = rewrite_argument_value_for_openai_files(
|
||||
&turn_context,
|
||||
Some(&auth),
|
||||
/*native_integrity_state*/ None,
|
||||
/*http_state*/ None,
|
||||
"file",
|
||||
&serde_json::json!("file_report.csv"),
|
||||
)
|
||||
@@ -446,7 +446,7 @@ mod tests {
|
||||
let rewritten = rewrite_argument_value_for_openai_files(
|
||||
&turn_context,
|
||||
Some(&auth),
|
||||
/*native_integrity_state*/ None,
|
||||
/*http_state*/ None,
|
||||
"files",
|
||||
&serde_json::json!(["one.csv", "two.csv"]),
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@ codex-agent-identity = { workspace = true }
|
||||
codex-aws-auth = { workspace = true }
|
||||
codex-client = { workspace = true }
|
||||
codex-feedback = { workspace = true }
|
||||
codex-http-state = { workspace = true }
|
||||
codex-login = { workspace = true }
|
||||
codex-model-provider-info = { workspace = true }
|
||||
codex-models-manager = { workspace = true }
|
||||
|
||||
@@ -5,7 +5,7 @@ use codex_agent_identity::AgentTaskAuthorizationTarget;
|
||||
use codex_agent_identity::authorization_header_for_agent_task;
|
||||
use codex_api::AuthProvider;
|
||||
use codex_api::SharedAuthProvider;
|
||||
use codex_client::NativeIntegrityStateContext;
|
||||
use codex_http_state::HttpStateContext;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
@@ -125,7 +125,7 @@ pub fn auth_provider_from_auth(auth: &CodexAuth) -> SharedAuthProvider {
|
||||
pub fn with_native_integrity_state(
|
||||
auth_provider: SharedAuthProvider,
|
||||
auth: Option<&CodexAuth>,
|
||||
state: Option<NativeIntegrityStateContext>,
|
||||
state: Option<HttpStateContext>,
|
||||
) -> SharedAuthProvider {
|
||||
let Some(state) = state else {
|
||||
return auth_provider;
|
||||
|
||||
@@ -2,9 +2,9 @@ use codex_api::AuthProvider;
|
||||
use codex_api::SharedAuthProvider;
|
||||
use codex_client::INTEGRITY_STATE_HEADER_NAME;
|
||||
use codex_client::INTEGRITY_STATE_UPDATE_HEADER_NAME;
|
||||
use codex_client::NativeIntegrityStateContext;
|
||||
use codex_client::NativeIntegritySurface;
|
||||
use codex_client::is_allowed_chatgpt_request_url;
|
||||
use codex_http_state::HttpStateContext;
|
||||
use codex_http_state::HttpStateSurface;
|
||||
use http::HeaderMap;
|
||||
use http::HeaderValue;
|
||||
|
||||
@@ -55,12 +55,12 @@ impl AuthProvider for BearerAuthProvider {
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct NativeIntegrityAuthProvider {
|
||||
auth: SharedAuthProvider,
|
||||
state: NativeIntegrityStateContext,
|
||||
surface: NativeIntegritySurface,
|
||||
state: HttpStateContext,
|
||||
surface: HttpStateSurface,
|
||||
}
|
||||
|
||||
impl NativeIntegrityAuthProvider {
|
||||
pub(crate) fn new(auth: SharedAuthProvider, state: NativeIntegrityStateContext) -> Self {
|
||||
pub(crate) fn new(auth: SharedAuthProvider, state: HttpStateContext) -> Self {
|
||||
let surface = state.surface();
|
||||
Self {
|
||||
auth,
|
||||
@@ -81,9 +81,9 @@ impl AuthProvider for NativeIntegrityAuthProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.state.load_for_surface(self.surface) {
|
||||
Ok(Some(state_file)) => {
|
||||
if let Ok(header) = HeaderValue::from_str(&state_file.state) {
|
||||
match self.state.get_for_surface(self.surface) {
|
||||
Ok(Some(state)) => {
|
||||
if let Ok(header) = HeaderValue::from_str(&state) {
|
||||
let _ = headers.insert(INTEGRITY_STATE_HEADER_NAME, header);
|
||||
}
|
||||
}
|
||||
@@ -119,7 +119,7 @@ impl AuthProvider for NativeIntegrityAuthProvider {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Err(error) = self.state.compare_and_store_for_surface(
|
||||
if let Err(error) = self.state.compare_and_set_for_surface(
|
||||
self.surface,
|
||||
expected_state,
|
||||
next_state.to_string(),
|
||||
@@ -132,8 +132,7 @@ impl AuthProvider for NativeIntegrityAuthProvider {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codex_client::NativeIntegrityStateFile;
|
||||
use codex_client::NativeIntegrityStateStore;
|
||||
use codex_http_state::HttpStateStore;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::sync::Arc;
|
||||
use tempfile::TempDir;
|
||||
@@ -198,14 +197,14 @@ mod tests {
|
||||
#[test]
|
||||
fn native_integrity_provider_scopes_and_rotates_surface_state() {
|
||||
let codex_home = TempDir::new().expect("tempdir");
|
||||
let context = NativeIntegrityStateContext::new(
|
||||
let context = HttpStateContext::new(
|
||||
codex_home.path().to_path_buf(),
|
||||
NativeIntegritySurface::CodexDesktop,
|
||||
HttpStateSurface::CodexDesktop,
|
||||
);
|
||||
let store = NativeIntegrityStateStore::new(codex_home.path().to_path_buf());
|
||||
let store = HttpStateStore::new(codex_home.path().to_path_buf());
|
||||
store
|
||||
.replace(
|
||||
NativeIntegritySurface::CodexDesktop,
|
||||
.set(
|
||||
HttpStateSurface::CodexDesktop,
|
||||
"ois1.initial.nonce.ciphertext".to_string(),
|
||||
)
|
||||
.expect("state should store");
|
||||
@@ -213,10 +212,10 @@ mod tests {
|
||||
Arc::new(BearerAuthProvider::new("access-token".to_string())),
|
||||
context.clone(),
|
||||
);
|
||||
context.set_surface(NativeIntegritySurface::CodexCli);
|
||||
context.set_surface(HttpStateSurface::CodexCli);
|
||||
store
|
||||
.replace(
|
||||
NativeIntegritySurface::CodexCli,
|
||||
.set(
|
||||
HttpStateSurface::CodexCli,
|
||||
"ois1.cli.nonce.ciphertext".to_string(),
|
||||
)
|
||||
.expect("CLI state should store");
|
||||
@@ -245,21 +244,17 @@ mod tests {
|
||||
);
|
||||
assert_eq!(
|
||||
store
|
||||
.load(NativeIntegritySurface::CodexDesktop)
|
||||
.get(HttpStateSurface::CodexDesktop)
|
||||
.expect("state should load")
|
||||
.expect("state should exist"),
|
||||
NativeIntegrityStateFile {
|
||||
state: "ois1.rotated.nonce.ciphertext".to_string(),
|
||||
}
|
||||
"ois1.rotated.nonce.ciphertext"
|
||||
);
|
||||
assert_eq!(
|
||||
store
|
||||
.load(NativeIntegritySurface::CodexCli)
|
||||
.get(HttpStateSurface::CodexCli)
|
||||
.expect("CLI state should load")
|
||||
.expect("CLI state should exist"),
|
||||
NativeIntegrityStateFile {
|
||||
state: "ois1.cli.nonce.ciphertext".to_string(),
|
||||
}
|
||||
"ois1.cli.nonce.ciphertext"
|
||||
);
|
||||
|
||||
let mut external_headers = HeaderMap::new();
|
||||
|
||||
Reference in New Issue
Block a user