mirror of
https://gh-proxy.org/https://github.com/klinsmaya/CLIProxyAPI.git
synced 2026-11-08 17:36:47 +08:00
feat(signature): upgrade provider signature checks
This commit is contained in:
@@ -226,6 +226,40 @@ func NormalizeClaudeThinkingSignature(rawSignature string, opts ...ClaudeSignatu
|
||||
}
|
||||
}
|
||||
|
||||
// NormalizeClaudeProviderNativeThinkingSignature strips any cache prefix,
|
||||
// validates the signature, and returns the single-layer E-form expected by
|
||||
// Claude-native providers.
|
||||
func NormalizeClaudeProviderNativeThinkingSignature(rawSignature string, opts ...ClaudeSignatureValidationOptions) (string, error) {
|
||||
opt := claudeSignatureValidationOptions(opts)
|
||||
sig := stripClaudeSignaturePrefix(rawSignature)
|
||||
if sig == "" {
|
||||
return "", fmt.Errorf("empty signature")
|
||||
}
|
||||
|
||||
if len(sig) > MaxClaudeThinkingSignatureLen {
|
||||
return "", fmt.Errorf("signature exceeds maximum length (%d bytes)", MaxClaudeThinkingSignatureLen)
|
||||
}
|
||||
|
||||
switch sig[0] {
|
||||
case 'E':
|
||||
if err := validateClaudeSingleLayerSignature(sig, opt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sig, nil
|
||||
case 'R':
|
||||
if err := validateClaudeDoubleLayerSignature(sig, opt); err != nil {
|
||||
return "", err
|
||||
}
|
||||
decoded, err := base64.StdEncoding.DecodeString(sig)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid double-layer signature: base64 decode failed: %w", err)
|
||||
}
|
||||
return string(decoded), nil
|
||||
default:
|
||||
return "", fmt.Errorf("invalid signature: expected 'E' or 'R' prefix, got %q", string(sig[0]))
|
||||
}
|
||||
}
|
||||
|
||||
func validateClaudeDoubleLayerSignature(sig string, opt ClaudeSignatureValidationOptions) error {
|
||||
decoded, err := base64.StdEncoding.DecodeString(sig)
|
||||
if err != nil {
|
||||
|
||||
@@ -229,6 +229,24 @@ func CompatibleSignatureForProviderBlock(targetProvider SignatureProvider, rawSi
|
||||
return decision.NormalizedSignature, true
|
||||
}
|
||||
|
||||
// CompatibleAntigravityClaudeThinkingSignature returns the double-layer R-form
|
||||
// required by Antigravity Claude replay. It only accepts signatures that are
|
||||
// strictly identifiable as Claude, so Gemini E-prefixed envelopes cannot slip
|
||||
// through the looser Antigravity bypass normalization path.
|
||||
func CompatibleAntigravityClaudeThinkingSignature(rawSignature string) (string, bool) {
|
||||
if DetectSignatureProviderForBlock(rawSignature, SignatureBlockKindClaudeThinking) != SignatureProviderClaude {
|
||||
return "", false
|
||||
}
|
||||
normalized, err := NormalizeClaudeThinkingSignature(
|
||||
SignaturePayloadWithoutProviderPrefix(rawSignature),
|
||||
ClaudeSignatureValidationOptions{Strict: true},
|
||||
)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
return normalized, true
|
||||
}
|
||||
|
||||
func normalizeSignatureTargetProvider(provider SignatureProvider) SignatureProvider {
|
||||
switch provider {
|
||||
case SignatureProviderGeminiBypass:
|
||||
@@ -255,7 +273,7 @@ func normalizeCompatibleSignatureForProvider(targetProvider SignatureProvider, r
|
||||
payload := SignaturePayloadWithoutProviderPrefix(rawSignature)
|
||||
switch normalizeSignatureTargetProvider(targetProvider) {
|
||||
case SignatureProviderClaude:
|
||||
normalized, err := NormalizeClaudeThinkingSignature(payload)
|
||||
normalized, err := NormalizeClaudeProviderNativeThinkingSignature(payload)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -61,6 +61,42 @@ func TestDetectSignatureProvider_Gemini3EPrefixDoesNotLookClaude(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompatibleSignatureForProvider_ClaudeUsesProviderNativeEForm(t *testing.T) {
|
||||
nativeSig := testClaudeThinkingSignature()
|
||||
doubleEncoded := base64.StdEncoding.EncodeToString([]byte(nativeSig))
|
||||
|
||||
normalized, ok := CompatibleSignatureForProvider(SignatureProviderClaude, doubleEncoded)
|
||||
if !ok {
|
||||
t.Fatal("double-layer Claude signature should be compatible")
|
||||
}
|
||||
if normalized != nativeSig {
|
||||
t.Fatalf("CompatibleSignatureForProvider(Claude) = %q, want provider-native %q", normalized, nativeSig)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompatibleAntigravityClaudeThinkingSignature_UsesDoubleLayerRForm(t *testing.T) {
|
||||
nativeSig := testClaudeThinkingSignature()
|
||||
expected := base64.StdEncoding.EncodeToString([]byte(nativeSig))
|
||||
|
||||
normalized, ok := CompatibleAntigravityClaudeThinkingSignature(nativeSig)
|
||||
if !ok {
|
||||
t.Fatal("Claude signature should be compatible with Antigravity Claude")
|
||||
}
|
||||
if normalized != expected {
|
||||
t.Fatalf("CompatibleAntigravityClaudeThinkingSignature = %q, want %q", normalized, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompatibleAntigravityClaudeThinkingSignature_RejectsGeminiEPrefix(t *testing.T) {
|
||||
geminiSig := testGemini3ThoughtSignature([]byte{0x01, 0x0c, 0x39, 0xd6, 0xc7, 0x34})
|
||||
if !strings.HasPrefix(geminiSig, "E") {
|
||||
t.Fatalf("test signature should start with E, got %q", geminiSig[:1])
|
||||
}
|
||||
if normalized, ok := CompatibleAntigravityClaudeThinkingSignature(geminiSig); ok || normalized != "" {
|
||||
t.Fatalf("Gemini E-prefix signature normalized=%q ok=%v, want rejected", normalized, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectSignatureProvider_DoesNotClassifyArbitraryBase64AsGemini(t *testing.T) {
|
||||
opaque := testGeminiThoughtSignature([]byte{0x45, 0x12})
|
||||
if got := DetectSignatureProvider(opaque); got != SignatureProviderUnknown {
|
||||
@@ -172,9 +208,9 @@ func TestSanitizeClaudeMessagesSignaturesForModel_NormalizesSameProviderClaude(t
|
||||
nativeSig := testClaudeThinkingSignature()
|
||||
sig := "claude#" + nativeSig
|
||||
input := []byte(`{"model":"claude-sonnet","messages":[{"role":"assistant","content":[{"type":"thinking","thinking":"keep","signature":"` + sig + `"},{"type":"text","text":"answer"}]}]}`)
|
||||
expectedSig, err := NormalizeClaudeThinkingSignature(nativeSig)
|
||||
expectedSig, err := NormalizeClaudeProviderNativeThinkingSignature(nativeSig)
|
||||
if err != nil {
|
||||
t.Fatalf("NormalizeClaudeThinkingSignature failed: %v", err)
|
||||
t.Fatalf("NormalizeClaudeProviderNativeThinkingSignature failed: %v", err)
|
||||
}
|
||||
|
||||
output, report := SanitizeClaudeMessagesSignaturesForModel(input, "claude-sonnet-4-5")
|
||||
|
||||
Reference in New Issue
Block a user