docs(workbuddy): 推 P0 安全评审 5 遗留任务清单
This commit is contained in:
@@ -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`
|
||||||
Reference in New Issue
Block a user