mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-21 14:19:18 +08:00
fix(gateway): auto mode recognize Claude Code IDE clients via any cc_entrypoint
This commit is contained in:
@@ -53,8 +53,10 @@ const (
|
||||
// 格式固定、不随提示词改版漂移,是比身份 prose 更稳定的客户端标识。
|
||||
// 生成见 gateway_billing_block.go;同类识别见 pkg/apicompat/anthropic_to_responses.go。
|
||||
claudeCodeBillingHeaderPrefix = "x-anthropic-billing-header"
|
||||
// claudeCodeCLIEntrypointMarker 标识请求来自 Claude Code CLI 入口。
|
||||
claudeCodeCLIEntrypointMarker = "cc_entrypoint=cli"
|
||||
// claudeCodeEntrypointMarker 标识计费块携带入口归因字段。不绑定具体入口值
|
||||
// (cli / claude-vscode / jetbrains / sdk 等都是真实入口):入口值会随新增 IDE 漂移,
|
||||
// 且伪造者同样可填任意值、不构成防伪边界,故仅要求该字段存在即可。
|
||||
claudeCodeEntrypointMarker = "cc_entrypoint="
|
||||
)
|
||||
|
||||
// NewClaudeCodeValidator 创建验证器实例
|
||||
@@ -180,7 +182,7 @@ func (v *ClaudeCodeValidator) hasClaudeCodeSystemPrompt(body map[string]any) boo
|
||||
// 计费归因块识别(WHY 见 claudeCodeBillingHeaderPrefix 注释)。先于 Dice 检查,
|
||||
// 大小写敏感:该块由 gateway_billing_block.go 固定小写生成。
|
||||
if strings.HasPrefix(text, claudeCodeBillingHeaderPrefix) &&
|
||||
strings.Contains(text, claudeCodeCLIEntrypointMarker) {
|
||||
strings.Contains(text, claudeCodeEntrypointMarker) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,44 @@ func TestClaudeCodeValidator_BillingBlockRecognizedWithoutIdentityPrompt(t *test
|
||||
require.True(t, ok)
|
||||
}
|
||||
|
||||
func TestClaudeCodeValidator_BillingBlockNonCLIEntrypointFallsThrough(t *testing.T) {
|
||||
func TestClaudeCodeValidator_BillingBlockVSCodeEntrypointRecognized(t *testing.T) {
|
||||
// 回归:Claude Code 在 VSCode 扩展内运行时,计费块入口为 cc_entrypoint=claude-vscode
|
||||
// 而非 cli。其安全监视器子请求同样不携带身份 prose,此前写死 cc_entrypoint=cli 的
|
||||
// 快速通道无法识别它,导致 claude_code_only 分组误拒。入口值不应作为识别条件。
|
||||
monitorPrompt, err := os.ReadFile("testdata/security_monitor_system_prompt.txt")
|
||||
require.NoError(t, err)
|
||||
|
||||
validator := NewClaudeCodeValidator()
|
||||
|
||||
// 前提:完整监视器正文经 Dice 相似度远低于阈值,放行只可能来自计费归因块识别。
|
||||
require.Less(t, validator.bestSimilarityScore(string(monitorPrompt)), systemPromptThreshold)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "http://example.com/v1/messages", nil)
|
||||
req.Header.Set("User-Agent", "claude-cli/2.1.181 (external, claude-vscode, agent-sdk/0.3.181)")
|
||||
req.Header.Set("X-App", "cli")
|
||||
req.Header.Set("anthropic-beta", "claude-code-20250219")
|
||||
req.Header.Set("anthropic-version", "2023-06-01")
|
||||
|
||||
ok := validator.Validate(req, map[string]any{
|
||||
"model": "claude-opus-4-8",
|
||||
"system": []any{
|
||||
map[string]any{
|
||||
"type": "text",
|
||||
"text": "x-anthropic-billing-header: cc_version=2.1.181.f17; cc_entrypoint=claude-vscode;",
|
||||
},
|
||||
map[string]any{
|
||||
"type": "text",
|
||||
"text": string(monitorPrompt),
|
||||
},
|
||||
},
|
||||
"metadata": map[string]any{
|
||||
"user_id": claudeCodeMetadataUserIDJSON,
|
||||
},
|
||||
})
|
||||
require.True(t, ok)
|
||||
}
|
||||
|
||||
func TestClaudeCodeValidator_BillingBlockWithoutEntrypointFallsThrough(t *testing.T) {
|
||||
validator := NewClaudeCodeValidator()
|
||||
req := httptest.NewRequest(http.MethodPost, "http://example.com/v1/messages", nil)
|
||||
req.Header.Set("User-Agent", "claude-cli/2.1.162 (external, cli)")
|
||||
@@ -143,14 +180,14 @@ func TestClaudeCodeValidator_BillingBlockNonCLIEntrypointFallsThrough(t *testing
|
||||
req.Header.Set("anthropic-beta", "claude-code-20250219")
|
||||
req.Header.Set("anthropic-version", "2023-06-01")
|
||||
|
||||
// 计费块存在但 entrypoint 不是 cli(如 sdk),且无身份 prose:
|
||||
// 不应凭前缀放行,应落回 Dice 检查并失败。验证 cc_entrypoint=cli 这一条件是必要的。
|
||||
// 计费块前缀命中但完全没有 cc_entrypoint= 字段,且无身份 prose:
|
||||
// 不应凭前缀放行,应落回 Dice 检查并失败。验证 cc_entrypoint= 字段的存在仍是必要条件。
|
||||
ok := validator.Validate(req, map[string]any{
|
||||
"model": "claude-3-5-haiku-20241022",
|
||||
"system": []any{
|
||||
map[string]any{
|
||||
"type": "text",
|
||||
"text": "x-anthropic-billing-header: cc_version=2.1.162.884; cc_entrypoint=sdk; cch=d8726;",
|
||||
"text": "x-anthropic-billing-header: cc_version=2.1.162.884; cch=d8726;",
|
||||
},
|
||||
map[string]any{
|
||||
"type": "text",
|
||||
|
||||
Reference in New Issue
Block a user