Files
wecom_it_smart_desk/docs/03-测试文档/05-缺陷单/BUG-通用-004-敏感词API无鉴权-001.md
Simon facc04aa65 chore: docs 结构整改 + compose 双目录对齐(合并重建提交)
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交
(5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。

一、docs 结构整改(整改 #14)
根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入,
随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录
树并存于 docs/,共 791 文件、双分类体系冲突。

修复动作:
- b2 同名异主题文件改名迁移保全 9 个
- C 类 39 个孤立文件按主题正确归类
- A/B1 类 222 个重复文件删除(新结构已有内容副本)
- 9 个旧独有空目录删除
- 270 处内部引用按 verified 映射改写
- 整改记录 #14 登记于 04-运维文档/部署运维

结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。
残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。

二、compose 双目录对齐(消除踩坑 A)
- docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist
  改为 src/frontend-*/dist(h5 / agent / admin / terminal)
- docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/
- 效果:本地 docker compose up 不再把根目录 stale dist 挂回,
  与线上一致,分叉隐患消除(已 docker compose config 校验通过)

防复发铁律:
- 重构须提交;仓库修复须 git stash -u 或先 commit
- 新结构须 git add 并提交,避免再次 untracked 复活
- H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
2026-08-07 22:31:32 +08:00

238 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 缺陷单:敏感词/隐私正则/审计日志/命中配置 13 端点无鉴权(v1.1 实施漏加 require_admin
> **缺陷编号**: BUG-通用-004
> **版本**: v1.0
> **状态**: [待修复]
> **优先级**: P0-Critical(合规/安全)
> **发现日期**: 2026-08-05
> **发现人**: 宋献
> **指派人**: 宋献
> **修复人**: Duckula (AI助手)
> **关联需求**: REQ-通用-004(敏感词检测)
> **关联文档**:
> - PRD: `docs/01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.0.md`v1.2 待出)
> - 技术方案: `docs/02-技术文档/技术架构/技术方案-REQ-通用-004-敏感词检测-v1.0.md`v1.2 待出)
> - 任务说明书: `docs/07-项目管理/任务说明书/任务说明书-03-v1.1-敏感词词库入库+后台UI.md`v1.1 增量,即将归档为 .v1.1.archive.md
> - 源码: `src/backend/app/api/admin/sensitive_words.py`
> - 测试用例: `docs/03-测试文档/03-功能测试用例/TC-通用-004-敏感词检测.md`
> - 整改记录: `docs/04-运维文档/部署运维/00-文档规范化整改记录.md`(#5 整改记录)
---
## 1. 基本信息
| 字段 | 内容 |
|------|------|
| 缺陷标题 | `src/backend/app/api/admin/sensitive_words.py` 13 个端点全部未挂 `Depends(require_admin)`,任何能访问 `http://10.90.5.110:8000/api/admin/...` 的内部用户均可直接调用,等同 admin 权限裸奔 |
| 影响范围 | 13 端点全覆盖:敏感词 CRUD/测试/重载 + 隐私正则 CRUD/测试 + 审计日志列表/统计 + 命中动作配置 |
| 涉及模块 | 后端 APIbackend FastAPI, v1.1 新增路由) |
| 涉及文件 | `src/backend/app/api/admin/sensitive_words.py`(仅此 1 个文件,APIRouter 未声明 `dependencies=` |
| 触发条件 | 任意已登录用户(含坐席/普通员工)通过任何渠道获取 `Bearer token` 后直接调用这 13 个端点;或绕开前端直接 curl 后端 |
| 预期行为 | 调用 13 端点时若 `agent.role != "admin"`,应抛出 AppException(1004, "无管理权限")(与 `admin_api.py` 行为一致) |
| 实际行为 | 13 端点全部 200 OK 通过;任何 token 持有者拥有增删改查敏感词库、读取审计日志(含员工消息片段)、上传任意正则(可触发 ReDoS)、触发 `/sensitive-words/reload` 强制热加载词库的能力 |
### 1.1 端点清单(v1.1 实施,13 个)
| 类别 | 方法 | 路径 | 用途 |
|---|---|---|---|
| 敏感词 | GET | `/api/admin/sensitive-words` | 词库列表(分页+筛选) |
| 敏感词 | POST | `/api/admin/sensitive-words` | 新增词 |
| 敏感词 | PUT | `/api/admin/sensitive-words/{id}` | 更新词 |
| 敏感词 | DELETE | `/api/admin/sensitive-words/{id}` | 删除词(软删) |
| 敏感词 | POST | `/api/admin/sensitive-words/test` | 测试输入文本 |
| 敏感词 | POST | `/api/admin/sensitive-words/reload` | 强制从 DB 热加载词库 |
| 隐私正则 | GET | `/api/admin/privacy-patterns` | 列表 |
| 隐私正则 | POST | `/api/admin/privacy-patterns` | 新增 |
| 隐私正则 | PUT | `/api/admin/privacy-patterns/{id}` | 更新 |
| 隐私正则 | POST | `/api/admin/privacy-patterns/{id}/test` | 正则测试器(ReDoS 入口) |
| 审计日志 | GET | `/api/admin/moderation-logs` | 列表(含 message_id/agent_id/matched_words/text_excerpt |
| 审计日志 | GET | `/api/admin/moderation-logs/stats` | 统计 |
| 命中配置 | GET | `/api/admin/moderation-config` | 全局命中动作配置 |
---
## 2. 复现步骤
### 2.1 复现 1:词库列表裸奔(GET)
```bash
# 任意坐席账号(role=agent,非 admin)的 Bearer token
TOKEN="<任意普通坐席 token>"
curl -sS -X GET "http://10.90.5.110:8000/api/admin/sensitive-words?page=1&page_size=20" \
-H "Authorization: Bearer $TOKEN" -H "Accept: application/json"
```
**预期**HTTP 401/403,返回 `{"code": 1004, "message": "无管理权限"}`
**实际**:HTTP 200,返回完整词库(含 profanity/politics/porn 等所有分类与 severity
### 2.2 复现 2:审计日志裸奔(GET)
```bash
curl -sS -X GET "http://10.90.5.110:8000/api/admin/moderation-logs?page=1&page_size=20" \
-H "Authorization: Bearer $TOKEN"
```
**预期**HTTP 401/403
**实际**HTTP 200,返回 `[{agent_id, matched_words, category, action, text_excerpt, created_at}, ...]`,含员工与坐席对话内容片段
### 2.3 复现 3:词库篡改(POST/PUT/DELETE
```bash
# 任意 token 即可删除核心拦截词
curl -sS -X DELETE "http://10.90.5.110:8000/api/admin/sensitive-words/1" \
-H "Authorization: Bearer $TOKEN"
# 实际:HTTP 200is_active=false,词条立即全局失效
```
### 2.4 复现 4:正则测试器 ReDoSPOST
```bash
curl -sS -X POST "http://10.90.5.110:8000/api/admin/privacy-patterns/1/test" \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"pattern": "(a+)+$", "text": "aaaaaaaaaaaaaaaaaaaab"}'
# 实际:HTTP 200,触发 catastrophic backtrackingCPU 100% 数秒
```
### 2.5 复现 5:热加载触发(POST /reload
```bash
curl -sS -X POST "http://10.90.5.110:8000/api/admin/sensitive-words/reload" \
-H "Authorization: Bearer $TOKEN"
# 实际:HTTP 200,全量重新加载词库到内存;高频调用可拖垮 DB + 后端
```
---
## 3. 根因分析
### 3.1 实现层:APIRouter 未声明 `dependencies`
`sensitive_words.py:44` 创建 router 时未附加 `dependencies=[Depends(require_admin)]`
```python
# ❌ 错误(当前实现)
router = APIRouter(prefix="/admin", tags=["敏感词管理(v1.1"])
# ✅ 正确(应改为)
from app.api.admin_api import require_admin
router = APIRouter(
prefix="/admin",
tags=["敏感词管理(v1.1"],
dependencies=[Depends(require_admin)], # 一行全覆盖 13 端点
)
```
### 3.2 对比层:同类 admin 路由全部正确
| 文件 | require_admin 覆盖 |
|---|---|
| `admin_api.py` | ✅ 每个端点 `Depends(require_admin)` |
| `admin_roles.py` | ✅ 已加 |
| `admin_users.py` | ✅ 已加 |
| `welcome.py` | ✅ 已加 |
| `quiz_admin.py` | ✅ 已加 |
| `troubleshooting_templates.py` | ✅ 已加 |
| **`admin/sensitive_words.py`** | ❌ **13 端点全部漏挂** |
### 3.3 规范层:v1.1 实施未走 spec.md 强制约束
- 技术方案 v1.0 §6.3 路由层表格**已明确列出**所有 11 个端点的"权限:admin"
- PRD v1.0 §5.2 路由层 API 计划清单同样声明 admin 权限
- v1.1 实施时(任务说明书 v1.1)未对照规范逐项实现
- 任务说明书也未在验收用例里写"13 端点必须 401" → 测试环节也漏了
### 3.4 三层根因(缺一不可)
| 层 | 问题 | 体现 |
|---|---|---|
| 规范 | v1.1 任务说明书验收清单缺"鉴权用例" | §5 输出成果要求未列鉴权维度 |
| 实现 | APIRouter 未声明 `dependencies` | sensitive_words.py:44 |
| 测试 | TC-通用-004 无鉴权章节 | 31 用例全在功能维度,无安全维度 |
---
## 4. 修复方案
### 4.1 最小修复(一行代码)
修改 `src/backend/app/api/admin/sensitive_words.py:44`
```python
# 顶部 imports 增加
from app.api.admin_api import require_admin
# router 创建增加 dependencies
router = APIRouter(
prefix="/admin",
tags=["敏感词管理(v1.1"],
dependencies=[Depends(require_admin)], # 13 端点全覆盖
)
```
### 4.2 文档同步(按 product-doc-standard 铁律)
| 文档 | 动作 |
|---|---|
| PRD v1.0 | 升级到 v1.2,加 §11 v1.1 增量 + §12 v1.2 安全补漏(鉴权) |
| 技术方案 v1.0 | 升级到 v1.2,§6.3 路由层表格的"权限"列追加"`+ ` 鉴权依赖:`Depends(require_admin)` |
| 任务说明书 v1.1 | 旧名 `.v1.1.archive.md` 归档;新建 v1.2 覆盖鉴权补漏 |
| TC-通用-004 | 末尾追加 §10 鉴权用例(4 条) |
| 整改记录 | 在 `00-文档规范化整改记录.md` 追加 #5 整改条目 |
| Bug 单 | 本文件 |
### 4.3 代码变更清单
| 文件 | 变更类型 | 内容 |
|---|---|---|
| `src/backend/app/api/admin/sensitive_words.py` | 修改 | imports 增加 `require_admin`router 加 `dependencies=[Depends(require_admin)]` |
| `src/backend/tests/` | 新增 | `test_sensitive_words_auth.py`(鉴权测试 6 条) |
---
## 5. 验证方式
### 5.1 单元/集成测试(自动化)
| 用例 | 预期 |
|---|---|
| 无 token 调用 13 端点 | 401 |
| 普通坐席(role=agent)调用 13 端点 | 403 + `code:1004 无管理权限` |
| 管理员(role=admin)调用 13 端点 | 200 |
| 重载/正则测试等写操作管理员调用 | 200 |
| 修复后源码 grep `Depends(require_admin)` | 应在 sensitive_words.py 出现至少 1 次 |
### 5.2 容器内端到端验证(修复后必做)
1. `docker compose restart backend`
2. `docker compose exec backend grep -n "require_admin" app/api/admin/sensitive_words.py`
3. 用普通坐席 token curl 13 端点 → 应全部 401/403
4. 用 admin token curl 13 端点 → 应全部 200
### 5.3 回归测试
- v1.1 既有 11 个功能测试用例全部通过
- TC-通用-004 §10 新增鉴权用例 4 条全部通过
---
## 6. 关联
| 类型 | 文档 |
|---|---|
| 规范 | `docs/00-产品开发流程与文档管理规范.md`(v1.9)§ 4.3 文档更新时机 + § 11 文档整改实践 |
| 需求 | REQ-通用-004 敏感词检测 |
| 上游实现 | `src/backend/app/services/content_moderation_service.py`v1.0 已上线审核服务) |
| 上游实现 | `src/backend/app/services/admin/sensitive_word_service.py`v1.1 词库入库 + 管理 service |
| 数据库迁移 | `src/backend/alembic/versions/056_add_moderation_tables.py` |
| 初始化数据 | `src/backend/scripts/init_moderation.sql` |
| 测试基线 | `src/backend/tests/test_content_moderation.py`13 用例基线) |
| 对照 admin 路由 | `src/backend/app/api/admin_api.py:50` require_admin 定义 |
---
## 7. 变更记录
| 日期 | 版本 | 变更内容 | 变更人 | 变更原因 | 影响范围 |
|------|------|----------|---------|----------|----------|
| 2026-08-05 | v1.0 | 首次登记:13 端点无鉴权缺陷 + 修复方案 + 文档同步清单 | 宋献 / Duckula | v1.1 实施时漏加 require_admin 依赖,违反 PRD v1.0 §5.2 + 技术方案 v1.0 §6.3 admin 权限约束 | 13 端点全覆盖;后端 FastAPI 安全维度补漏;PRD/技术方案/任务说明书升级 v1.2 |