File size: 4,632 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 |
const logger = require('../../utils/logger')
const { CLIENT_DEFINITIONS } = require('../clientDefinitions')
/**
* Codex CLI 验证器
* 验证请求是否来自 Codex CLI
*/
class CodexCliValidator {
/**
* 获取客户端ID
*/
static getId() {
return CLIENT_DEFINITIONS.CODEX_CLI.id
}
/**
* 获取客户端名称
*/
static getName() {
return CLIENT_DEFINITIONS.CODEX_CLI.name
}
/**
* 获取客户端描述
*/
static getDescription() {
return CLIENT_DEFINITIONS.CODEX_CLI.description
}
/**
* 验证请求是否来自 Codex CLI
* @param {Object} req - Express 请求对象
* @returns {boolean} 验证结果
*/
static validate(req) {
try {
const userAgent = req.headers['user-agent'] || ''
const originator = req.headers['originator'] || ''
const sessionId = req.headers['session_id']
// 1. 基础 User-Agent 检查
// Codex CLI 的 UA 格式:
// - codex_vscode/0.35.0 (Windows 10.0.26100; x86_64) unknown (Cursor; 0.4.10)
// - codex_cli_rs/0.38.0 (Ubuntu 22.4.0; x86_64) WindowsTerminal
const codexCliPattern = /^(codex_vscode|codex_cli_rs)\/[\d\.]+/i
const uaMatch = userAgent.match(codexCliPattern)
if (!uaMatch) {
logger.debug(`Codex CLI validation failed - UA mismatch: ${userAgent}`)
return false
}
// 2. 对于特定路径,进行额外的严格验证
// 对于 /openai 和 /azure 路径需要完整验证
const strictValidationPaths = ['/openai', '/azure']
const needsStrictValidation =
req.path && strictValidationPaths.some((path) => req.path.startsWith(path))
if (!needsStrictValidation) {
// 其他路径,只要 User-Agent 匹配就认为是 Codex CLI
logger.debug(`Codex CLI detected for path: ${req.path}, allowing access`)
return true
}
// 3. 验证 originator 头必须与 UA 中的客户端类型匹配
const clientType = uaMatch[1].toLowerCase()
if (originator.toLowerCase() !== clientType) {
logger.debug(
`Codex CLI validation failed - originator mismatch. UA: ${clientType}, originator: ${originator}`
)
return false
}
// 4. 检查 session_id - 必须存在且长度大于20
if (!sessionId || sessionId.length <= 20) {
logger.debug(`Codex CLI validation failed - session_id missing or too short: ${sessionId}`)
return false
}
// 5. 对于 /openai/responses 和 /azure/response 路径,额外检查 body 中的 instructions 字段
if (
req.path &&
(req.path.includes('/openai/responses') || req.path.includes('/azure/response'))
) {
if (!req.body || !req.body.instructions) {
logger.debug(`Codex CLI validation failed - missing instructions in body for ${req.path}`)
return false
}
const expectedPrefix =
'You are Codex, based on GPT-5. You are running as a coding agent in the Codex CLI'
if (!req.body.instructions.startsWith(expectedPrefix)) {
logger.debug(`Codex CLI validation failed - invalid instructions prefix for ${req.path}`)
logger.debug(`Expected: "${expectedPrefix}..."`)
logger.debug(`Received: "${req.body.instructions.substring(0, 100)}..."`)
return false
}
// 额外检查 model 字段应该是 gpt-5-codex
if (req.body.model && req.body.model !== 'gpt-5-codex') {
logger.debug(`Codex CLI validation warning - unexpected model: ${req.body.model}`)
// 只记录警告,不拒绝请求
}
}
// 所有必要检查通过
logger.debug(`Codex CLI validation passed for UA: ${userAgent}`)
return true
} catch (error) {
logger.error('Error in CodexCliValidator:', error)
// 验证出错时默认拒绝
return false
}
}
/**
* 比较版本号
* @returns {number} -1: v1 < v2, 0: v1 = v2, 1: v1 > v2
*/
static compareVersions(v1, v2) {
const parts1 = v1.split('.').map(Number)
const parts2 = v2.split('.').map(Number)
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
const part1 = parts1[i] || 0
const part2 = parts2[i] || 0
if (part1 < part2) return -1
if (part1 > part2) return 1
}
return 0
}
/**
* 获取验证器信息
*/
static getInfo() {
return {
id: this.getId(),
name: this.getName(),
description: this.getDescription(),
icon: CLIENT_DEFINITIONS.CODEX_CLI.icon
}
}
}
module.exports = CodexCliValidator
|