mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Preserve Cloudfare HTTP cookies in codex (#17783)
## Summary - Adds a process-local, in-memory cookie store for ChatGPT HTTP clients. - Limits cookie storage and replay to a shared ChatGPT host allowlist. - Wires the shared store into the default Codex reqwest client and backend client. - Shares the ChatGPT host allowlist with remote-control URL validation to avoid drift. - Enables reqwest cookie support and updates lockfiles.
This commit is contained in:
committed by
GitHub
Unverified
parent
be75785504
commit
c5e9c6f71f
Generated
+5
@@ -755,6 +755,8 @@
|
||||
"convert_case_0.10.0": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"unicode-segmentation\",\"req\":\"^1.9.0\"}],\"features\":{}}",
|
||||
"convert_case_0.6.0": "{\"dependencies\":[{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.7\"},{\"kind\":\"dev\",\"name\":\"strum\",\"req\":\"^0.18.0\"},{\"kind\":\"dev\",\"name\":\"strum_macros\",\"req\":\"^0.18.0\"},{\"name\":\"unicode-segmentation\",\"req\":\"^1.9.0\"}],\"features\":{\"random\":[\"rand\"]}}",
|
||||
"cookie-factory_0.3.3": "{\"dependencies\":[{\"features\":[\"attributes\"],\"kind\":\"dev\",\"name\":\"async-std\",\"req\":\"^1.9.0\"},{\"name\":\"futures\",\"optional\":true,\"req\":\"^0.3.16\"},{\"kind\":\"dev\",\"name\":\"maplit\",\"req\":\"^1.0\"}],\"features\":{\"async\":[\"futures\"],\"default\":[\"std\",\"async\"],\"std\":[]}}",
|
||||
"cookie_0.18.1": "{\"dependencies\":[{\"name\":\"aes-gcm\",\"optional\":true,\"req\":\"^0.10.0\"},{\"name\":\"base64\",\"optional\":true,\"req\":\"^0.22\"},{\"name\":\"hkdf\",\"optional\":true,\"req\":\"^0.12.0\"},{\"name\":\"hmac\",\"optional\":true,\"req\":\"^0.12.0\"},{\"name\":\"percent-encoding\",\"optional\":true,\"req\":\"^2.0\"},{\"name\":\"rand\",\"optional\":true,\"req\":\"^0.8\"},{\"name\":\"sha2\",\"optional\":true,\"req\":\"^0.10.0\"},{\"name\":\"subtle\",\"optional\":true,\"req\":\"^2.3\"},{\"default_features\":false,\"features\":[\"std\",\"parsing\",\"formatting\",\"macros\"],\"name\":\"time\",\"req\":\"^0.3\"},{\"kind\":\"build\",\"name\":\"version_check\",\"req\":\"^0.9.4\"}],\"features\":{\"key-expansion\":[\"sha2\",\"hkdf\"],\"percent-encode\":[\"percent-encoding\"],\"private\":[\"aes-gcm\",\"base64\",\"rand\",\"subtle\"],\"secure\":[\"private\",\"signed\",\"key-expansion\"],\"signed\":[\"hmac\",\"sha2\",\"base64\",\"rand\",\"subtle\"]}}",
|
||||
"cookie_store_0.22.1": "{\"dependencies\":[{\"features\":[\"percent-encode\"],\"name\":\"cookie\",\"req\":\"^0.18.0\"},{\"name\":\"document-features\",\"req\":\"^0.2.10\"},{\"name\":\"idna\",\"req\":\"^1.0\"},{\"name\":\"indexmap\",\"optional\":true,\"req\":\"^2.6.0\"},{\"name\":\"log\",\"req\":\"^0.4.17\"},{\"name\":\"publicsuffix\",\"optional\":true,\"req\":\"^2.2.3\"},{\"name\":\"ron\",\"optional\":true,\"req\":\"^0.10.1\"},{\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.147\"},{\"name\":\"serde_derive\",\"optional\":true,\"req\":\"^1.0.147\"},{\"name\":\"serde_json\",\"optional\":true,\"req\":\"^1.0.87\"},{\"name\":\"time\",\"req\":\"^0.3.47\"},{\"name\":\"url\",\"req\":\"^2.3.1\"}],\"features\":{\"default\":[\"public_suffix\",\"serde_json\"],\"log_secure_cookie_values\":[],\"preserve_order\":[\"dep:indexmap\"],\"public_suffix\":[\"dep:publicsuffix\"],\"serde\":[\"dep:serde\",\"dep:serde_derive\"],\"serde_json\":[\"serde\",\"dep:serde_json\"],\"serde_ron\":[\"serde\",\"dep:ron\"],\"wasm-bindgen\":[\"time/wasm-bindgen\"]}}",
|
||||
"core-foundation-sys_0.8.7": "{\"dependencies\":[],\"features\":{\"default\":[\"link\"],\"link\":[],\"mac_os_10_7_support\":[],\"mac_os_10_8_features\":[]}}",
|
||||
"core-foundation_0.10.1": "{\"dependencies\":[{\"default_features\":false,\"name\":\"core-foundation-sys\",\"req\":\"^0.8\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^1\"}],\"features\":{\"default\":[\"link\"],\"link\":[\"core-foundation-sys/link\"],\"mac_os_10_7_support\":[\"core-foundation-sys/mac_os_10_7_support\"],\"mac_os_10_8_features\":[\"core-foundation-sys/mac_os_10_8_features\"],\"with-uuid\":[\"dep:uuid\"]}}",
|
||||
"core-foundation_0.9.4": "{\"dependencies\":[{\"name\":\"chrono\",\"optional\":true,\"req\":\"^0.4\"},{\"default_features\":false,\"name\":\"core-foundation-sys\",\"req\":\"^0.8.6\"},{\"name\":\"libc\",\"req\":\"^0.2\"},{\"name\":\"uuid\",\"optional\":true,\"req\":\"^0.5\"}],\"features\":{\"default\":[\"link\"],\"link\":[\"core-foundation-sys/link\"],\"mac_os_10_7_support\":[\"core-foundation-sys/mac_os_10_7_support\"],\"mac_os_10_8_features\":[\"core-foundation-sys/mac_os_10_8_features\"],\"with-chrono\":[\"chrono\"],\"with-uuid\":[\"uuid\"]}}",
|
||||
@@ -833,6 +835,7 @@
|
||||
"display_container_0.9.0": "{\"dependencies\":[{\"name\":\"either\",\"req\":\"^1.8\"},{\"name\":\"indenter\",\"req\":\"^0.3.3\"}],\"features\":{}}",
|
||||
"displaydoc_0.2.5": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"pretty_assertions\",\"req\":\"^0.6.1\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"rustversion\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1\"},{\"name\":\"syn\",\"req\":\"^2.0\"},{\"kind\":\"dev\",\"name\":\"thiserror\",\"req\":\"^1.0.24\"},{\"kind\":\"dev\",\"name\":\"trybuild\",\"req\":\"^1.0\"}],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
|
||||
"dns-lookup_3.0.1": "{\"dependencies\":[{\"name\":\"cfg-if\",\"req\":\"^1.0\"},{\"name\":\"libc\",\"req\":\"^0.2\",\"target\":\"cfg(unix)\"},{\"name\":\"socket2\",\"req\":\"^0.6.0\"},{\"features\":[\"Win32_Networking_WinSock\",\"Win32_Foundation\"],\"name\":\"windows-sys\",\"req\":\"^0.60\",\"target\":\"cfg(windows)\"}],\"features\":{}}",
|
||||
"document-features_0.2.12": "{\"dependencies\":[{\"name\":\"litrs\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[],\"self-test\":[]}}",
|
||||
"dotenvy_0.15.7": "{\"dependencies\":[{\"name\":\"clap\",\"optional\":true,\"req\":\"^3.2\"},{\"kind\":\"dev\",\"name\":\"once_cell\",\"req\":\"^1.16.0\"},{\"kind\":\"dev\",\"name\":\"tempfile\",\"req\":\"^3.3.0\"}],\"features\":{\"cli\":[\"clap\"]}}",
|
||||
"downcast-rs_1.2.1": "{\"dependencies\":[],\"features\":{\"default\":[\"std\"],\"std\":[]}}",
|
||||
"dtor-proc-macro_0.0.6": "{\"dependencies\":[],\"features\":{\"default\":[]}}",
|
||||
@@ -1140,6 +1143,7 @@
|
||||
"linux-raw-sys_0.12.1": "{\"dependencies\":[{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.100\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"auxvec\":[],\"bootparam\":[],\"btrfs\":[],\"default\":[\"std\",\"general\",\"errno\"],\"elf\":[],\"elf_uapi\":[],\"errno\":[],\"general\":[],\"if_arp\":[],\"if_ether\":[],\"if_packet\":[],\"if_tun\":[],\"image\":[],\"io_uring\":[],\"ioctl\":[],\"landlock\":[],\"loop_device\":[],\"mempolicy\":[],\"net\":[],\"netlink\":[],\"no_std\":[],\"prctl\":[],\"ptrace\":[],\"rustc-dep-of-std\":[\"core\",\"no_std\"],\"std\":[],\"system\":[],\"vm_sockets\":[],\"xdp\":[]}}",
|
||||
"linux-raw-sys_0.4.15": "{\"dependencies\":[{\"name\":\"compiler_builtins\",\"optional\":true,\"req\":\"^0.1.49\"},{\"name\":\"core\",\"optional\":true,\"package\":\"rustc-std-workspace-core\",\"req\":\"^1.0.0\"},{\"kind\":\"dev\",\"name\":\"libc\",\"req\":\"^0.2.100\"},{\"kind\":\"dev\",\"name\":\"static_assertions\",\"req\":\"^1.1.0\"}],\"features\":{\"bootparam\":[],\"btrfs\":[],\"default\":[\"std\",\"general\",\"errno\"],\"elf\":[],\"elf_uapi\":[],\"errno\":[],\"general\":[],\"if_arp\":[],\"if_ether\":[],\"if_packet\":[],\"io_uring\":[],\"ioctl\":[],\"landlock\":[],\"loop_device\":[],\"mempolicy\":[],\"net\":[],\"netlink\":[],\"no_std\":[],\"prctl\":[],\"ptrace\":[],\"rustc-dep-of-std\":[\"core\",\"compiler_builtins\",\"no_std\"],\"std\":[],\"system\":[],\"xdp\":[]}}",
|
||||
"litemap_0.8.1": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5.0\",\"target\":\"cfg(not(target_arch = \\\"wasm32\\\"))\"},{\"default_features\":false,\"name\":\"databake\",\"optional\":true,\"req\":\"^0.2.0\"},{\"default_features\":false,\"features\":[\"use-std\"],\"kind\":\"dev\",\"name\":\"postcard\",\"req\":\"^1.0.3\"},{\"kind\":\"dev\",\"name\":\"rand\",\"req\":\"^0.9\"},{\"features\":[\"validation\"],\"kind\":\"dev\",\"name\":\"rkyv\",\"req\":\"^0.7\"},{\"default_features\":false,\"features\":[\"alloc\"],\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0.220\"},{\"default_features\":false,\"kind\":\"dev\",\"name\":\"serde_core\",\"req\":\"^1.0.220\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.45\"},{\"default_features\":false,\"features\":[\"derive\"],\"name\":\"yoke\",\"optional\":true,\"req\":\"^0.8.0\"}],\"features\":{\"alloc\":[],\"databake\":[\"dep:databake\"],\"default\":[\"alloc\"],\"serde\":[\"dep:serde_core\",\"alloc\"],\"testing\":[\"alloc\"],\"yoke\":[\"dep:yoke\"]}}",
|
||||
"litrs_1.0.0": "{\"dependencies\":[{\"name\":\"proc-macro2\",\"optional\":true,\"req\":\"^1.0.63\"},{\"name\":\"unicode-xid\",\"optional\":true,\"req\":\"^0.2.4\"}],\"features\":{\"check_suffix\":[\"unicode-xid\"]}}",
|
||||
"local-waker_0.1.4": "{\"dependencies\":[],\"features\":{}}",
|
||||
"lock_api_0.4.14": "{\"dependencies\":[{\"name\":\"owning_ref\",\"optional\":true,\"req\":\"^0.4.1\"},{\"default_features\":false,\"name\":\"scopeguard\",\"req\":\"^1.1.0\"},{\"default_features\":false,\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0.126\"}],\"features\":{\"arc_lock\":[],\"atomic_usize\":[],\"default\":[\"atomic_usize\"],\"nightly\":[]}}",
|
||||
"log_0.4.29": "{\"dependencies\":[{\"default_features\":false,\"kind\":\"dev\",\"name\":\"proc-macro2\",\"req\":\"^1.0.63\"},{\"features\":[\"derive\"],\"kind\":\"dev\",\"name\":\"serde\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"serde_core\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_test\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"sval\",\"optional\":true,\"req\":\"^2.16\"},{\"kind\":\"dev\",\"name\":\"sval\",\"req\":\"^2.16\"},{\"kind\":\"dev\",\"name\":\"sval_derive\",\"req\":\"^2.16\"},{\"default_features\":false,\"name\":\"sval_ref\",\"optional\":true,\"req\":\"^2.16\"},{\"default_features\":false,\"features\":[\"inline-i128\"],\"name\":\"value-bag\",\"optional\":true,\"req\":\"^1.12\"},{\"features\":[\"test\"],\"kind\":\"dev\",\"name\":\"value-bag\",\"req\":\"^1.12\"}],\"features\":{\"kv\":[],\"kv_serde\":[\"kv_std\",\"value-bag/serde\",\"serde\"],\"kv_std\":[\"std\",\"kv\",\"value-bag/error\"],\"kv_sval\":[\"kv\",\"value-bag/sval\",\"sval\",\"sval_ref\"],\"kv_unstable\":[\"kv\",\"value-bag\"],\"kv_unstable_serde\":[\"kv_serde\",\"kv_unstable_std\"],\"kv_unstable_std\":[\"kv_std\",\"kv_unstable\"],\"kv_unstable_sval\":[\"kv_sval\",\"kv_unstable\"],\"max_level_debug\":[],\"max_level_error\":[],\"max_level_info\":[],\"max_level_off\":[],\"max_level_trace\":[],\"max_level_warn\":[],\"release_max_level_debug\":[],\"release_max_level_error\":[],\"release_max_level_info\":[],\"release_max_level_off\":[],\"release_max_level_trace\":[],\"release_max_level_warn\":[],\"serde\":[\"serde_core\"],\"std\":[]}}",
|
||||
@@ -1324,6 +1328,7 @@
|
||||
"protoc-gen-tonic_0.4.1": "{\"dependencies\":[{\"name\":\"heck\",\"req\":\"^0.5.0\"},{\"name\":\"prettyplease\",\"req\":\"^0.2.9\"},{\"name\":\"proc-macro2\",\"req\":\"^1.0\"},{\"default_features\":false,\"features\":[\"std\"],\"name\":\"prost\",\"req\":\"^0.13.1\"},{\"default_features\":false,\"name\":\"prost-build\",\"req\":\"^0.13.1\"},{\"default_features\":false,\"name\":\"prost-types\",\"req\":\"^0.13.1\"},{\"name\":\"protoc-gen-prost\",\"req\":\"^0.4.0\"},{\"name\":\"quote\",\"req\":\"^1.0\"},{\"default_features\":false,\"name\":\"regex\",\"req\":\"^1.5.5\"},{\"features\":[\"parsing\",\"full\"],\"name\":\"syn\",\"req\":\"^2.0.22\"},{\"name\":\"tonic-build\",\"req\":\"^0.12.0\"}],\"features\":{}}",
|
||||
"psl-types_2.0.11": "{\"dependencies\":[],\"features\":{}}",
|
||||
"psl_2.1.184": "{\"dependencies\":[{\"name\":\"psl-types\",\"req\":\"^2.0.11\"},{\"kind\":\"dev\",\"name\":\"rspec\",\"req\":\"^1.0.0\"}],\"features\":{\"default\":[\"helpers\"],\"helpers\":[]}}",
|
||||
"publicsuffix_2.3.0": "{\"dependencies\":[{\"features\":[\"inline-more\"],\"name\":\"hashbrown\",\"optional\":true,\"req\":\"^0.15.1\"},{\"name\":\"idna\",\"optional\":true,\"req\":\"^1.0\"},{\"name\":\"psl-types\",\"req\":\"^2.0.11\"},{\"kind\":\"dev\",\"name\":\"rspec\",\"req\":\"^1.0.0\"},{\"default_features\":false,\"name\":\"unicase\",\"optional\":true,\"req\":\"^2.6.0\"}],\"features\":{\"anycase\":[\"unicase\"],\"default\":[\"punycode\"],\"punycode\":[\"idna\"],\"std\":[]}}",
|
||||
"pulldown-cmark-escape_0.10.1": "{\"dependencies\":[],\"features\":{\"simd\":[]}}",
|
||||
"pulldown-cmark_0.10.3": "{\"dependencies\":[{\"kind\":\"dev\",\"name\":\"bincode\",\"req\":\"^1.3.1\"},{\"name\":\"bitflags\",\"req\":\"^2\"},{\"kind\":\"dev\",\"name\":\"criterion\",\"req\":\"^0.5\"},{\"name\":\"getopts\",\"optional\":true,\"req\":\"^0.2\"},{\"kind\":\"dev\",\"name\":\"lazy_static\",\"req\":\"^1.4\"},{\"name\":\"memchr\",\"req\":\"^2.5\"},{\"name\":\"pulldown-cmark-escape\",\"optional\":true,\"req\":\"^0.10.0\"},{\"kind\":\"dev\",\"name\":\"regex\",\"req\":\"^1.6\"},{\"features\":[\"derive\"],\"name\":\"serde\",\"optional\":true,\"req\":\"^1.0\"},{\"kind\":\"dev\",\"name\":\"serde_json\",\"req\":\"^1.0.61\"},{\"name\":\"unicase\",\"req\":\"^2.6\"}],\"features\":{\"default\":[\"getopts\",\"html\"],\"gen-tests\":[],\"html\":[\"pulldown-cmark-escape\"],\"simd\":[\"pulldown-cmark-escape?/simd\"]}}",
|
||||
"pxfm_0.1.27": "{\"dependencies\":[{\"name\":\"num-traits\",\"req\":\"^0.2.3\"}],\"features\":{}}",
|
||||
|
||||
Generated
+56
@@ -3481,6 +3481,17 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie-factory"
|
||||
version = "0.3.3"
|
||||
@@ -3490,6 +3501,24 @@ dependencies = [
|
||||
"futures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie_store"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"document-features",
|
||||
"idna",
|
||||
"log",
|
||||
"publicsuffix",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"time",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
@@ -4344,6 +4373,15 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
|
||||
dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
@@ -7432,6 +7470,12 @@ version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
|
||||
[[package]]
|
||||
name = "livekit-protocol"
|
||||
version = "0.7.1"
|
||||
@@ -9237,6 +9281,16 @@ version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
|
||||
|
||||
[[package]]
|
||||
name = "publicsuffix"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"psl-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pulldown-cmark"
|
||||
version = "0.10.3"
|
||||
@@ -9921,6 +9975,8 @@ checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"cookie",
|
||||
"cookie_store",
|
||||
"encoding_rs",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
||||
+1
-1
@@ -294,7 +294,7 @@ ratatui = "0.29.0"
|
||||
ratatui-macros = "0.6.0"
|
||||
regex = "1.12.3"
|
||||
regex-lite = "0.1.8"
|
||||
reqwest = "0.12"
|
||||
reqwest = { version = "0.12", features = ["cookies"] }
|
||||
rmcp = { version = "0.15.0", default-features = false }
|
||||
runfiles = { git = "https://github.com/dzbarsky/rules_rust", rev = "b56cbaa8465e74127f1ea216f813cd377295ad81" }
|
||||
rustls = { version = "0.23", default-features = false, features = [
|
||||
|
||||
@@ -105,7 +105,7 @@ pub(crate) struct ServerEnvelope {
|
||||
pub(crate) seq_id: u64,
|
||||
}
|
||||
|
||||
fn is_allowed_chatgpt_host(host: &Option<Host<&str>>) -> bool {
|
||||
fn is_allowed_remote_control_chatgpt_host(host: &Option<Host<&str>>) -> bool {
|
||||
let Some(Host::Domain(host)) = *host else {
|
||||
return false;
|
||||
};
|
||||
@@ -156,7 +156,7 @@ pub(super) fn normalize_remote_control_url(
|
||||
.map_err(map_url_parse_error)?;
|
||||
let host = enroll_url.host();
|
||||
match enroll_url.scheme() {
|
||||
"https" if is_localhost(&host) || is_allowed_chatgpt_host(&host) => {
|
||||
"https" if is_localhost(&host) || is_allowed_remote_control_chatgpt_host(&host) => {
|
||||
websocket_url.set_scheme("wss").map_err(map_scheme_error)?;
|
||||
}
|
||||
"http" if is_localhost(&host) => {
|
||||
@@ -232,6 +232,7 @@ mod tests {
|
||||
"http://chatgpt.com/backend-api",
|
||||
"http://example.com/backend-api",
|
||||
"https://example.com/backend-api",
|
||||
"https://chat.openai.com/backend-api",
|
||||
"https://chatgpt.com.evil.com/backend-api",
|
||||
"https://evilchatgpt.com/backend-api",
|
||||
"https://foo.localhost/backend-api",
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::types::RateLimitStatusPayload;
|
||||
use crate::types::TurnAttemptsSiblingTurnsResponse;
|
||||
use anyhow::Result;
|
||||
use codex_client::build_reqwest_client_with_custom_ca;
|
||||
use codex_client::with_chatgpt_cloudflare_cookie_store;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::get_codex_user_agent;
|
||||
use codex_protocol::account::PlanType as AccountPlanType;
|
||||
@@ -137,7 +138,9 @@ impl Client {
|
||||
{
|
||||
base_url = format!("{base_url}/backend-api");
|
||||
}
|
||||
let http = build_reqwest_client_with_custom_ca(reqwest::Client::builder())?;
|
||||
let http = build_reqwest_client_with_custom_ca(with_chatgpt_cloudflare_cookie_store(
|
||||
reqwest::Client::builder(),
|
||||
))?;
|
||||
let path_style = PathStyle::from_base_url(&base_url);
|
||||
Ok(Self {
|
||||
base_url,
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
use std::sync::Arc;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use reqwest::cookie::CookieStore;
|
||||
use reqwest::cookie::Jar;
|
||||
use reqwest::header::HeaderValue;
|
||||
|
||||
use crate::chatgpt_hosts::is_allowed_chatgpt_host;
|
||||
|
||||
// WARNING: this store is process-global and may be shared across auth contexts.
|
||||
// It must only ever contain Cloudflare infrastructure cookies. Never extend this
|
||||
// store to persist ChatGPT account, session, auth, or other user-specific cookie
|
||||
// data.
|
||||
static SHARED_CHATGPT_CLOUDFLARE_COOKIE_STORE: LazyLock<Arc<ChatGptCloudflareCookieStore>> =
|
||||
LazyLock::new(|| Arc::new(ChatGptCloudflareCookieStore::default()));
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ChatGptCloudflareCookieStore {
|
||||
jar: Jar,
|
||||
}
|
||||
|
||||
impl CookieStore for ChatGptCloudflareCookieStore {
|
||||
fn set_cookies(
|
||||
&self,
|
||||
cookie_headers: &mut dyn Iterator<Item = &HeaderValue>,
|
||||
url: &reqwest::Url,
|
||||
) {
|
||||
if !is_chatgpt_cookie_url(url) {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut cloudflare_cookie_headers =
|
||||
cookie_headers.filter(|header| is_allowed_cloudflare_set_cookie_header(header));
|
||||
self.jar.set_cookies(&mut cloudflare_cookie_headers, url);
|
||||
}
|
||||
|
||||
fn cookies(&self, url: &reqwest::Url) -> Option<HeaderValue> {
|
||||
if is_chatgpt_cookie_url(url) {
|
||||
self.jar.cookies(url).and_then(only_cloudflare_cookies)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the process-local ChatGPT Cloudflare cookie jar used by Codex HTTP clients.
|
||||
///
|
||||
/// WARNING: this jar is global within the process. It is only acceptable because it hardcodes a
|
||||
/// small allowlist of Cloudflare cookie names and refuses all other ChatGPT cookies. Do not store
|
||||
/// ChatGPT account, session, auth, or other user-specific cookies here. If a future caller needs
|
||||
/// those cookies, the store must be scoped to the auth/session owner instead of shared globally.
|
||||
pub fn with_chatgpt_cloudflare_cookie_store(
|
||||
builder: reqwest::ClientBuilder,
|
||||
) -> reqwest::ClientBuilder {
|
||||
builder.cookie_provider(Arc::clone(&SHARED_CHATGPT_CLOUDFLARE_COOKIE_STORE))
|
||||
}
|
||||
|
||||
fn is_chatgpt_cookie_url(url: &reqwest::Url) -> bool {
|
||||
match url.scheme() {
|
||||
"https" => {}
|
||||
_ => return false,
|
||||
}
|
||||
|
||||
let Some(host) = url.host_str() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
is_allowed_chatgpt_host(host)
|
||||
}
|
||||
|
||||
fn is_allowed_cloudflare_set_cookie_header(header: &HeaderValue) -> bool {
|
||||
header
|
||||
.to_str()
|
||||
.ok()
|
||||
.and_then(set_cookie_name)
|
||||
.is_some_and(is_allowed_cloudflare_cookie_name)
|
||||
}
|
||||
|
||||
fn set_cookie_name(header: &str) -> Option<&str> {
|
||||
let (name, _) = header.split_once('=')?;
|
||||
let name = name.trim();
|
||||
(!name.is_empty()).then_some(name)
|
||||
}
|
||||
|
||||
fn only_cloudflare_cookies(header: HeaderValue) -> Option<HeaderValue> {
|
||||
let header = header.to_str().ok()?;
|
||||
let cookies = header
|
||||
.split(';')
|
||||
.filter_map(|cookie| {
|
||||
let cookie = cookie.trim();
|
||||
let name = cookie.split_once('=')?.0.trim();
|
||||
is_allowed_cloudflare_cookie_name(name).then_some(cookie)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("; ");
|
||||
|
||||
if cookies.is_empty() {
|
||||
None
|
||||
} else {
|
||||
HeaderValue::from_str(&cookies).ok()
|
||||
}
|
||||
}
|
||||
|
||||
fn is_allowed_cloudflare_cookie_name(name: &str) -> bool {
|
||||
// Keep this allowlist aligned with Cloudflare's documented service cookies:
|
||||
// https://developers.cloudflare.com/fundamentals/reference/policies-compliances/cloudflare-cookies/
|
||||
matches!(
|
||||
name,
|
||||
"__cf_bm"
|
||||
| "__cflb"
|
||||
| "__cfruid"
|
||||
| "__cfseq"
|
||||
| "__cfwaitingroom"
|
||||
| "_cfuvid"
|
||||
| "cf_clearance"
|
||||
| "cf_ob_info"
|
||||
| "cf_use_ob"
|
||||
) || name.starts_with("cf_chl_")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
use reqwest::cookie::CookieStore;
|
||||
|
||||
#[test]
|
||||
fn stores_and_returns_cloudflare_cookies_for_chatgpt_hosts() {
|
||||
let store = ChatGptCloudflareCookieStore::default();
|
||||
let url = reqwest::Url::parse("https://chatgpt.com/backend-api/codex/responses").unwrap();
|
||||
let cfuvid = HeaderValue::from_static("_cfuvid=visitor; Path=/; Secure; HttpOnly");
|
||||
let clearance =
|
||||
HeaderValue::from_static("cf_clearance=clearance; Path=/; Secure; HttpOnly");
|
||||
|
||||
store.set_cookies(&mut [&cfuvid, &clearance].into_iter(), &url);
|
||||
|
||||
let mut cookies = store
|
||||
.cookies(&url)
|
||||
.and_then(|value| value.to_str().ok().map(str::to_string))
|
||||
.map(|header| {
|
||||
header
|
||||
.split("; ")
|
||||
.map(str::to_string)
|
||||
.collect::<Vec<String>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
cookies.sort();
|
||||
assert_eq!(
|
||||
cookies,
|
||||
vec![
|
||||
"_cfuvid=visitor".to_string(),
|
||||
"cf_clearance=clearance".to_string()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_non_chatgpt_cookies() {
|
||||
let store = ChatGptCloudflareCookieStore::default();
|
||||
let url = reqwest::Url::parse("https://api.openai.com/v1/responses").unwrap();
|
||||
let set_cookie = HeaderValue::from_static("_cfuvid=visitor; Path=/; Secure; HttpOnly");
|
||||
|
||||
store.set_cookies(&mut std::iter::once(&set_cookie), &url);
|
||||
|
||||
assert_eq!(store.cookies(&url), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_non_cloudflare_cookies_for_chatgpt_hosts() {
|
||||
let store = ChatGptCloudflareCookieStore::default();
|
||||
let url = reqwest::Url::parse("https://chatgpt.com/backend-api/codex/responses").unwrap();
|
||||
let set_cookie = HeaderValue::from_static(
|
||||
"__Secure-next-auth.session-token=secret; Path=/; Secure; HttpOnly",
|
||||
);
|
||||
|
||||
store.set_cookies(&mut std::iter::once(&set_cookie), &url);
|
||||
|
||||
assert_eq!(store.cookies(&url), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_mixed_non_cloudflare_cookies_for_chatgpt_hosts() {
|
||||
let store = ChatGptCloudflareCookieStore::default();
|
||||
let url = reqwest::Url::parse("https://chatgpt.com/backend-api/codex/responses").unwrap();
|
||||
let cfuvid = HeaderValue::from_static("_cfuvid=visitor; Path=/; Secure; HttpOnly");
|
||||
let account_cookie =
|
||||
HeaderValue::from_static("chatgpt_session=secret; Path=/; Secure; HttpOnly");
|
||||
|
||||
store.set_cookies(&mut [&cfuvid, &account_cookie].into_iter(), &url);
|
||||
|
||||
assert_eq!(
|
||||
store
|
||||
.cookies(&url)
|
||||
.and_then(|value| value.to_str().ok().map(str::to_string)),
|
||||
Some("_cfuvid=visitor".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_return_chatgpt_cloudflare_cookies_for_other_hosts() {
|
||||
let store = ChatGptCloudflareCookieStore::default();
|
||||
let chatgpt_url =
|
||||
reqwest::Url::parse("https://chatgpt.com/backend-api/codex/responses").unwrap();
|
||||
let api_url = reqwest::Url::parse("https://api.openai.com/v1/responses").unwrap();
|
||||
let set_cookie = HeaderValue::from_static("_cfuvid=visitor; Path=/; Secure; HttpOnly");
|
||||
|
||||
store.set_cookies(&mut std::iter::once(&set_cookie), &chatgpt_url);
|
||||
|
||||
assert_eq!(store.cookies(&api_url), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_plain_http_chatgpt_cookie_urls() {
|
||||
let store = ChatGptCloudflareCookieStore::default();
|
||||
let http_url = reqwest::Url::parse("http://chatgpt.com/backend-api/codex/responses")
|
||||
.expect("URL should parse");
|
||||
let https_url = reqwest::Url::parse("https://chatgpt.com/backend-api/codex/responses")
|
||||
.expect("URL should parse");
|
||||
let set_cookie = HeaderValue::from_static("_cfuvid=visitor; Path=/; Secure; HttpOnly");
|
||||
|
||||
store.set_cookies(&mut std::iter::once(&set_cookie), &http_url);
|
||||
|
||||
assert_eq!(store.cookies(&http_url), None);
|
||||
assert_eq!(store.cookies(&https_url), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_allows_https_urls() {
|
||||
let url = reqwest::Url::parse("http://chatgpt.com/backend-api/codex/responses").unwrap();
|
||||
|
||||
assert!(!is_chatgpt_cookie_url(&url));
|
||||
|
||||
let url = reqwest::Url::parse("wss://chatgpt.com/backend-api/codex/responses").unwrap();
|
||||
|
||||
assert!(!is_chatgpt_cookie_url(&url));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allows_only_known_cloudflare_cookie_names() {
|
||||
for name in [
|
||||
"__cf_bm",
|
||||
"__cflb",
|
||||
"__cfruid",
|
||||
"__cfseq",
|
||||
"__cfwaitingroom",
|
||||
"_cfuvid",
|
||||
"cf_clearance",
|
||||
"cf_ob_info",
|
||||
"cf_use_ob",
|
||||
"cf_chl_rc_i",
|
||||
] {
|
||||
assert!(is_allowed_cloudflare_cookie_name(name));
|
||||
}
|
||||
|
||||
for name in [
|
||||
"__Secure-next-auth.session-token",
|
||||
"chatgpt_session",
|
||||
"oai-auth-token",
|
||||
"not_cf_clearance",
|
||||
] {
|
||||
assert!(!is_allowed_cloudflare_cookie_name(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/// Returns whether `host` is one of the ChatGPT hosts Codex is allowed to treat
|
||||
/// as first-party ChatGPT traffic.
|
||||
pub fn is_allowed_chatgpt_host(host: &str) -> bool {
|
||||
const EXACT_HOSTS: &[&str] = &["chatgpt.com", "chat.openai.com", "chatgpt-staging.com"];
|
||||
const SUBDOMAIN_SUFFIXES: &[&str] = &[".chatgpt.com", ".chatgpt-staging.com"];
|
||||
|
||||
EXACT_HOSTS.contains(&host)
|
||||
|| SUBDOMAIN_SUFFIXES
|
||||
.iter()
|
||||
.any(|suffix| host.ends_with(suffix))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn recognizes_chatgpt_hosts_without_suffix_tricks() {
|
||||
for host in [
|
||||
"chatgpt.com",
|
||||
"foo.chatgpt.com",
|
||||
"staging.chatgpt.com",
|
||||
"chat.openai.com",
|
||||
"chatgpt-staging.com",
|
||||
"api.chatgpt-staging.com",
|
||||
] {
|
||||
assert!(is_allowed_chatgpt_host(host));
|
||||
}
|
||||
|
||||
for host in [
|
||||
"evilchatgpt.com",
|
||||
"chatgpt.com.evil.example",
|
||||
"api.openai.com",
|
||||
"foo.chat.openai.com",
|
||||
] {
|
||||
assert!(!is_allowed_chatgpt_host(host));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
mod chatgpt_cloudflare_cookies;
|
||||
mod chatgpt_hosts;
|
||||
mod custom_ca;
|
||||
mod default_client;
|
||||
mod error;
|
||||
@@ -7,6 +9,8 @@ mod sse;
|
||||
mod telemetry;
|
||||
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::custom_ca::BuildCustomCaTransportError;
|
||||
/// Test-only subprocess hook for custom CA coverage.
|
||||
///
|
||||
|
||||
@@ -8,6 +8,7 @@ use codex_client::BuildCustomCaTransportError;
|
||||
use codex_client::CodexHttpClient;
|
||||
pub use codex_client::CodexRequestBuilder;
|
||||
use codex_client::build_reqwest_client_with_custom_ca;
|
||||
use codex_client::with_chatgpt_cloudflare_cookie_store;
|
||||
use codex_terminal_detection::user_agent;
|
||||
use reqwest::header::HeaderMap;
|
||||
use reqwest::header::HeaderValue;
|
||||
@@ -201,7 +202,15 @@ pub fn create_client() -> CodexHttpClient {
|
||||
pub fn build_reqwest_client() -> reqwest::Client {
|
||||
try_build_reqwest_client().unwrap_or_else(|error| {
|
||||
tracing::warn!(error = %error, "failed to build default reqwest client");
|
||||
reqwest::Client::new()
|
||||
with_chatgpt_cloudflare_cookie_store(reqwest::Client::builder())
|
||||
.build()
|
||||
.unwrap_or_else(|fallback_error| {
|
||||
tracing::warn!(
|
||||
error = %fallback_error,
|
||||
"failed to build fallback reqwest client with ChatGPT Cloudflare cookie store"
|
||||
);
|
||||
reqwest::Client::new()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -219,6 +228,7 @@ pub fn try_build_reqwest_client() -> Result<reqwest::Client, BuildCustomCaTransp
|
||||
if is_sandboxed() {
|
||||
builder = builder.no_proxy();
|
||||
}
|
||||
builder = with_chatgpt_cloudflare_cookie_store(builder);
|
||||
|
||||
build_reqwest_client_with_custom_ca(builder)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user