# workbuddy 任务 — 修 P0 安全评审遗留 5 项 **触发日期**: 2026-06-14 **来源**: Claude 评审(主报告: `docs/评审报告/workbuddy-2026-06-14-P0安全.md`) **Gitea 仓(公网 Funnel URL)**: `https://ds923plus.tail58d872.ts.net/simon/wecom_it_smart_desk` **Gitea 仓(内网 LAN,快)**: `http://100.85.152.112:8418/simon/wecom_it_smart_desk` **当前 HEAD**: `9b6f477` (含 workbuddy 任务清单) **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 仓: `https://ds923plus.tail58d872.ts.net/simon/wecom_it_smart_desk` (公网 Funnel)