Skip to content

Commit 13bfbf9

Browse files
鲁工鲁工
authored andcommitted
fix: prevent cross-project leaks and stream corruption
1 parent df7bc6f commit 13bfbf9

67 files changed

Lines changed: 2319 additions & 646 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ MIMO_PAYG_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxx
9191
# 设置后,调用方需在 "x-api-key" 或 "Authorization: Bearer <token>" 中携带此令牌。
9292
# 仅当网关保持绑定在 127.0.0.1 时才可留空。
9393
# CCMR_REQUIRED_AUTH_TOKEN=
94+
# 客户端令牌覆盖(通常无需设置;默认复用 CCMR_REQUIRED_AUTH_TOKEN)
95+
# CCMR_AUTH_TOKEN=
9496

9597
# 日志级别
9698
# LOG_LEVEL=INFO

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ jobs:
3131
- name: Test
3232
run: npm test
3333

34+
- name: Audit dependencies
35+
run: npm audit --audit-level=high
36+
3437
- name: Smoke test the built gateway
3538
run: npm run smoke
3639

IMPLEMENTATION_PLAN.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
# v1.8.0 实施计划(易用性 + 风险修复)
22

3+
## v1.8.3 审查修复计划(2026-07-16)
4+
5+
本轮按风险优先级逐项修复,并为每项补回归测试;不删除现有项目文件。
6+
7+
| # | 阶段 | 内容 | 状态 |
8+
|---|------|------|------|
9+
| 1 | 配置隔离 | 阻止 `ccmr claude` 跨项目复用来源不一致的后台网关 | ✅ 完成 |
10+
| 2 | 流式可靠性 | 增量 UTF-8 解码、CRLF SSE、断连取消、响应背压 | ✅ 完成 |
11+
| 3 | 鉴权与密钥 | 统一客户端/服务端令牌、可撤销 `.env` 热加载、网络绑定 fail-closed | ✅ 完成 |
12+
| 4 | 配置与功能 | CLI/YAML/env 优先级、严格校验、能力约束、token counting 代理 | ✅ 完成 |
13+
| 5 | 发布工程 | 修复依赖审计、补 LICENSE、更新 README/CI,并完成全量发布检查 | ✅ 完成 |
14+
15+
---
16+
317
按 review 结论分 5 个阶段实施,测试先行(vitest 基线 → 新功能 red-green)。
418

519
| # | 阶段 | 内容 | 状态 |
@@ -21,3 +35,10 @@
2135
- `ccmr doctor`(真实 key,ccmr-start):7 ok / 3 fail / 18 skip;确认 seed-2.1-pro 已开通可用,暴露 seed-2.1-turbo 未开通、mimo-token-cn Key 失效 ✅
2236
- 自动拉起 E2E:`ccmr claude -p ... --gateway-port 8096`(空端口)→ 网关自动拉起 → DeepSeek 真实请求返回 `SELFTEST-OK`
2337
- `ccmr stats`:正确显示 1 次请求 39148/41 tokens ✅
38+
39+
## v1.8.3 自测记录(2026-07-16)
40+
41+
- `npm run check`:TypeScript 类型检查、107/107 测试、构建与网关烟雾测试全部通过 ✅
42+
- 烟雾测试覆盖健康检查、非流式转发、SSE 流式转发与用量统计 ✅
43+
- `npm audit --audit-level=high`:0 vulnerabilities ✅
44+
- `npm pack --dry-run`:发布包内容检查通过 ✅

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 luwill
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ npx claude-code-model-router init --global # 写入 ~/.ccmr,全目录共享
5959
# 启动网关(前台运行;models.yaml / .env 修改后自动热重载,无需重启)
6060
npx claude-code-model-router start
6161
npx claude-code-model-router start --port 9000 # 指定端口
62-
npx claude-code-model-router start --host 0.0.0.0 # 监听所有网卡(见下方安全提示)
62+
npx claude-code-model-router start --host 0.0.0.0 # 需设置入站鉴权令牌
6363

6464
# 查看本机正在运行的网关(端口 / PID / 版本 / 配置来源 / Key 状态)
6565
npx claude-code-model-router status
@@ -91,7 +91,7 @@ npx claude-code-model-router claude --gateway-port 9000 # 自定义网关端口
9191
claude
9292
```
9393

94-
> **配置发现顺序**`-c 指定路径` > `./models.yaml` > `./config/models.yaml` > `./.claude-router.yaml` > `~/.ccmr/models.yaml``.env` 同理:`./.env` 优先,`~/.ccmr/.env` 兜底(环境变量 `CCMR_HOME` 可改写全局目录位置,日志 `gateway.log` 也随之移动)。
94+
> **配置发现顺序**`-c 指定路径` > `./models.yaml` > `./config/models.yaml` > `./.claude-router.yaml` > `~/.ccmr/models.yaml`显式 `-c` 不存在或配置非法时会直接报错,不会回退到另一份配置。`.env` `~/.ccmr/.env` < `./.env` < 配置文件相邻 `.env` 的优先级加载,父进程环境变量优先级最高(`CCMR_HOME` 可改写全局目录位置,日志也随之移动)。
9595
9696
### 网关的生命周期
9797

@@ -102,10 +102,12 @@ claude
102102

103103
自动拉起的网关是 detached 进程(自成进程组、`PPID=1`),收不到终端的 `SIGHUP`——这是有意设计:多个 Claude Code 会话可以共用同一个网关,关掉其中一个窗口不该打断其他会话。代价是它不会自己消失,用 `ccmr status` 查看、`ccmr stop` 收掉。
104104

105-
> `ccmr stop` 只会停止通过 `/health` 自证身份的 ccmr 网关。如果端口被其他程序占用,它会明确报错并拒绝操作,绝不会误杀你的其他进程。
105+
复用网关前会比较配置路径、路由内容、`.env` 来源及 API Key 的不可逆摘要。若端口上的网关来自另一个项目,`ccmr claude` 会拒绝复用并提示换端口或先停止旧网关,避免提示词发往错误的供应商账号。
106+
107+
> `ccmr stop` 会同时校验 `/health` 身份和 `~/.ccmr/gateway-<port>.identity.json` 中的本机随机身份记录,匹配后才会发送信号。只伪造 HTTP 响应不能诱导它停止任意 PID。
106108
107109
> **安全提示**:网关默认绑定到 `127.0.0.1`(仅本机可访问)。网关会用你本地配置的各厂商 API Key 代理上游请求,因此任何能访问该端口的人都能消耗你的额度。
108-
> 若确需通过 `--host 0.0.0.0` 暴露到局域网,请务必设置环境变量 `CCMR_REQUIRED_AUTH_TOKEN`此时调用方必须在 `x-api-key``Authorization: Bearer <token>` 中携带该令牌。未设置时绑定非回环地址会打印警告
110+
> 若确需通过 `--host 0.0.0.0` 暴露到局域网,必须设置环境变量 `CCMR_REQUIRED_AUTH_TOKEN`此时调用方需在 `x-api-key``Authorization: Bearer <token>` 中携带该令牌。未设置时网关默认拒绝启动;仅在已隔离且明确接受风险时使用 `--allow-insecure-network`。远程未认证的 `/health` 只返回基础存活信息,不暴露 PID、路径或 Key 状态
109111
110112
### Claude Code 原生参数支持
111113

@@ -244,15 +246,23 @@ MIMO_API_KEY=tp-xxx # MiMo Token Plan,默认 SGP 集群
244246
MIMO_TOKEN_CN_API_KEY=tp-xxx # MiMo Token Plan CN 集群
245247
MIMO_TOKEN_AMS_API_KEY=tp-xxx # MiMo Token Plan AMS 集群
246248
MIMO_PAYG_API_KEY=sk-xxx # MiMo Pay-as-you-go: https://platform.xiaomimimo.com/
249+
250+
# 可选网关设置
251+
GATEWAY_PORT=8080
252+
REQUEST_TIMEOUT=300
253+
LOG_LEVEL=INFO # DEBUG / INFO / WARN / ERROR / SILENT
254+
CCMR_REQUIRED_AUTH_TOKEN= # 非回环监听时必须设置
247255
```
248256

257+
`ccmr claude``ccmr stats` 会自动使用 `CCMR_REQUIRED_AUTH_TOKEN`。如客户端与服务端使用不同环境,可在客户端单独设置 `CCMR_AUTH_TOKEN` 覆盖发送令牌。
258+
249259
MiMo Token Plan 的 Base URL 与购买套餐所在集群绑定。默认 `mimo` 使用 SGP 集群;如果订阅页显示 CN 或 AMS 集群,请分别配置 `MIMO_TOKEN_CN_API_KEY` / `MIMO_TOKEN_AMS_API_KEY`,并使用 `mimo-token-cn``mimo-token-ams``tp-*` Token Plan Key 不能用于按量付费接口,`sk-*` 按量付费 Key 也不能用于 Token Plan 接口。
250260

251261
### 配置文件 (models.yaml)
252262

253263
可以自定义供应商、模型变体、别名等。运行 `init` 命令会生成 `providers -> variants` 结构的模板;旧版平铺 `models` 配置仍然兼容。
254264

255-
网关运行中修改 `models.yaml``.env`**自动热重载**(轮询检测,约 1 秒生效),加模型、换 Key 都不用重启。**即使网关启动时这些文件还不存在**,之后创建也会被捕获(v1.8.1 起)。注意:`gateway.host` / `gateway.port` 变更仍需重启才能重新绑定。
265+
网关运行中修改 `models.yaml``.env`**自动热重载**(轮询检测,约 1 秒生效),新增、轮换或删除 Key 都会生效;入站鉴权令牌也可热轮换。即使文件启动时不存在,之后创建也会被捕获。注意:`gateway.host` / `gateway.port` 变更仍需重启才能重新绑定。
256266

257267
排查某个网关到底在用哪份配置:
258268

@@ -399,9 +409,10 @@ npx claude-code-model-router claude
399409
| 端点 | 方法 | 说明 |
400410
|------|------|------|
401411
| `/v1/messages` | POST | Anthropic Messages API |
412+
| `/v1/messages/count_tokens` | POST | 转发到所选供应商的兼容 token counting 端点 |
402413
| `/v1/models` | GET | 列出可用模型 |
403414
| `/usage` | GET | 按模型的用量统计(请求数 / 错误数 / tokens,网关重启后清零) |
404-
| `/health` | GET | 健康检查(含网关版本号、`pid``config_file` 配置来源、`ccmr_home`、各模型 Key 状态) |
415+
| `/health` | GET | 健康检查;本机或已认证请求额外返回 PID、配置来源和模型状态 |
405416

406417
## 开发
407418

@@ -419,6 +430,9 @@ npm run dev
419430
# 构建
420431
npm run build
421432

433+
# 类型检查、测试、冒烟测试与依赖审计
434+
npm run check
435+
422436
# 本地测试
423437
npm link
424438
ccmr start
@@ -458,6 +472,17 @@ DeepSeek Anthropic 兼容接口会忽略 `metadata` 字段,但某些 Claude Co
458472

459473
## 更新日志
460474

475+
### v1.8.3
476+
477+
- 阻止 detached 网关跨项目复用错误的配置、端点或 API Key
478+
- 流式转发改用增量 UTF-8 解码,兼容 CRLF SSE;客户端断开会取消上游请求并处理背压
479+
- `.env` 删除 Key 后会真正撤销,入站鉴权支持热轮换;内置客户端统一使用相同令牌
480+
- 非回环无鉴权监听改为默认拒绝,远程 `/health` 隐藏本机路径、PID 与 Key 状态
481+
- `ccmr stop` 增加本机随机身份记录校验,不再只信任 HTTP 自报 PID
482+
- 显式配置路径、YAML、端口、超时和 CLI 数值参数改为严格校验;YAML/env/CLI 优先级保持一致
483+
- 实现兼容 `/v1/messages/count_tokens` 转发,并执行 streaming/tools 能力声明
484+
- 修复流式断连继续计费、生产依赖漏洞和缺失 LICENSE;CI/发布前新增依赖审计
485+
461486
### v1.8.2
462487

463488
- **新增 `ccmr status`**:列出本机正在运行的 ccmr 网关及其端口、PID、版本、配置来源、可用模型数。`ccmr claude` 自动拉起的网关是 detached 进程(关掉终端后继续运行),此前只能靠 `lsof` 才能找到它
@@ -497,7 +522,7 @@ DeepSeek Anthropic 兼容接口会忽略 `metadata` 字段,但某些 Claude Co
497522
- 配置热重载遇到损坏的 YAML 时保留旧配置继续运行,不再静默退化为默认配置
498523

499524
**工程化**
500-
- 引入 vitest 测试体系(69 个测试):路由解析 / URL 构建 / SSE 转发 / 鉴权 / 热重载 / failover / 用量统计,以及 DEFAULT_CONFIG 与 YAML 模板的一致性测试(当场修复了模板中 `default_variant: 5.2` 未加引号、缺失 `kimi-k2.6` 别名两处漂移)
525+
- 引入 vitest 测试体系:路由解析 / URL 构建 / SSE 转发 / 鉴权 / 热重载 / failover / 用量统计,以及 DEFAULT_CONFIG 与 YAML 模板的一致性测试
501526
- 新增 GitHub Actions CI:Node 18/20/24 矩阵构建 + 测试 + dist 一致性校验
502527

503528
### v1.7.1
@@ -557,4 +582,4 @@ DeepSeek Anthropic 兼容接口会忽略 `metadata` 字段,但某些 Claude Co
557582

558583
## License
559584

560-
MIT
585+
[MIT](LICENSE)

0 commit comments

Comments
 (0)