File size: 7,101 Bytes
69b897d 6c6056a 69b897d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
const logger = require('../../utils/logger')
const { CLIENT_DEFINITIONS } = require('../clientDefinitions')
const { bestSimilarityByTemplates, SYSTEM_PROMPT_THRESHOLD } = require('../../utils/contents')
/**
* Claude Code CLI 验证器
* 验证请求是否来自 Claude Code CLI
*/
class ClaudeCodeValidator {
/**
* 获取客户端ID
*/
static getId() {
return CLIENT_DEFINITIONS.CLAUDE_CODE.id
}
/**
* 获取客户端名称
*/
static getName() {
return CLIENT_DEFINITIONS.CLAUDE_CODE.name
}
/**
* 获取客户端描述
*/
static getDescription() {
return CLIENT_DEFINITIONS.CLAUDE_CODE.description
}
/**
* 获取客户端图标
*/
static getIcon() {
return CLIENT_DEFINITIONS.CLAUDE_CODE.icon || '🤖'
}
/**
* 检查请求是否包含 Claude Code 系统提示词
* @param {Object} body - 请求体
* @returns {boolean} 是否包含 Claude Code 系统提示词
*/
static hasClaudeCodeSystemPrompt(body, customThreshold) {
if (!body || typeof body !== 'object') {
return false
}
const model = typeof body.model === 'string' ? body.model : null
if (!model) {
return false
}
const systemEntries = Array.isArray(body.system) ? body.system : null
if (!systemEntries) {
return false
}
const threshold =
typeof customThreshold === 'number' && Number.isFinite(customThreshold)
? customThreshold
: SYSTEM_PROMPT_THRESHOLD
for (const entry of systemEntries) {
const rawText = typeof entry?.text === 'string' ? entry.text : ''
const { bestScore } = bestSimilarityByTemplates(rawText)
if (bestScore < threshold) {
logger.error(
`Claude system prompt similarity below threshold: score=${bestScore.toFixed(4)}, threshold=${threshold}, prompt=${rawText}`
)
return false
}
}
return true
}
/**
* 判断是否存在 Claude Code 系统提示词(存在即返回 true)
* @param {Object} body - 请求体
* @param {number} [customThreshold] - 自定义阈值
* @returns {boolean} 是否存在 Claude Code 系统提示词
*/
static includesClaudeCodeSystemPrompt(body, customThreshold) {
if (!body || typeof body !== 'object') {
return false
}
const model = typeof body.model === 'string' ? body.model : null
if (!model) {
return false
}
const systemEntries = Array.isArray(body.system) ? body.system : null
if (!systemEntries) {
return false
}
const threshold =
typeof customThreshold === 'number' && Number.isFinite(customThreshold)
? customThreshold
: SYSTEM_PROMPT_THRESHOLD
let bestMatchScore = 0
for (const entry of systemEntries) {
const rawText = typeof entry?.text === 'string' ? entry.text : ''
const { bestScore } = bestSimilarityByTemplates(rawText)
if (bestScore > bestMatchScore) {
bestMatchScore = bestScore
}
if (bestScore >= threshold) {
return true
}
}
logger.debug(
`Claude system prompt not detected: bestScore=${bestMatchScore.toFixed(4)}, threshold=${threshold}`
)
return false
}
/**
* 验证请求是否来自 Claude Code CLI
* @param {Object} req - Express 请求对象
* @returns {boolean} 验证结果
*/
static validate(req) {
try {
const userAgent = req.headers['user-agent'] || ''
const path = req.path || ''
const claudeCodePattern = /^claude-cli\/\d+\.\d+\.\d+/i
if (!claudeCodePattern.test(userAgent)) {
// 不是 Claude Code 的请求,此验证器不处理
return false
}
// 2. Claude Code 检测到,对于特定路径进行额外的严格验证
if (!path.includes('messages')) {
// 其他路径,只要 User-Agent 匹配就认为是 Claude Code
logger.debug(`Claude Code detected for path: ${path}, allowing access`)
return true
}
// 3. 检查系统提示词是否为 Claude Code 的系统提示词
if (!this.hasClaudeCodeSystemPrompt(req.body)) {
logger.debug('Claude Code validation failed - missing or invalid Claude Code system prompt')
return false
}
// 4. 检查必需的头部(值不为空即可)
const xApp = req.headers['x-app']
const anthropicBeta = req.headers['anthropic-beta']
const anthropicVersion = req.headers['anthropic-version']
if (!xApp || xApp.trim() === '') {
logger.debug('Claude Code validation failed - missing or empty x-app header')
return false
}
if (!anthropicBeta || anthropicBeta.trim() === '') {
logger.debug('Claude Code validation failed - missing or empty anthropic-beta header')
return false
}
if (!anthropicVersion || anthropicVersion.trim() === '') {
logger.debug('Claude Code validation failed - missing or empty anthropic-version header')
return false
}
logger.debug(
`Claude Code headers - x-app: ${xApp}, anthropic-beta: ${anthropicBeta}, anthropic-version: ${anthropicVersion}`
)
// 5. 验证 body 中的 metadata.user_id
if (!req.body || !req.body.metadata || !req.body.metadata.user_id) {
logger.debug('Claude Code validation failed - missing metadata.user_id in body')
return false
}
const userId = req.body.metadata.user_id
// 格式: user_{64位字符串}_account__session_{哈希值}
// user_d98385411c93cd074b2cefd5c9831fe77f24a53e4ecdcd1f830bba586fe62cb9_account__session_17cf0fd3-d51b-4b59-977d-b899dafb3022
const userIdPattern = /^user_[a-fA-F0-9]{64}_account__session_[\w-]+$/
if (!userIdPattern.test(userId)) {
logger.debug(`Claude Code validation failed - invalid user_id format: ${userId}`)
// 提供更详细的错误信息
if (!userId.startsWith('user_')) {
logger.debug('user_id must start with "user_"')
} else {
const parts = userId.split('_')
if (parts.length < 4) {
logger.debug('user_id format is incomplete')
} else if (parts[1].length !== 64) {
logger.debug(`user hash must be 64 characters, got ${parts[1].length}`)
} else if (parts[2] !== 'account' || parts[3] !== '' || parts[4] !== 'session') {
logger.debug('user_id must contain "_account__session_"')
}
}
return false
}
// 6. 额外日志记录(用于调试)
logger.debug(`Claude Code validation passed - UA: ${userAgent}, userId: ${userId}`)
// 所有必要检查通过
return true
} catch (error) {
logger.error('Error in ClaudeCodeValidator:', error)
// 验证出错时默认拒绝
return false
}
}
/**
* 获取验证器信息
*/
static getInfo() {
return {
id: this.getId(),
name: this.getName(),
description: this.getDescription(),
icon: CLIENT_DEFINITIONS.CLAUDE_CODE.icon
}
}
}
module.exports = ClaudeCodeValidator
|