diff --git a/README.md b/README.md
index b2ed81ec..e267fbcd 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,11 @@
>
> 目标:让 `claude`(Claude Code CLI)与 Antigravity / Gemini 账户体系无缝对接,并提供可观测、可运维的稳定转发服务。
+> [!CAUTION]
+> **安全更新通知**:v1.1.248 及以下版本存在严重的管理员认证绕过漏洞,攻击者可未授权访问管理面板。
+>
+> **请立即更新到 v1.1.249+ 版本**,或迁移到新一代项目 **[CRS 2.0 (sub2api)](https://github.com/Wei-Shaw/sub2api)**
+
[](https://opensource.org/licenses/MIT)
diff --git a/README_EN.md b/README_EN.md
index 856ecb90..e3e8cdbb 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -1,6 +1,12 @@
# Claude Relay Service (Antigravity Edition)
-Maintained fork by **dadongwo**.
+
+> [!CAUTION]
+> **Security Update**: v1.1.248 and below contain a critical admin authentication bypass vulnerability allowing unauthorized access to the admin panel.
+>
+> **Please update to v1.1.249+ immediately**, or migrate to the next-generation project **[CRS 2.0 (sub2api)](https://github.com/Wei-Shaw/sub2api)**
+
+
This fork focuses on:
- Native compatibility for `claude` (Claude Code CLI)
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..034e8480
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,21 @@
+# Security Policy
+
+## Supported Versions
+
+Use this section to tell people about which versions of your project are
+currently being supported with security updates.
+
+| Version | Supported |
+| ------- | ------------------ |
+| 5.1.x | :white_check_mark: |
+| 5.0.x | :x: |
+| 4.0.x | :white_check_mark: |
+| < 4.0 | :x: |
+
+## Reporting a Vulnerability
+
+Use this section to tell people how to report a vulnerability.
+
+Tell them where to go, how often they can expect to get an update on a
+reported vulnerability, what to expect if the vulnerability is accepted or
+declined, etc.
diff --git a/VERSION b/VERSION
index 9c6cacb8..0f5fe772 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.1.241
+1.1.252
diff --git a/src/middleware/auth.js b/src/middleware/auth.js
index 8cccee0c..1883a2a0 100644
--- a/src/middleware/auth.js
+++ b/src/middleware/auth.js
@@ -1434,7 +1434,6 @@ const authenticateAdmin = async (req, res, next) => {
// 设置管理员信息(只包含必要信息)
req.admin = {
- id: adminSession.adminId || 'admin',
username: adminSession.username,
sessionId: token,
loginTime: adminSession.loginTime
@@ -1567,17 +1566,25 @@ const authenticateUserOrAdmin = async (req, res, next) => {
try {
const adminSession = await redis.getSession(adminToken)
if (adminSession && Object.keys(adminSession).length > 0) {
- req.admin = {
- id: adminSession.adminId || 'admin',
- username: adminSession.username,
- sessionId: adminToken,
- loginTime: adminSession.loginTime
- }
- req.userType = 'admin'
+ // 🔒 安全修复:验证会话必须字段(与 authenticateAdmin 保持一致)
+ if (!adminSession.username || !adminSession.loginTime) {
+ logger.security(
+ `🔒 Corrupted admin session in authenticateUserOrAdmin from ${req.ip || 'unknown'} - missing required fields (username: ${!!adminSession.username}, loginTime: ${!!adminSession.loginTime})`
+ )
+ await redis.deleteSession(adminToken) // 清理无效/伪造的会话
+ // 不返回 401,继续尝试用户认证
+ } else {
+ req.admin = {
+ username: adminSession.username,
+ sessionId: adminToken,
+ loginTime: adminSession.loginTime
+ }
+ req.userType = 'admin'
- const authDuration = Date.now() - startTime
- logger.security(`🔐 Admin authenticated: ${adminSession.username} in ${authDuration}ms`)
- return next()
+ const authDuration = Date.now() - startTime
+ logger.security(`🔐 Admin authenticated: ${adminSession.username} in ${authDuration}ms`)
+ return next()
+ }
}
} catch (error) {
logger.debug('Admin authentication failed, trying user authentication:', error.message)
diff --git a/src/routes/api.js b/src/routes/api.js
index 6ec81cbd..4b983d64 100644
--- a/src/routes/api.js
+++ b/src/routes/api.js
@@ -205,18 +205,18 @@ async function handleMessagesRequest(req, res) {
const isStream = req.body.stream === true
// 临时修复新版本客户端,删除context_management字段,避免报错
- // if (req.body.context_management) {
- // delete req.body.context_management
- // }
+ if (req.body.context_management) {
+ delete req.body.context_management
+ }
// 遍历tools数组,删除input_examples字段
- // if (req.body.tools && Array.isArray(req.body.tools)) {
- // req.body.tools.forEach((tool) => {
- // if (tool && typeof tool === 'object' && tool.input_examples) {
- // delete tool.input_examples
- // }
- // })
- // }
+ if (req.body.tools && Array.isArray(req.body.tools)) {
+ req.body.tools.forEach((tool) => {
+ if (tool && typeof tool === 'object' && tool.input_examples) {
+ delete tool.input_examples
+ }
+ })
+ }
logger.api(
`🚀 Processing ${isStream ? 'stream' : 'non-stream'} request for key: ${req.apiKey.name}`
diff --git a/src/routes/openaiClaudeRoutes.js b/src/routes/openaiClaudeRoutes.js
index 2bb7cc09..35314e4a 100644
--- a/src/routes/openaiClaudeRoutes.js
+++ b/src/routes/openaiClaudeRoutes.js
@@ -402,16 +402,29 @@ async function handleChatCompletion(req, res, apiKeyData) {
const duration = Date.now() - startTime
logger.info(`✅ OpenAI-Claude request completed in ${duration}ms`)
} catch (error) {
- logger.error('❌ OpenAI-Claude request error:', error)
+ // 客户端主动断开连接是正常情况,使用 INFO 级别
+ if (error.message === 'Client disconnected') {
+ logger.info('🔌 OpenAI-Claude stream ended: Client disconnected')
+ } else {
+ logger.error('❌ OpenAI-Claude request error:', error)
+ }
- const status = error.status || 500
- res.status(status).json({
- error: {
- message: error.message || 'Internal server error',
- type: 'server_error',
- code: 'internal_error'
+ // 检查响应是否已发送(流式响应场景),避免 ERR_HTTP_HEADERS_SENT
+ if (!res.headersSent) {
+ // 客户端断开使用 499 状态码 (Client Closed Request)
+ if (error.message === 'Client disconnected') {
+ res.status(499).end()
+ } else {
+ const status = error.status || 500
+ res.status(status).json({
+ error: {
+ message: error.message || 'Internal server error',
+ type: 'server_error',
+ code: 'internal_error'
+ }
+ })
}
- })
+ }
} finally {
// 清理资源
if (abortController) {
diff --git a/src/routes/openaiGeminiRoutes.js b/src/routes/openaiGeminiRoutes.js
index a2678e3f..9b6b5bb8 100644
--- a/src/routes/openaiGeminiRoutes.js
+++ b/src/routes/openaiGeminiRoutes.js
@@ -673,17 +673,24 @@ router.post('/v1/chat/completions', authenticateApiKey, async (req, res) => {
}
}
- // 返回 OpenAI 格式的错误响应
- const status = error.status || 500
- const errorResponse = {
- error: error.error || {
- message: error.message || 'Internal server error',
- type: 'server_error',
- code: 'internal_error'
+ // 检查响应是否已发送(流式响应场景),避免 ERR_HTTP_HEADERS_SENT
+ if (!res.headersSent) {
+ // 客户端断开使用 499 状态码 (Client Closed Request)
+ if (error.message === 'Client disconnected') {
+ res.status(499).end()
+ } else {
+ // 返回 OpenAI 格式的错误响应
+ const status = error.status || 500
+ const errorResponse = {
+ error: error.error || {
+ message: error.message || 'Internal server error',
+ type: 'server_error',
+ code: 'internal_error'
+ }
+ }
+ res.status(status).json(errorResponse)
}
}
-
- res.status(status).json(errorResponse)
} finally {
// 清理资源
if (abortController) {