fix: 修复CodeReview发现的安全问题和代码质量问题 | fix security and code quality issues identified by CodeReview

- 修复JSON注入漏洞:使用json.Marshal()安全转义字符串内容
- 定义常量CHARS_PER_TOKEN替换硬编码的token估算数字4
- 处理UnmarshalJSON错误,避免静默失败并记录错误日志
- 定义常量替换硬编码的API端点路径,提高可维护性

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Deadwalk
2025-09-28 16:41:48 +08:00
parent e27612a620
commit 48396d3f33
3 changed files with 26 additions and 7 deletions

View File

@@ -14,6 +14,13 @@ import (
"github.com/songquanpeng/one-api/relay/relaymode"
)
const (
// NativeAnthropicEndpoint is the endpoint for native Anthropic API
NativeAnthropicEndpoint = "/v1/messages"
// ThirdPartyAnthropicEndpoint is the endpoint for third-party providers supporting Anthropic protocol
ThirdPartyAnthropicEndpoint = "/anthropic/v1/messages"
)
type Adaptor struct {
}
@@ -24,13 +31,13 @@ func (a *Adaptor) Init(meta *meta.Meta) {
func (a *Adaptor) GetRequestURL(meta *meta.Meta) (string, error) {
// For native Anthropic API
if strings.Contains(meta.BaseURL, "api.anthropic.com") {
return fmt.Sprintf("%s/v1/messages", meta.BaseURL), nil
return fmt.Sprintf("%s%s", meta.BaseURL, NativeAnthropicEndpoint), nil
}
// For third-party providers supporting Anthropic protocol (like DeepSeek)
// They typically expose the endpoint at /anthropic/v1/messages
baseURL := strings.TrimSuffix(meta.BaseURL, "/")
return fmt.Sprintf("%s/anthropic/v1/messages", baseURL), nil
return fmt.Sprintf("%s%s", baseURL, ThirdPartyAnthropicEndpoint), nil
}
func (a *Adaptor) SetupRequestHeader(c *gin.Context, req *http.Request, meta *meta.Meta) error {

View File

@@ -93,9 +93,15 @@ func ConvertRequest(textRequest model.GeneralOpenAIRequest) *Request {
if message.Role == "system" && claudeRequest.System.IsEmpty() {
// Create a SystemPrompt from the string content
systemPrompt := SystemPrompt{}
systemData := []byte(`"` + message.StringContent() + `"`) // Wrap in JSON string quotes
_ = systemPrompt.UnmarshalJSON(systemData)
claudeRequest.System = systemPrompt
systemData, err := json.Marshal(message.StringContent()) // Safely escape string for JSON
if err != nil {
logger.SysError(fmt.Sprintf("Failed to marshal system prompt: %v", err))
} else {
if err := systemPrompt.UnmarshalJSON(systemData); err != nil {
logger.SysError(fmt.Sprintf("Failed to unmarshal system prompt: %v", err))
}
claudeRequest.System = systemPrompt
}
continue
}
claudeMessage := Message{

View File

@@ -142,6 +142,12 @@ func getAnthropicRequestBody(c *gin.Context, anthropicRequest *anthropic.Request
return bytes.NewBuffer(jsonData), nil
}
const (
// CHARS_PER_TOKEN represents the rough character-to-token ratio for Anthropic models
// This is a conservative estimate: approximately 1 token per 4 characters
CHARS_PER_TOKEN = 4
)
func estimateAnthropicTokens(request *anthropic.Request) int {
// Simple token estimation for Anthropic requests
// This is a rough estimation, actual implementation might need more sophisticated logic
@@ -150,14 +156,14 @@ func estimateAnthropicTokens(request *anthropic.Request) int {
// Count tokens in system prompt
if !request.System.IsEmpty() {
systemText := request.System.String()
totalTokens += len(systemText) / 4 // rough estimate: 1 token per 4 characters
totalTokens += len(systemText) / CHARS_PER_TOKEN // rough estimate: 1 token per 4 characters
}
// Count tokens in messages
for _, message := range request.Messages {
for _, content := range message.Content {
if content.Type == "text" {
totalTokens += len(content.Text) / 4
totalTokens += len(content.Text) / CHARS_PER_TOKEN
}
}
}