mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
[codex] wire native HTTP state transport [ci changed_files]
This commit is contained in:
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user