# workbuddy 评审反馈 — 2026-06-14 P0 安全止血

**推送内容**: WS token 鉴权改造 + 坐席本地密码 + secret 管理规划文档
**评审日期**: 2026-06-14
**评审人**: Claude
**主报告**: `D:\资料\03-项目开发\wecom_it_smart_desk\docs\评审报告\workbuddy-2026-06-14-P0安全.md`
**commit**: 3735dc0 (本地 main,未推 Gitea)

---

## ⭐ 给 workbuddy 的关键反馈(高优先级)

1. **🔴 浏览器 WebSocket API 不支持自定义 header** — 误用 Node.js `ws` 库的 options.headers
2. **🔴 nginx access_log 没关** — 即使前端修好,token 仍经 access_log 泄露
3. **🟡 Mapped[str] + nullable=True 类型不一致** — 改 Optional[str]
4. **🟡 企微降级放行仍能绕过 password 验证** — P0-#5 被反削弱
5. **🟡 requirements.txt 缺 passlib** — 部署会 ImportError

## 🔴 遗留 5 项(下一轮必修)

| # | 严重度 | 文件 | 修复要点 |
|---|---|---|---|
| 1 | 🔴 P0 | `frontend-agent/.../useWebSocket.ts:106-110` | 改 `new WebSocket(wsUrl, [\`bearer.${token}\`])` + 服务端从 `sec-websocket-protocol` 取 |
| 2 | 🔴 P0 | `nginx.conf` + `deploy-server/nginx.conf` | 加 `location /ws/ { access_log off; }` |
| 3 | 🟡 P1 | `backend/app/models/agent.py:142-148` | `Mapped[str]` → `Mapped[Optional[str]]` |
| 4 | 🟡 P1 | `backend/app/api/agents.py` 降级放行 | 检测 `agent.password_hash` 存在 → 强制 password |
| 5 | 🟡 P1 | `backend/requirements.txt` | 加 `passlib[bcrypt]==1.7.4` 或改用原生 `bcrypt==4.1.2` |

## 🟢 评审验收

- ✅ ws.py 服务端:header 优先 + query 降级,**逻辑正确**
- ✅ model 字段定义:`password_hash` String(128) nullable,**结构 OK**(类型注解除外)
- ✅ schema:`AgentLogin.password` + `AgentPasswordUpdate`,**OK**
- ✅ 改密端点 `POST /agents/password`:走 `Depends(get_current_agent)`,**OK**
- ✅ alembic 008:down_revision='007_role_system' 正确,**OK**
- ✅ docs/安全/secret-管理.md:**作为规划文档 OK**

## 📊 完成度

| 任务 | 完成 |
|---|---|
| P0-#1 WECOM_SECRET 集中化 | 🟡 仅规划文档 |
| P0-#2 SSL 私钥在仓 | 🟢 之前已修(8-A 阶段) |
| P0-#3 Mock login | 🟢 之前已修 |
| P0-#4 WS token URL/日志 | 🟡 半成品(服务端 OK,前端 + nginx 待关) |
| P0-#5 坐席本地密码 | 🟡 半成品(模型/Schema/端点 OK,类型 + 降级 + 依赖) |

**整体**: 2/5 P0 真正完成,3 项遗留待下一轮。

## 🔁 流程建议

- 推送前自检清单:
  - [ ] 浏览器 WebSocket API 边界(不要用 `ws` 库的 options.headers)
  - [ ] nginx/conf 改动 plan 写了就必须做
  - [ ] Mapped[T] + nullable=True 必须用 Optional
  - [ ] 改代码必须同步 requirements.txt
  - [ ] 加新鉴权必须 review 已有降级路径是否被绕过
- **强烈建议**: workbuddy 推送前先回答"我的改动在浏览器侧能跑吗?"(不要假设 Node.js API = 浏览器 API)

## 🔗 推 Gitea 状态

- **本地 commit 3735dc0**: ✅ 已存
- **推 Gitea**: 🔴 卡 #8(MariaDB 套件未装)
- **下次**: Gitea 起来后 `git push -u origin main` 推 → workbuddy 拿 Gitea URL 二次评审
