facc04aa65
本提交为 .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-*/
238 lines
10 KiB
Markdown
238 lines
10 KiB
Markdown
# 缺陷单:敏感词/隐私正则/审计日志/命中配置 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/测试 + 审计日志列表/统计 + 命中动作配置 |
|
||
| 涉及模块 | 后端 API(backend 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 200,is_active=false,词条立即全局失效
|
||
```
|
||
|
||
### 2.4 复现 4:正则测试器 ReDoS(POST)
|
||
|
||
```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 backtracking,CPU 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 | |