fix(gateway): skip body mimicry for real Claude Code clients to restore prompt caching
PR #1914 unconditionally applied the full mimicry pipeline to all OAuth accounts, including real Claude Code CLI clients. This replaced the client's long system prompt (~10K+ tokens with stable cache_control breakpoints) with a short ~45 token [billing, CC prompt] pair, which falls below Anthropic's 1024-token minimum cacheable prefix threshold. The result: every request created a new cache but never hit an existing one. Fix: restore the Claude Code client detection gate so that real CC clients bypass body-level mimicry (system rewrite, message cache management, tool name obfuscation). Non-CC third-party clients (opencode, etc.) continue to receive full mimicry. Also harden the detection logic: - Make UA regex case-insensitive (align with claude_code_validator.go) - Validate metadata.user_id format via ParseMetadataUserID() instead of just checking non-empty, preventing third-party tools from spoofing a claude-cli/* UA with an arbitrary user_id string to bypass mimicry
This commit is contained in:
@@ -9,6 +9,11 @@ import (
|
||||
)
|
||||
|
||||
func TestIsClaudeCodeClient(t *testing.T) {
|
||||
// 合法的 legacy 格式 metadata.user_id(64位 hex + account uuid + session uuid)
|
||||
legacyUserID := "user_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2_account_550e8400-e29b-41d4-a716-446655440000_session_123e4567-e89b-12d3-a456-426614174000"
|
||||
// 合法的 JSON 格式 metadata.user_id(2.1.78+ 版本)
|
||||
jsonUserID := `{"device_id":"a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2","account_uuid":"550e8400-e29b-41d4-a716-446655440000","session_id":"123e4567-e89b-12d3-a456-426614174000"}`
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
userAgent string
|
||||
@@ -16,15 +21,21 @@ func TestIsClaudeCodeClient(t *testing.T) {
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "Claude Code client",
|
||||
name: "Claude Code client with legacy user_id",
|
||||
userAgent: "claude-cli/1.0.62 (darwin; arm64)",
|
||||
metadataUserID: "session_123e4567-e89b-12d3-a456-426614174000",
|
||||
metadataUserID: legacyUserID,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Claude Code without version suffix",
|
||||
userAgent: "claude-cli/2.0.0",
|
||||
metadataUserID: "session_abc",
|
||||
name: "Claude Code client with JSON user_id",
|
||||
userAgent: "claude-cli/2.1.92 (external, cli)",
|
||||
metadataUserID: jsonUserID,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "Claude Code case insensitive UA",
|
||||
userAgent: "Claude-CLI/2.0.0",
|
||||
metadataUserID: legacyUserID,
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
@@ -34,21 +45,33 @@ func TestIsClaudeCodeClient(t *testing.T) {
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Different user agent",
|
||||
name: "Claude CLI UA with invalid user_id format",
|
||||
userAgent: "claude-cli/2.0.0",
|
||||
metadataUserID: "fake-user-id-12345",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Different user agent with valid user_id",
|
||||
userAgent: "curl/7.68.0",
|
||||
metadataUserID: "user123",
|
||||
metadataUserID: legacyUserID,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Empty user agent",
|
||||
userAgent: "",
|
||||
metadataUserID: "user123",
|
||||
metadataUserID: legacyUserID,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Similar but not Claude CLI",
|
||||
userAgent: "claude-api/1.0.0",
|
||||
metadataUserID: "user123",
|
||||
metadataUserID: legacyUserID,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "Opencode spoofing UA with arbitrary user_id",
|
||||
userAgent: "claude-cli/2.1.92",
|
||||
metadataUserID: "session_abc",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user