diff --git a/.workbuddy/memory/2026-06-14-任务-修遗留.md b/.workbuddy/memory/2026-06-14-任务-修遗留.md new file mode 100644 index 0000000..774fc9c --- /dev/null +++ b/.workbuddy/memory/2026-06-14-任务-修遗留.md @@ -0,0 +1,139 @@ +# workbuddy 任务 — 修 P0 安全评审遗留 5 项 + +**触发日期**: 2026-06-14 +**来源**: Claude 评审(主报告: `docs/评审报告/workbuddy-2026-06-14-P0安全.md`) +**Gitea 仓**: `http://100.85.152.112:8418/simon/wecom_it_smart_desk` +**当前 HEAD**: `3735dc0` +**workbuddy token**: 见 `.workbuddy/config.json` 的 `gitea.token`(用户已配) + +--- + +## ▶▶▶ 任务清单(按严重度,5 项)起 + +### 🔴 1. [P0] 修 ws.ts:用 Sec-WebSocket-Protocol 携带 token + +**文件**: `frontend-agent/src/composables/useWebSocket.ts:106-110` + +**问题**: 当前代码: +```ts +ws = new WebSocket(wsUrl, [], { + headers: { Authorization: `Bearer ${agentStore.token}` } +}) +``` +浏览器原生 WebSocket API 第 3 参数 options 没有 `headers` 字段。**Chromium / Firefox / Safari 全部忽略**。token 实际**未发送**。 + +**修复**: +1. 前端改成: + ```ts + ws = new WebSocket(wsUrl, [`bearer.${agentStore.token}`]) + ``` +2. 服务端 `backend/app/api/ws.py` 改: + ```python + # 优先从 subprotocol 取 + subprotocol = websocket.headers.get("sec-websocket-protocol", "") + if subprotocol.startswith("bearer."): + token = subprotocol[7:] + else: + auth_header = request.headers.get("Authorization", "") + if auth_header.startswith("Bearer "): + token = auth_header[7:] + else: + token = request.query_params.get("token", "") + ``` +3. H5 端 `h5_websocket_endpoint` 同改 + +### 🔴 2. [P0] 加 nginx access_log 关闭 + +**文件**: +- `nginx.conf`(根目录) +- `deploy-server/nginx.conf` + +**修复**: 找 `location /api/` 段,在前后加: +```nginx +location /ws/ { + access_log off; +} +``` + +### 🟡 3. [P1] 修 model `Mapped[str]` 类型 bug + +**文件**: `backend/app/models/agent.py:142-148` + +**问题**: `Mapped[str]` + `nullable=True` + `default=None` 严格模式下 `None` 赋值会报错。 + +**修复**: +```python +from typing import Optional +... +password_hash: Mapped[Optional[str]] = mapped_column( + String(128), + nullable=True, + default=None, + comment="本地密码哈希(bcrypt)", +) +``` + +### 🟡 4. [P1] 修降级放行必须 password 验证 + +**文件**: `backend/app/api/agents.py` `agent_login` 函数(企微 API 不可达分支) + +**问题**: 走 "已注册坐席降级放行" 路径时,**不强制 password 验证**。P0-#5 加的 password 字段被绕过。 + +**修复**: 在降级放行分支检测: +```python +# 已有 agent 且 password_hash 存在 → 必须走 password 验证 +if agent and agent.password_hash: + if not body.password: + raise AppException(1011, "请输入本地密码") + if not bcrypt.verify(body.password, agent.password_hash): + raise AppException(1011, "本地密码错误") + # 通过后放行 +``` + +### 🟡 5. [P1] requirements.txt 缺 passlib 依赖 + +**文件**: `backend/requirements.txt` + +**问题**: `agents.py` 用了 `from passlib.hash import bcrypt`,但 requirements.txt **没加**。生产部署会 ImportError。 + +**修复**: 加一行: +``` +passlib[bcrypt]==1.7.4 +``` +或(推荐,passlib 2024 停维护): +``` +bcrypt==4.1.2 +``` +后者需同步改 `agents.py`: +```python +import bcrypt +# 哈希 +bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) +# 验证 +bcrypt.checkpw(password.encode('utf-8'), agent.password_hash.encode('utf-8')) +``` + +## ▼▼▼ 任务清单止 + +--- + +## 🔄 工作流(等 workbuddy 修完 5 项后) + +1. workbuddy 修完 → 提交 commit 到 Gitea +2. 通知 Claude 评审 +3. Claude 评审(2/5 改成 5/5 完成) +4. 合并 + 推 main +5. 关 #18 + +## 🔴 token 状态(用户已配) + +- 用户给 Claude 的 token: `255eeaf88b...`(已撤销请用户) +- workbuddy 自己的 token: `workbuddy-claude`(在 `.workbuddy/config.json`) +- 推 Gitea 走 HTTPS(SSH 2222 不可达 + .ssh 权限锁) + +## 关联 + +- 评审主报告: `docs/评审报告/workbuddy-2026-06-14-P0安全.md` +- 风险跟踪表: 第十节(5 项遗留追踪) +- Claude 记忆: `review-p0-security-2026-06-14.md` +- Gitea 仓: `http://100.85.152.112:8418/simon/wecom_it_smart_desk`