docs: 审计+changelog 全站bug审查修复

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-06-01 11:40:23 +08:00
parent 6183047fd6
commit a20cefcfbd
2 changed files with 176 additions and 0 deletions
+99
View File
@@ -0,0 +1,99 @@
# 审计记录:全站 bug 审查修复 (前端9项 + 后端5项)
## 审计信息
- **日期**: 2026-06-01
- **审计人**: Claude (AI)
- **触发原因**: 全站巡检 bug 审查
## 审计范围
| 文件 | 变更类型 | 状态 |
|------|----------|------|
| frontend/src/App.vue | 1行导入修复 | ☑ 已审 |
| frontend/src/pages/LoginPage.vue | TOTP流程+锁定倒计时 | ☑ 已审 |
| frontend/src/pages/ServersPage.vue | domain端口污染修复 | ☑ 已审 |
| frontend/src/pages/PushPage.vue | Set索引修复 | ☑ 已审 |
| frontend/src/pages/SettingsPage.vue | 类型转换修复 | ☑ 已审 |
| frontend/src/api/index.ts | 删除重复定义 | ☑ 已审 |
| frontend/src/pages/TerminalPage.vue | 路径验证正则 | ☑ 已审 |
| frontend/src/composables/useWebSocket.ts | 连接泄漏修复 | ☑ 已审 |
| server/api/websocket.py | redis.keys→scan_iter | ☑ 已审 |
| server/application/services/auth_service.py | TTL重置修复 | ☑ 已审 |
| server/api/servers.py | 并发session lock | ☑ 已审 |
| server/api/settings.py | 弃用API修复 | ☑ 已审 |
## 审计8步结果
### Step 1: 登记 ✅
commit 6183047: fix: 全站 bug 审查修复 (前端9项 + 后端5项)
### Step 2: 全文Read ✅
所有12个文件逐行审读完毕
### Step 3: 规则扫描H
| H# | 规则 | 文件 | 行 | 说明 |
|----|------|------|-----|------|
| H1 | 硬编码密钥 | 全部 | - | 无命中 |
| H2 | f-string拼SQL | 全部 | - | 无命中 |
| H3 | 静默吞错 | 全部 | - | 无新增空catch |
| H4 | 未验证输入拼shell | 全部 | - | 无命中 |
| H5 | 明文密码前端 | 全部 | - | 无命中 |
| H6 | XSS | 全部 | - | 无新增v-html |
| H7 | JWT保护 | 全部 | - | 无新增未保护端点 |
| H8 | 审计日志 | 全部 | - | 无新增CUD操作 |
### Step 4: Closure表
| H# | 判定 | 依据 |
|----|------|------|
| H1-H8 | 无命中 | 本次修复均为bug修复,未新增端点/功能/密钥/XSS风险 |
### Step 5: 入口表
无新增入口。本次修复不改变API接口。
### Step 6: 输入→Sink
- **SettingsPage 数字转换**: 正则 `/^\d+(\.\d+)?$/` 匹配纯数字字符串 → `Number(val)` → 安全(仅转数字字符串为数字)
- **TerminalPage 路径正则**: `/^[^\x00-\x1f\x7f"']+$/.test(p)` → 白名单排除控制字符和引号 → 安全
- **PushPage Set取值**: `selectedIds.value.values().next().value` → 标准 ES6 迭代器 → 安全
### Step 7: 归类
```
frontend/src/App.vue 1行修改 0H 0FINDING
frontend/src/pages/LoginPage.vue 15行修改 0H 0FINDING
frontend/src/pages/ServersPage.vue 1行修改 0H 0FINDING
frontend/src/pages/PushPage.vue 1行修改 0H 0FINDING
frontend/src/pages/SettingsPage.vue 6行修改 0H 0FINDING
frontend/src/api/index.ts -4行删除 0H 0FINDING
frontend/src/pages/TerminalPage.vue 1行修改 0H 0FINDING
frontend/src/composables/useWebSocket.ts 5行修改 0H 0FINDING
server/api/websocket.py 3行修改 0H 0FINDING
server/application/services/auth_service.py 4行修改 0H 0FINDING
server/api/servers.py 4行新增 0H 0FINDING
server/api/settings.py 3行修改 0H 0FINDING
──────────────────────────────────────────────────────────
总计 0H 0FINDING
```
### Step 8: DoD ✅
- [x] 无新增硬编码密钥
- [x] 无新增f-string拼SQL
- [x] 无新增空catch
- [x] 无新增XSS风险
- [x] 无新增未保护端点
- [x] SettingsPage 数字转换仅对纯数字字符串生效
- [x] TerminalPage 路径验证排除控制字符和引号
- [x] servers.py db_lock 使用 asyncio.Lock 正确序列化并发 commit
## FINDING 列表
## 结论
☑ 审计通过,0 FINDING,可部署
@@ -0,0 +1,77 @@
# Changelog: 全站 bug 审查修复 (前端9项 + 后端5项)
**日期**: 2026-06-01
**变更摘要**: 全站巡检发现并修复 14 个 bug1 P0 + 9 P1 + 4 P2),涵盖前端全局搜索崩溃、TOTP登录流程断裂、服务器编辑域名污染、后端Redis阻塞、并发session竞态等
## 动机
对 Nexus 全站进行系统性 bug 审查,发现多个影响生产使用的问题。最严重的 3 个:全局搜索因 `http` 未导入直接崩溃、TOTP 双因素登录完全不可用、编辑服务器后域名被端口字符串污染导致后续连接失败。
## 涉及文件
| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `frontend/src/App.vue` | 修改 | 添加 `http` 导入 |
| `frontend/src/pages/LoginPage.vue` | 修改 | TOTP异常捕获 + 锁定倒计时响应式 |
| `frontend/src/pages/ServersPage.vue` | 修改 | 编辑时 domain 不再拼接端口 |
| `frontend/src/pages/PushPage.vue` | 修改 | Set 索引 → 迭代器 |
| `frontend/src/pages/SettingsPage.vue` | 修改 | API字符串值转数字 |
| `frontend/src/api/index.ts` | 修改 | 删除重复 getList 定义 |
| `frontend/src/pages/TerminalPage.vue` | 修改 | 路径验证正则放松 |
| `frontend/src/composables/useWebSocket.ts` | 修改 | 关闭旧连接再建新连接 |
| `server/api/websocket.py` | 修改 | redis.keys → scan_iter |
| `server/application/services/auth_service.py` | 修改 | TTL 仅在无TTL时设置 |
| `server/api/servers.py` | 修改 | 批量操作加 asyncio.Lock |
| `server/api/settings.py` | 修改 | 弃用API替换 |
## 详细变更
### 前端 P0 修复
1. **App.vue `http` 未导入** — 第320行 `doSearch()` 调用 `http.get()` 但只导入了 `api`,导致 `ReferenceError: http is not defined`。修复:`import { api, http } from '@/api'`
### 前端 P1 修复
2. **TOTP 登录流程断裂**`api()` 在后端返回 202 时抛 `TotpRequiredError`(非 `ApiError` 子类),`LoginPage` catch 只检查 `e instanceof ApiError`TOTP 用户看到"网络错误"无法登录。修复:导入 `TotpRequiredError` 并在 catch 中优先检查
3. **ServersPage 编辑域名污染**`editServer()` 设置 `form.address = domain:port`,保存时 `domain: form.address` 发送含端口字符串。修复:`form.address = item.domain`
4. **PushPage Set 索引无效**`selectedIds.value[0]``Set<number>` 返回 `undefined`。修复:`selectedIds.value.values().next().value`
5. **SettingsPage 类型转换** — API 返回 `value: "80"` 字符串直接赋值给 number 字段。修复:正则检测纯数字字符串并 `Number()` 转换
### 前端 P2 修复
6. **api/index.ts 重复定义**`getList` 方法定义两次,删除第二个
7. **LoginPage 锁定倒计时**`remainingMinutes` computed 依赖 `Date.now()` 非响应式。修复:使用 `ref(Date.now())` + `watch(lockoutUntil)` + 30秒定时器
8. **TerminalPage 路径验证过严** — 正则 `/^[a-zA-Z0-9/._\-]+$/` 排除空格/中文/~。修复:`/^[^\x00-\x1f\x7f"']+$/` 仅排除控制字符和引号
9. **useWebSocket 连接泄漏** — CONNECTING/CLOSING 状态时创建新连接但不关闭旧连接。修复:关闭旧连接后再创建新的
### 后端 P1 修复
10. **websocket.py redis.keys() 阻塞**`KEYS` 命令 O(N) 扫描整个键空间。修复:`scan_iter(match="ws:count:*")` 非阻塞迭代
11. **auth_service.py TTL 重置** — 每次 `sadd``expire` 重置 TTL,频繁刷新的管理员 refresh token 键永不过期。修复:`ttl = await redis.ttl(key)`,仅在 `ttl < 0` 时设置 expire
12. **servers.py 并发 session commit** — 批量操作中 `asyncio.gather` 并发协程共享同一 db session 并 `commit()`。修复:添加 `asyncio.Lock()` 序列化 commit
### 后端 P2 修复
13. **settings.py asyncio.get_event_loop()** — Python 3.10+ 弃用。修复:`asyncio.get_running_loop()`
14. **settings.py datetime.utcfromtimestamp()** — Python 3.12+ 弃用。修复:`datetime.fromtimestamp(ts, tz=timezone.utc)`
## 是否需迁移/重启
- ✅ 需重启 — 后端代码变更
- ✅ 前端需重新构建部署
## 验证方式
1. 登录页 → 输入 admin/Nexus@2026 → 成功跳转仪表盘 ✓
2. 全局搜索框 → 输入"机器人" → 无崩溃(之前会 ReferenceError)✓
3. 控制台零错误 ✓
4. 健康检查 → `ok`