From d53a399d41672fbd832bf2a5b45cf221e7d55777 Mon Sep 17 00:00:00 2001 From: shaw Date: Fri, 26 Dec 2025 19:15:50 +0800 Subject: [PATCH 01/11] =?UTF-8?q?revert:=20=E5=9B=9E=E9=80=80=E5=88=B0?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E6=BC=8F=E6=B4=9E=E4=BF=AE=E5=A4=8D=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ecf576bd..d13c4d02 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.240 +1.1.248 \ No newline at end of file From 6165fad090ba36b8618db9e861bb3ca69903f055 Mon Sep 17 00:00:00 2001 From: shaw Date: Fri, 26 Dec 2025 19:22:08 +0800 Subject: [PATCH 02/11] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E6=BC=8F=E6=B4=9E=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 5 +++++ README_EN.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/README.md b/README.md index d802aa86..9e358474 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Claude Relay Service +> [!CAUTION] +> **安全更新通知**:v1.1.248 及以下版本存在严重的管理员认证绕过漏洞,攻击者可未授权访问管理面板。 +> +> **请立即更新到 v1.1.249+ 版本**,或迁移到新一代项目 **[CRS 2.0 (sub2api)](https://github.com/Wei-Shaw/sub2api)** +
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) diff --git a/README_EN.md b/README_EN.md index 477c2f52..2eac90ca 100644 --- a/README_EN.md +++ b/README_EN.md @@ -1,5 +1,10 @@ # Claude Relay Service +> [!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)** +
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) From 1f06af4a5654d31c8094a02f16bb38307673f202 Mon Sep 17 00:00:00 2001 From: shaw Date: Fri, 26 Dec 2025 19:25:53 +0800 Subject: [PATCH 03/11] chore: trigger release [force release] From fc571332301141c1ab3a86a37d36846cdcd51c32 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 26 Dec 2025 11:26:14 +0000 Subject: [PATCH 04/11] chore: sync VERSION file with release v1.1.249 [skip ci] --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d13c4d02..099e0d19 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.248 \ No newline at end of file +1.1.249 From 45b81bd4786d292c9081f7b56399e4a19576cb96 Mon Sep 17 00:00:00 2001 From: Junming Chen Date: Sun, 28 Dec 2025 23:56:05 -0500 Subject: [PATCH 05/11] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20authenticateU?= =?UTF-8?q?serOrAdmin=20=E8=AE=A4=E8=AF=81=E7=BB=95=E8=BF=87=E6=BC=8F?= =?UTF-8?q?=E6=B4=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 username 和 loginTime 字段验证(与 authenticateAdmin 保持一致) - 无效/伪造会话自动删除并记录安全日志 - 删除未使用的 id 字段(死代码清理) 漏洞详情: - 位置:src/middleware/auth.js:1569-1581 - 原因:只检查 Object.keys(session).length > 0,未验证必须字段 - 影响:攻击者可通过注入最小会话 {foo:'bar'} 绕过认证 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/middleware/auth.js | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/middleware/auth.js b/src/middleware/auth.js index 44e3cb37..f051a266 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) From 3c64038fa7a0db8ea498c84485605743d3dda13c Mon Sep 17 00:00:00 2001 From: Wesley Liddick Date: Mon, 29 Dec 2025 13:37:15 +0800 Subject: [PATCH 06/11] Create SECURITY.md for security policy [skip ci] Add a security policy document outlining supported versions and vulnerability reporting. --- SECURITY.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 SECURITY.md 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. From a345812cd7dfada91bdefb2f5d4f4c30049e8ab3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 29 Dec 2025 05:46:39 +0000 Subject: [PATCH 07/11] chore: sync VERSION file with release v1.1.250 [skip ci] --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 099e0d19..7b7bb57d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.249 +1.1.250 From c4448db6abde90c7851de11df997431defac501d Mon Sep 17 00:00:00 2001 From: Chapoly1305 Date: Tue, 30 Dec 2025 01:10:43 -0500 Subject: [PATCH 08/11] =?UTF-8?q?fix:=20=E9=98=B2=E6=AD=A2=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=E6=96=AD=E5=BC=80=E8=BF=9E=E6=8E=A5=E6=97=B6?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 当客户端在流式响应过程中断开连接时,catch 块尝试发送 JSON 错误响应 会触发 ERR_HTTP_HEADERS_SENT 错误,导致 unhandledRejection 使服务崩溃。 修复文件: - src/routes/openaiClaudeRoutes.js - src/routes/openaiGeminiRoutes.js 修复内容: - 添加 res.headersSent 检查,避免在响应已发送后再次尝试发送 - 客户端断开连接使用 INFO 级别日志(不是 ERROR) - 客户端断开使用 499 状态码 (Client Closed Request) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- src/routes/openaiClaudeRoutes.js | 29 +++++++++++++++++++++-------- src/routes/openaiGeminiRoutes.js | 32 ++++++++++++++++++++++---------- 2 files changed, 43 insertions(+), 18 deletions(-) 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 fd74ad86..ef65acc1 100644 --- a/src/routes/openaiGeminiRoutes.js +++ b/src/routes/openaiGeminiRoutes.js @@ -604,7 +604,12 @@ router.post('/v1/chat/completions', authenticateApiKey, async (req, res) => { const duration = Date.now() - startTime logger.info(`OpenAI-Gemini request completed in ${duration}ms`) } catch (error) { - logger.error('OpenAI-Gemini request error:', error) + // 客户端主动断开连接是正常情况,使用 INFO 级别 + if (error.message === 'Client disconnected') { + logger.info('🔌 OpenAI-Gemini stream ended: Client disconnected') + } else { + logger.error('OpenAI-Gemini request error:', error) + } // 处理速率限制 if (error.status === 429) { @@ -613,17 +618,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) { From 0a59a0f9d4e407cd5e093715179dbbe56a391021 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 1 Jan 2026 05:57:53 +0000 Subject: [PATCH 09/11] chore: sync VERSION file with release v1.1.251 [skip ci] --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 7b7bb57d..548ae3b2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.250 +1.1.251 From 2693fd77b75e068c68d8d9876f7ae8fe45a59e44 Mon Sep 17 00:00:00 2001 From: shaw Date: Wed, 7 Jan 2026 16:21:04 +0800 Subject: [PATCH 10/11] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4context=5Fmanage?= =?UTF-8?q?ment=E5=AD=97=E6=AE=B5=EF=BC=8C=E9=81=BF=E5=85=8D=E6=8A=A5?= =?UTF-8?q?=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/api.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/routes/api.js b/src/routes/api.js index c38c4d6f..c43ce90a 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -179,18 +179,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}` From 39ba345a43d4438921a52891583e2d51ed359499 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 7 Jan 2026 08:22:01 +0000 Subject: [PATCH 11/11] chore: sync VERSION file with release v1.1.252 [skip ci] --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 548ae3b2..0f5fe772 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.251 +1.1.252