fix(codex): align websocket cloaking headers

This commit is contained in:
sususu
2026-08-01 13:01:33 +08:00
parent 08eb05ae87
commit 91561df0b1
5 changed files with 73 additions and 7 deletions

View File

@@ -220,7 +220,7 @@ codex:
# Some superstitious users believe request tracking identifiers can be used
# as evidence for TOS enforcement bans; this option only satisfies those odd concerns.
identity-confuse: false
# Disable forcing the official Codex User-Agent and Originator headers on HTTP requests.
# Disable forcing the official Codex User-Agent and Originator headers on HTTP/SSE and WebSocket requests.
disable-codex-cloaking: false
# When true, optimize Codex Desktop and codex-tui requests for multi-agent v2.
# This refreshes Codex spawn_agent model details, removes message parameter encryption,

View File

@@ -127,7 +127,7 @@ type XAIConfig struct {
// CodexConfig configures provider-wide Codex request behavior.
type CodexConfig struct {
IdentityConfuse bool `yaml:"identity-confuse" json:"identity-confuse"`
// DisableCodexCloaking disables forcing the official Codex identity headers on HTTP requests.
// DisableCodexCloaking disables forcing the official Codex identity headers on HTTP/SSE and WebSocket requests.
DisableCodexCloaking bool `yaml:"disable-codex-cloaking" json:"disable-codex-cloaking"`
// OptimizeMultiAgentV2 optimizes official Codex multi-agent requests.
OptimizeMultiAgentV2 bool `yaml:"optimize-multi-agent-v2" json:"optimize-multi-agent-v2"`

View File

@@ -352,10 +352,15 @@ func applyCodexHeadersFromSources(r *http.Request, auth *cliproxyauth.Auth, toke
attrs = auth.Attributes
}
util.ApplyCustomHeadersFromAttrs(r, attrs)
if cfg != nil && !cfg.Codex.DisableCodexCloaking {
r.Header.Set("User-Agent", codexUserAgent)
r.Header.Set("Originator", codexOriginator)
applyCodexCloakingHeaders(r.Header, cfg)
}
func applyCodexCloakingHeaders(headers http.Header, cfg *config.Config) {
if headers == nil || cfg == nil || cfg.Codex.DisableCodexCloaking {
return
}
headers.Set("User-Agent", codexUserAgent)
headers.Set("Originator", codexOriginator)
}
func normalizeCodexInstructions(body []byte) []byte {

View File

@@ -997,7 +997,63 @@ func TestApplyCodexWebsocketHeadersDefaultsToCurrentResponsesBeta(t *testing.T)
}
}
func TestApplyCodexWebsocketHeadersPassesThroughClientIdentityHeaders(t *testing.T) {
func TestApplyCodexWebsocketHeadersDefaultsToCodexCloaking(t *testing.T) {
tests := []struct {
name string
auth *cliproxyauth.Auth
token string
}{
{
name: "OAuth",
auth: &cliproxyauth.Auth{
Provider: "codex",
Attributes: map[string]string{
"header:User-Agent": "custom-ua",
"header:Originator": "custom-origin",
},
},
},
{
name: "API key",
auth: &cliproxyauth.Auth{
Provider: "codex",
Attributes: map[string]string{
"api_key": "sk-test",
"header:User-Agent": "custom-ua",
"header:Originator": "custom-origin",
},
},
token: "sk-test",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := &config.Config{
CodexHeaderDefaults: config.CodexHeaderDefaults{UserAgent: "config-ua"},
}
ctx := contextWithGinHeaders(map[string]string{
"User-Agent": "client-ua",
"Originator": "client-origin",
})
headers := http.Header{}
headers.Set("User-Agent", "existing-ua")
headers.Set("Originator", "existing-origin")
headers = applyCodexWebsocketHeaders(ctx, headers, tt.auth, tt.token, cfg)
if got := headers.Get("User-Agent"); got != codexUserAgent {
t.Fatalf("User-Agent = %q, want %q", got, codexUserAgent)
}
if got := headers.Get("Originator"); got != codexOriginator {
t.Fatalf("Originator = %q, want %q", got, codexOriginator)
}
})
}
}
func TestApplyCodexWebsocketHeadersPassesThroughClientIdentityHeadersWhenCloakingDisabled(t *testing.T) {
cfg := &config.Config{Codex: config.CodexConfig{DisableCodexCloaking: true}}
auth := &cliproxyauth.Auth{
Provider: "codex",
Metadata: map[string]any{"email": "user@example.com"},
@@ -1011,7 +1067,7 @@ func TestApplyCodexWebsocketHeadersPassesThroughClientIdentityHeaders(t *testing
"session-id": "legacy-session",
})
headers := applyCodexWebsocketHeaders(ctx, http.Header{}, auth, "", nil)
headers := applyCodexWebsocketHeaders(ctx, http.Header{}, auth, "", cfg)
if got := headers.Get("Originator"); got != "Codex Desktop" {
t.Fatalf("Originator = %s, want %s", got, "Codex Desktop")
@@ -1059,6 +1115,7 @@ func TestApplyCodexWebsocketHeadersCanonicalizesLegacyUnderscoreSessionHeader(t
func TestApplyCodexWebsocketHeadersUsesConfigDefaultsForOAuth(t *testing.T) {
cfg := &config.Config{
Codex: config.CodexConfig{DisableCodexCloaking: true},
CodexHeaderDefaults: config.CodexHeaderDefaults{
UserAgent: "my-codex-client/1.0",
BetaFeatures: "feature-a,feature-b",
@@ -1084,6 +1141,7 @@ func TestApplyCodexWebsocketHeadersUsesConfigDefaultsForOAuth(t *testing.T) {
func TestApplyCodexWebsocketHeadersPrefersExistingHeadersOverClientAndConfig(t *testing.T) {
cfg := &config.Config{
Codex: config.CodexConfig{DisableCodexCloaking: true},
CodexHeaderDefaults: config.CodexHeaderDefaults{
UserAgent: "config-ua",
BetaFeatures: "config-beta",
@@ -1113,6 +1171,7 @@ func TestApplyCodexWebsocketHeadersPrefersExistingHeadersOverClientAndConfig(t *
func TestApplyCodexWebsocketHeadersConfigUserAgentOverridesClientHeader(t *testing.T) {
cfg := &config.Config{
Codex: config.CodexConfig{DisableCodexCloaking: true},
CodexHeaderDefaults: config.CodexHeaderDefaults{
UserAgent: "config-ua",
BetaFeatures: "config-beta",
@@ -1139,6 +1198,7 @@ func TestApplyCodexWebsocketHeadersConfigUserAgentOverridesClientHeader(t *testi
func TestApplyCodexWebsocketHeadersIgnoresConfigForAPIKeyAuth(t *testing.T) {
cfg := &config.Config{
Codex: config.CodexConfig{DisableCodexCloaking: true},
CodexHeaderDefaults: config.CodexHeaderDefaults{
UserAgent: "config-ua",
BetaFeatures: "config-beta",

View File

@@ -124,6 +124,7 @@ func applyCodexWebsocketHeaders(ctx context.Context, headers http.Header, auth *
attrs = auth.Attributes
}
util.ApplyCustomHeadersFromAttrs(&http.Request{Header: headers}, attrs)
applyCodexCloakingHeaders(headers, cfg)
return headers
}