9329 lines · 367.5 KB · Go
RawDownload
package executor import (	"bytes"	"compress/gzip"	"context"	"encoding/base64"	"encoding/json"	"errors"	"fmt"	"io"	"net/http"	"net/http/httptest"	"strings"	"sync"	"testing"	"time" 	"github.com/andybalholm/brotli"	"github.com/gin-gonic/gin"	"github.com/klauspost/compress/zstd"	claudeauth "github.com/router-for-me/CLIProxyAPI/v7/internal/auth/claude"	"github.com/router-for-me/CLIProxyAPI/v7/internal/config"	"github.com/router-for-me/CLIProxyAPI/v7/internal/registry"	"github.com/router-for-me/CLIProxyAPI/v7/internal/runtime/executor/helps"	cliproxyauth "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/auth"	cliproxyexecutor "github.com/router-for-me/CLIProxyAPI/v7/sdk/cliproxy/executor"	sdktranslator "github.com/router-for-me/CLIProxyAPI/v7/sdk/translator"	"github.com/tidwall/gjson"	"github.com/tidwall/sjson") func resetClaudeDeviceProfileCache() {	helps.ResetClaudeDeviceProfileCache()} func claudeOAuthTestMetadata() map[string]any {	return map[string]any{		"account_uuid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",		claudeauth.ClaudeDeviceIDsMetadataKey: []string{			"0000000000000000000000000000000000000000000000000000000000000000",		},	}} func malformedClaudeTreeSignatureForClaudeExecutorTest() string {	return base64.StdEncoding.EncodeToString([]byte{0x12, 0xFF, 0xFE, 0xFD})} func newClaudeHeaderTestRequest(t *testing.T, incoming http.Header) *http.Request {	t.Helper() 	gin.SetMode(gin.TestMode)	recorder := httptest.NewRecorder()	ginCtx, _ := gin.CreateTestContext(recorder)	ginReq := httptest.NewRequest(http.MethodPost, "http://localhost/v1/messages", nil)	ginReq.Header = incoming.Clone()	ginCtx.Request = ginReq 	req := httptest.NewRequest(http.MethodPost, "https://api.anthropic.com/v1/messages", nil)	return req.WithContext(context.WithValue(req.Context(), "gin", ginCtx))} func assertClaudeFingerprint(t *testing.T, headers http.Header, userAgent, pkgVersion, runtimeVersion, osName, arch string) {	t.Helper() 	if got := headers.Get("User-Agent"); got != userAgent {		t.Fatalf("User-Agent = %q, want %q", got, userAgent)	}	if got := headers.Get("X-Stainless-Package-Version"); got != pkgVersion {		t.Fatalf("X-Stainless-Package-Version = %q, want %q", got, pkgVersion)	}	if got := headers.Get("X-Stainless-Runtime-Version"); got != runtimeVersion {		t.Fatalf("X-Stainless-Runtime-Version = %q, want %q", got, runtimeVersion)	}	if got := headers.Get("X-Stainless-Os"); got != osName {		t.Fatalf("X-Stainless-Os = %q, want %q", got, osName)	}	if got := headers.Get("X-Stainless-Arch"); got != arch {		t.Fatalf("X-Stainless-Arch = %q, want %q", got, arch)	}} func TestApplyClaudeHeaders_FastModeBetaIsConditional(t *testing.T) {	baseline := claudeCodeCLIBetas([]byte(`{"model":"claude-opus-5"}`), nil, false)	betasWithoutFastMode := baseline	betasWithFastMode := baseline + "," + claudeFastModeBeta 	tests := []struct {		name string		body string		want string	}{		{			name: "omitted speed excludes fast mode beta",			body: `{"model":"claude-opus-5"}`,			want: betasWithoutFastMode,		},		{			name: "fast speed appends fast mode beta",			body: `{"model":"claude-opus-5","speed":"fast"}`,			want: betasWithFastMode,		},		{			name: "explicit body beta appends fast mode beta",			body: `{"model":"claude-opus-5","betas":["fast-mode-2026-02-01"]}`,			want: betasWithFastMode,		},	} 	auth := &cliproxyauth.Auth{Attributes: map[string]string{"api_key": "key-fast-mode-beta", "cloak_mode": "always"}}	for _, tt := range tests {		t.Run(tt.name, func(t *testing.T) {			extraBetas, body := extractAndRemoveBetas([]byte(tt.body))			req := newClaudeHeaderTestRequest(t, nil)			if errApply := applyClaudeHeaders(req, auth, "key-fast-mode-beta", false, extraBetas, body, nil, nil, false); errApply != nil {				t.Fatalf("applyClaudeHeaders() error = %v", errApply)			}			if got := req.Header.Get("Anthropic-Beta"); got != tt.want {				t.Fatalf("Anthropic-Beta = %q, want %q", got, tt.want)			}		})	}} func assertClaudeCredentialIdentity(t *testing.T, body []byte, headers http.Header, deviceIDs []string, accountUUID string) {	t.Helper()	userID := gjson.GetBytes(body, "metadata.user_id").String()	deviceID := gjson.Get(userID, "device_id").String()	inPool := false	for _, candidate := range deviceIDs {		if deviceID == candidate {			inPool = true			break		}	}	if !inPool {		t.Fatalf("device_id = %q, want selected credential device pool entry", deviceID)	}	if got := gjson.Get(userID, "account_uuid").String(); got != accountUUID {		t.Fatalf("account_uuid = %q, want selected credential account %q", got, accountUUID)	}	sessionID := gjson.Get(userID, "session_id").String()	if sessionID == "" || sessionID != headers.Get("X-Claude-Code-Session-Id") {		t.Fatalf("metadata session_id = %q, header session ID = %q", sessionID, headers.Get("X-Claude-Code-Session-Id"))	}	resigned, errResign := finalizeAnthropicMessagesBodyCCH(body, "")	if errResign != nil {		t.Fatalf("re-finalize Claude CCH: %v", errResign)	}	if !bytes.Equal(resigned, body) {		t.Fatal("Claude CCH was calculated before final credential metadata rewrite")	}} // assertClaudeCountTokensIdentity pins the count_tokens shape captured from real// Claude Code 2.1.220: the endpoint carries no metadata whatsoever. Anthropic// rejects the field there with "metadata: Extra inputs are not permitted", so the// credential identity travels only on the header and on the Messages endpoint.func assertClaudeCountTokensIdentity(t *testing.T, body []byte, headers http.Header) {	t.Helper()	if got := gjson.GetBytes(body, "metadata"); got.Exists() {		t.Fatalf("count_tokens metadata = %s, want it absent", got.Raw)	}	if got := headers.Get("X-Claude-Code-Session-Id"); got == "" {		t.Fatal("count_tokens is missing X-Claude-Code-Session-Id")	}	resigned, errResign := finalizeAnthropicMessagesBodyCCH(body, "")	if errResign != nil {		t.Fatalf("re-finalize Claude CCH: %v", errResign)	}	if !bytes.Equal(resigned, body) {		t.Fatal("count_tokens CCH was calculated before the final body rewrite")	}} func TestApplyClaudeHeaders_UsesConfiguredBaselineFingerprint(t *testing.T) {	resetClaudeDeviceProfileCache()	stabilize := true 	cfg := &config.Config{		ClaudeHeaderDefaults: config.ClaudeHeaderDefaults{			UserAgent:              "claude-cli/2.1.70 (external, cli)",			PackageVersion:         "0.80.0",			RuntimeVersion:         "v24.5.0",			OS:                     "MacOS",			Arch:                   "arm64",			Timeout:                "900",			StabilizeDeviceProfile: &stabilize,		},	}	auth := &cliproxyauth.Auth{		ID: "auth-baseline",		Attributes: map[string]string{			"api_key":                            "key-baseline",			"cloak_mode":                         "always",			"header:User-Agent":                  "evil-client/9.9",			"header:X-Stainless-Os":              "Linux",			"header:X-Stainless-Arch":            "x64",			"header:X-Stainless-Package-Version": "9.9.9",		},	}	incoming := http.Header{		"User-Agent":                  []string{"curl/8.7.1"},		"X-Stainless-Package-Version": []string{"0.10.0"},		"X-Stainless-Runtime-Version": []string{"v18.0.0"},		"X-Stainless-Os":              []string{"Linux"},		"X-Stainless-Arch":            []string{"x64"},	} 	req := newClaudeHeaderTestRequest(t, incoming)	applyClaudeHeaders(req, auth, "key-baseline", false, nil, nil, cfg, nil, false) 	assertClaudeFingerprint(t, req.Header, "evil-client/9.9", "9.9.9", "v24.5.0", "Linux", "x64")	if got := req.Header.Get("X-Stainless-Timeout"); got != "900" {		t.Fatalf("X-Stainless-Timeout = %q, want %q", got, "900")	}} func TestApplyClaudeHeaders_RejectsUnmeasuredClaudeCLIFingerprints(t *testing.T) {	resetClaudeDeviceProfileCache()	stabilize := true 	cfg := &config.Config{		ClaudeHeaderDefaults: config.ClaudeHeaderDefaults{			UserAgent:              "claude-cli/2.1.60 (external, cli)",			PackageVersion:         "0.70.0",			RuntimeVersion:         "v22.0.0",			OS:                     "MacOS",			Arch:                   "arm64",			StabilizeDeviceProfile: &stabilize,		},	}	auth := &cliproxyauth.Auth{		ID: "auth-upgrade",		Attributes: map[string]string{			"api_key":    "key-upgrade",			"cloak_mode": "always",		},	} 	firstReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"claude-cli/2.1.62 (external, cli)"},		"X-Stainless-Package-Version": []string{"0.74.0"},		"X-Stainless-Runtime-Version": []string{"v24.3.0"},		"X-Stainless-Os":              []string{"Linux"},		"X-Stainless-Arch":            []string{"x64"},	})	applyClaudeHeaders(firstReq, auth, "key-upgrade", false, nil, nil, cfg, nil, true)	assertClaudeFingerprint(t, firstReq.Header, "claude-cli/2.1.60 (external, cli)", "0.70.0", "v22.0.0", "MacOS", "arm64") 	thirdPartyReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"lobe-chat/1.0"},		"X-Stainless-Package-Version": []string{"0.10.0"},		"X-Stainless-Runtime-Version": []string{"v18.0.0"},		"X-Stainless-Os":              []string{"Windows"},		"X-Stainless-Arch":            []string{"x64"},	})	applyClaudeHeaders(thirdPartyReq, auth, "key-upgrade", false, nil, nil, cfg, nil, false)	assertClaudeFingerprint(t, thirdPartyReq.Header, "claude-cli/2.1.60 (external, cli)", "0.70.0", "v22.0.0", "MacOS", "arm64") 	higherReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"claude-cli/2.1.63 (external, cli)"},		"X-Stainless-Package-Version": []string{"0.75.0"},		"X-Stainless-Runtime-Version": []string{"v24.4.0"},		"X-Stainless-Os":              []string{"MacOS"},		"X-Stainless-Arch":            []string{"arm64"},	})	applyClaudeHeaders(higherReq, auth, "key-upgrade", false, nil, nil, cfg, nil, true)	assertClaudeFingerprint(t, higherReq.Header, "claude-cli/2.1.60 (external, cli)", "0.70.0", "v22.0.0", "MacOS", "arm64") 	lowerReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"claude-cli/2.1.61 (external, cli)"},		"X-Stainless-Package-Version": []string{"0.73.0"},		"X-Stainless-Runtime-Version": []string{"v24.2.0"},		"X-Stainless-Os":              []string{"Windows"},		"X-Stainless-Arch":            []string{"x64"},	})	applyClaudeHeaders(lowerReq, auth, "key-upgrade", false, nil, nil, cfg, nil, true)	assertClaudeFingerprint(t, lowerReq.Header, "claude-cli/2.1.60 (external, cli)", "0.70.0", "v22.0.0", "MacOS", "arm64")} func TestApplyClaudeHeaders_DoesNotDowngradeConfiguredBaselineOnFirstClaudeClient(t *testing.T) {	resetClaudeDeviceProfileCache()	stabilize := true 	cfg := &config.Config{		ClaudeHeaderDefaults: config.ClaudeHeaderDefaults{			UserAgent:              "claude-cli/2.1.70 (external, cli)",			PackageVersion:         "0.80.0",			RuntimeVersion:         "v24.5.0",			OS:                     "MacOS",			Arch:                   "arm64",			StabilizeDeviceProfile: &stabilize,		},	}	auth := &cliproxyauth.Auth{		ID: "auth-baseline-floor",		Attributes: map[string]string{			"api_key": "key-baseline-floor",		},	} 	olderClaudeReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"claude-cli/2.1.62 (external, cli)"},		"X-Stainless-Package-Version": []string{"0.74.0"},		"X-Stainless-Runtime-Version": []string{"v24.3.0"},		"X-Stainless-Os":              []string{"Linux"},		"X-Stainless-Arch":            []string{"x64"},	})	applyClaudeHeaders(olderClaudeReq, auth, "key-baseline-floor", false, nil, nil, cfg, nil, true)	assertClaudeFingerprint(t, olderClaudeReq.Header, "claude-cli/2.1.70 (external, cli)", "0.80.0", "v24.5.0", "MacOS", "arm64") 	newerClaudeReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"claude-cli/2.1.71 (external, cli)"},		"X-Stainless-Package-Version": []string{"0.81.0"},		"X-Stainless-Runtime-Version": []string{"v24.6.0"},		"X-Stainless-Os":              []string{"Linux"},		"X-Stainless-Arch":            []string{"x64"},	})	applyClaudeHeaders(newerClaudeReq, auth, "key-baseline-floor", false, nil, nil, cfg, nil, true)	assertClaudeFingerprint(t, newerClaudeReq.Header, "claude-cli/2.1.70 (external, cli)", "0.80.0", "v24.5.0", "MacOS", "arm64")} func TestApplyClaudeHeaders_UpgradesCachedSoftwareFingerprintWhenBaselineAdvances(t *testing.T) {	resetClaudeDeviceProfileCache()	stabilize := true 	oldCfg := &config.Config{		ClaudeHeaderDefaults: config.ClaudeHeaderDefaults{			UserAgent:              "claude-cli/2.1.70 (external, cli)",			PackageVersion:         "0.80.0",			RuntimeVersion:         "v24.5.0",			OS:                     "MacOS",			Arch:                   "arm64",			StabilizeDeviceProfile: &stabilize,		},	}	newCfg := &config.Config{		ClaudeHeaderDefaults: config.ClaudeHeaderDefaults{			UserAgent:              "claude-cli/2.1.77 (external, cli)",			PackageVersion:         "0.87.0",			RuntimeVersion:         "v24.8.0",			OS:                     "MacOS",			Arch:                   "arm64",			StabilizeDeviceProfile: &stabilize,		},	}	auth := &cliproxyauth.Auth{		ID: "auth-baseline-reload",		Attributes: map[string]string{			"api_key":    "key-baseline-reload",			"cloak_mode": "always",		},	} 	officialReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"claude-cli/2.1.71 (external, cli)"},		"X-Stainless-Package-Version": []string{"0.81.0"},		"X-Stainless-Runtime-Version": []string{"v24.6.0"},		"X-Stainless-Os":              []string{"Linux"},		"X-Stainless-Arch":            []string{"x64"},	})	applyClaudeHeaders(officialReq, auth, "key-baseline-reload", false, nil, nil, oldCfg, nil, true)	assertClaudeFingerprint(t, officialReq.Header, "claude-cli/2.1.70 (external, cli)", "0.80.0", "v24.5.0", "MacOS", "arm64") 	thirdPartyReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"curl/8.7.1"},		"X-Stainless-Package-Version": []string{"0.10.0"},		"X-Stainless-Runtime-Version": []string{"v18.0.0"},		"X-Stainless-Os":              []string{"Linux"},		"X-Stainless-Arch":            []string{"x64"},	})	applyClaudeHeaders(thirdPartyReq, auth, "key-baseline-reload", false, nil, nil, newCfg, nil, false)	assertClaudeFingerprint(t, thirdPartyReq.Header, "claude-cli/2.1.77 (external, cli)", "0.87.0", "v24.8.0", "MacOS", "arm64")} func TestApplyClaudeHeaders_LearnsOfficialFingerprintAfterCustomBaselineFallback(t *testing.T) {	resetClaudeDeviceProfileCache()	stabilize := true 	cfg := &config.Config{		ClaudeHeaderDefaults: config.ClaudeHeaderDefaults{			UserAgent:              "my-gateway/1.0",			PackageVersion:         "custom-pkg",			RuntimeVersion:         "custom-runtime",			OS:                     "MacOS",			Arch:                   "arm64",			StabilizeDeviceProfile: &stabilize,		},	}	auth := &cliproxyauth.Auth{		ID: "auth-custom-baseline-learning",		Attributes: map[string]string{			"api_key":    "key-custom-baseline-learning",			"cloak_mode": "always",		},	} 	thirdPartyReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"curl/8.7.1"},		"X-Stainless-Package-Version": []string{"0.10.0"},		"X-Stainless-Runtime-Version": []string{"v18.0.0"},		"X-Stainless-Os":              []string{"Linux"},		"X-Stainless-Arch":            []string{"x64"},	})	applyClaudeHeaders(thirdPartyReq, auth, "key-custom-baseline-learning", false, nil, nil, cfg, nil, false)	assertClaudeFingerprint(t, thirdPartyReq.Header, "my-gateway/1.0", "custom-pkg", "custom-runtime", "MacOS", "arm64") 	officialReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"claude-cli/2.1.77 (external, cli)"},		"X-Stainless-Package-Version": []string{"0.87.0"},		"X-Stainless-Runtime-Version": []string{"v24.8.0"},		"X-Stainless-Os":              []string{"Linux"},		"X-Stainless-Arch":            []string{"x64"},	})	applyClaudeHeaders(officialReq, auth, "key-custom-baseline-learning", false, nil, nil, cfg, nil, true)	assertClaudeFingerprint(t, officialReq.Header, "my-gateway/1.0", "custom-pkg", "custom-runtime", "MacOS", "arm64") 	postLearningThirdPartyReq := newClaudeHeaderTestRequest(t, http.Header{		"User-Agent":                  []string{"curl/8.7.1"},		"X-Stainless-Package-Version": []string{"0.10.0"},		"X-Stainless-Runtime-Version": []string{"v18.0.0"},		"X-Stainless-Os":              []string{"Linux"},		"X-Stainless-Arch":            []string{"x64"},	})	applyClaudeHeaders(postLearningThirdPartyReq, auth, "key-custom-baseline-learning", false, nil, nil, cfg, nil, false)	assertClaudeFingerprint(t, postLearningThirdPartyReq.Header, "my-gateway/1.0", "custom-pkg", "custom-runtime", "MacOS", "arm64")} func TestResolveClaudeDeviceProfile_RechecksCacheBeforeStoringCandidate(t *testing.T) {	resetClaudeDeviceProfileCache()	stabilize := true 	cfg := &config.Config{		ClaudeHeaderDefaults: config.ClaudeHeaderDefaults{			UserAgent:              "claude-cli/2.1.60 (external, cli)",			PackageVersion:         "0.70.0",			RuntimeVersion:         "v22.0.0",			OS:                     "MacOS",			Arch:                   "arm64",			StabilizeDeviceProfile: &stabilize,		},	}	auth := &cliproxyauth.Auth{		ID: "auth-racy-upgrade",		Attributes: map[string]string{			"api_key": "key-racy-upgrade",		},	} 	lowPaused := make(chan struct{})	releaseLow := make(chan struct{})	var pauseOnce sync.Once	var releaseOnce sync.Once 	helps.ClaudeDeviceProfileBeforeCandidateStore = func(candidate helps.ClaudeDeviceProfile) {		if candidate.UserAgent != "claude-cli/2.1.60 (external, cli)" {			return		}		pause := false		pauseOnce.Do(func() {			pause = true			close(lowPaused)		})		if pause {			<-releaseLow		}	}	t.Cleanup(func() {		helps.ClaudeDeviceProfileBeforeCandidateStore = nil		releaseOnce.Do(func() { close(releaseLow) })	}) 	lowResultCh := make(chan helps.ClaudeDeviceProfile, 1)	go func() {		lowResultCh <- helps.ResolveClaudeDeviceProfile(auth, "key-racy-upgrade", http.Header{			"User-Agent":                  []string{"claude-cli/2.1.60 (external, cli)"},			"X-Stainless-Package-Version": []string{"0.70.0"},			"X-Stainless-Runtime-Version": []string{"v22.0.0"},			"X-Stainless-Os":              []string{"Linux"},			"X-Stainless-Arch":            []string{"x64"},		}, cfg)	}() 	select {	case <-lowPaused:	case <-time.After(2 * time.Second):		t.Fatal("timed out waiting for lower candidate to pause before storing")	} 	highResult := helps.ResolveClaudeDeviceProfile(auth, "key-racy-upgrade", http.Header{		"User-Agent":                  []string{"claude-cli/2.1.60 (external, cli)"},		"X-Stainless-Package-Version": []string{"0.70.0"},		"X-Stainless-Runtime-Version": []string{"v22.0.0"},		"X-Stainless-Os":              []string{"MacOS"},		"X-Stainless-Arch":            []string{"arm64"},	}, cfg)	releaseOnce.Do(func() { close(releaseLow) }) 

Lines 1–500 of 9329.