Files
wecom_it_smart_desk/docs/01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.1.1.md
T
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

251 lines
12 KiB
Markdown
Raw 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.
# PRD - 敏感词检测 v1.1.1(v1.1 安全补漏:13 端点鉴权修复)
> **需求编号**: REQ-通用-004
> **版本**: v1.1.1v1.1 的安全补丁 / PATCH 级别)
> **状态**: [待评审]
> **作者**: 宋献 / Duckula
> **日期**: 2026-08-05
> **前置版本**:
> - PRD v1.0v0.7.1 上线)→ `.v1.0.archive.md`
> - PRD v1.1DB化 + 后台 UI + 审计日志)→ **未单独成文**,仅见任务说明书 `任务说明书-03-v1.1-...v1.1.archive.md`
> - **PRD v1.2 草案(AI 辅助运营)** → `PRD-REQ-通用-004-敏感词检测-v1.2-AI辅助.md`(独立演进路线,**与本补丁无关**,不互相阻塞)
> **关联缺陷单**: `docs/03-测试文档/05-缺陷单/BUG-通用-004-敏感词API无鉴权-001.md`P0-Critical
> **关联文档**:
> - 技术方案 v1.0(已归档):`docs/02-技术文档/技术架构/技术方案-REQ-通用-004-敏感词检测-v1.0.archive.md`
> - 技术方案 v1.1.1(本补丁):`docs/02-技术文档/技术架构/技术方案-REQ-通用-004-敏感词检测-v1.1.1.md`
> - 测试用例:`docs/03-测试文档/03-功能测试用例/TC-通用-004-敏感词检测.md`(在 v1.0 基础上加 §10 鉴权章节)
> - 任务说明书:`docs/07-项目管理/任务说明书/任务说明书-REQ-通用-004-敏感词检测-v1.1.1.md`
> - 整改记录:`docs/04-运维文档/部署运维/00-文档规范化整改记录.md`(#5 整改)
> - 源码:`src/backend/app/api/admin/sensitive_words.py`
---
## 1. 需求描述
### 1.1 背景
v1.12026-07-28 上线)实现了敏感词词库入库、隐私正则入库、后台管理 UI(4 Tabs)、命中审计日志。同步落地的 13 个 HTTP 端点全部位于 `/api/admin/sensitive-words``/api/admin/privacy-patterns``/api/admin/moderation-logs``/api/admin/moderation-config` 命名空间下。
**PRD v1.0 §5.2 与技术方案 v1.0 §6.3 已明确声明**:这 11+ 个端点的"权限:admin"。
但 v1.1 实施时 `src/backend/app/api/admin/sensitive_words.py``APIRouter` 创建时**未声明 `dependencies=[Depends(require_admin)]`**,且每个 `@router.xxx` 端点装饰器也未逐个挂 `Depends(require_admin)`,导致 13 端点全部无鉴权。
### 1.2 漏洞影响(5 个维度)
| 维度 | 具体影响 |
|---|---|
| **合规/个保法** | `/api/admin/moderation-logs` 含 message_id / agent_id / matched_words / text_excerpt(员工消息前 100 字),任意登录用户可拉取全公司员工与坐席对话的隐私片段 |
| **业务/防线瓦解** | 任意 token 可增删改敏感词、上传恶意正则、强制 `/sensitive-words/reload` 热加载;攻击者针对性规避词库可让整条内容审核防线失效 |
| **可用性/DoS** | `/privacy-patterns/{id}/test` 是任意人可用的正则测试器,提交 `(a+)+$` 类灾难回溯正则触发 ReDoS`/sensitive-words/reload` 高频调用可拖垮 DB + 后端 |
| **审计链断裂** | service 层未记录 operator_id/operator_name,事故排查无法追责恶意删除/篡改者 |
| **横向越权** | 即便其他 admin 端点有 require_admin,这 13 端点构成"鉴权盲区下的 admin 通道",成为后续攻击跳板 |
### 1.3 v1.1.1 目标
| 目标 | 描述 |
|---|---|
| **G1 全覆盖鉴权** | 13 端点全部要求 `agent.role == "admin"`;非 admin 统一抛 AppException(1004, "无管理权限") |
| **G2 最小代码变更** | 一行 APIRouter 配置 + 一行 import,覆盖全部 13 端点,不引入新依赖 |
| **G3 文档同步** | 按 product-doc-standard 铁律,PRD / 技术方案 / 任务说明书 / TC / BUG 单 / 整改记录 全部到位 |
| **G4 与 v1.2 AI 化解耦** | 本补丁不影响在评审中的 v1.2 AI 辅助运营路线,二者可独立部署 |
### 1.4 范围
| 范围项 | 状态 | 说明 |
|---|---|---|
| sensitive_words 6 端点鉴权 | ✅ 必做 | 列表 / 新增 / 更新 / 删除 / 测试 / 重载 |
| privacy_patterns 4 端点鉴权 | ✅ 必做 | 列表 / 新增 / 更新 / 正则测试器 |
| moderation_logs 2 端点鉴权 | ✅ 必做 | 列表 / 统计 |
| moderation_config 1 端点鉴权 | ✅ 必做 | 全局配置 |
| operator_id / operator_name 审计字段 | ❌ 不在本补丁 | 列入 v1.1.2 或 v1.2 路线 |
| 词库防注入(如正则复杂度限制) | ❌ 不在本补丁 | 列入 v1.1.2 |
| AI 辅助运营(v1.2 草案) | ❌ 不在本补丁 | 独立路线,互不阻塞 |
### 1.5 Non-goals
| 不做 | 原因 |
|---|---|
| 重写词库管理 service | v1.1 service 层逻辑正确,仅缺鉴权边界 |
| 引入新依赖(如 fastapi-users | 一行 `Depends` 已解决,避免库膨胀 |
| 给非 admin 开放"只读"权限 | 当前所有 admin 端点都是 admin 独占,鉴权分层会带来新的越权风险面 |
| 词库操作审计(operator_id | 跨 service 改动,列入后续版本 |
| 与 v1.2 AI 化合并 | 不同维度,避免变更爆炸 |
---
## 2. 用户故事
| 优先级 | 用户故事 |
|---|---|
| **P0** | 作为系统安全边界,**非 admin 任何 HTTP 调用必须被 401/403 拦截**,包括坐席、员工、任何持有 token 的用户 |
| **P0** | 作为管理员,我能正常调用 13 端点完成词库管理,不受新鉴权影响 |
| P1 | 作为审计员,我能从 `moderation_logs` 看到操作人(**v1.1.1 不做,留 v1.1.2** |
| P1 | 作为运维,我能 grep `Depends(require_admin)` 在 sensitive_words.py 中至少出现 1 次 |
---
## 3. 功能需求
### 3.1 鉴权补漏(v1.1.1 唯一功能点)
#### 3.1.1 修改文件
`src/backend/app/api/admin/sensitive_words.py` 共 2 处变更:
1. **顶部 imports**L33-41 之后)新增:
```python
from app.api.admin_api import require_admin
```
2. **APIRouter 创建**L44)由:
```python
router = APIRouter(prefix="/admin", tags=["敏感词管理(v1.1"])
```
改为:
```python
router = APIRouter(
prefix="/admin",
tags=["敏感词管理(v1.1"],
dependencies=[Depends(require_admin)], # v1.1.1 鉴权补漏
)
```
#### 3.1.2 鉴权行为对齐
| 输入 | 期望 | HTTP 码 | 错误码 |
|---|---|---|---|
| 无 Authorization 头 | 拦截 | 401 | - |
| Bearer token 无效 | 拦截 | 401 | - |
| Bearer token 有效但 `agent.role != "admin"` | 拦截 | 403 | 1004 "无管理权限" |
| Bearer token 有效且 `agent.role == "admin"` | 通过 | 200 | - |
#### 3.1.3 影响范围声明
| 端点 | 原行为 | v1.1.1 行为 |
|---|---|---|
| `/api/admin/sensitive-words` (GET/POST/PUT/DELETE/test/reload) | 任意 token 通过 | 仅 admin 通过 |
| `/api/admin/privacy-patterns` (GET/POST/PUT/{id}/test) | 任意 token 通过 | 仅 admin 通过 |
| `/api/admin/moderation-logs` (GET/stats) | 任意 token 通过 | 仅 admin 通过 |
| `/api/admin/moderation-config` (GET) | 任意 token 通过 | 仅 admin 通过 |
---
## 4. 非功能需求
| 维度 | 要求 |
|---|---|
| 性能 | 鉴权检查开销 < 1msDepends 缓存 + JWT 本地解析) |
| 兼容性 | 不破坏 v1.1 已有 admin 用户的工作流;admin 调用 13 端点全部仍返回 200 |
| 可回滚 | 一行代码回滚即可(删除 `dependencies=`);无需 DB 迁移 |
| 可测试 | 增加 6 条鉴权用例(无 token / agent / admin / 重载 / 正则测试 / 审计列表) |
---
## 5. 接口需求
### 5.1 接口契约(不变)
13 端点的请求 / 响应 schema 全部不变。仅在 handler 执行前增加一道鉴权门。
### 5.2 错误响应统一
| 场景 | 响应体 |
|---|---|
| 401 | FastAPI 默认 |
| 403 | `{"code": 1004, "message": "无管理权限", "data": null}`(与 `admin_api.py` 一致) |
---
## 6. 数据需求
**无 DB 变更**。
---
## 7. 风险与降级
| 风险 | 等级 | 降级措施 |
|---|---|---|
| admin token 过期导致管理员误锁 | 🟡 中 | 鉴权依赖 `get_current_agent`token 过期返回 401 不是 403,前端可正常重登录 |
| require_admin 与其他依赖冲突 | 🟢 低 | 一行 import 已验证存在;`admin_api.py:50` 已有定义 |
| 修复后 admin 操作流程未及时验证 | 🟡 中 | 容器内端到端 curl 13 端点必做(见验收 §8) |
| 与 v1.2 AI 化部署冲突 | 🟢 低 | 本补丁独立部署,不动 AI 工作流;二者可以任意顺序上线 |
---
## 8. 验收标准
### 8.1 必达项(v1.1.1 必做)
- [ ] `src/backend/app/api/admin/sensitive_words.py` 含 `Depends(require_admin)` 至少 1 次
- [ ] `APIRouter` 配置含 `dependencies=[Depends(require_admin)]`
- [ ] 容器内 grep `require_admin` 在 sensitive_words.py 命中
- [ ] 用普通坐席(role=agenttoken curl 13 端点 → 全部返回 403 + `code:1004`
- [ ] 用 admin token curl 13 端点 → 全部返回 200(回归)
- [ ] 无 token curl 13 端点 → 全部返回 401
- [ ] TC-通用-004 §10 新增鉴权用例 6 条全部通过
- [ ] 源码 BUG-通用-004-001 缺陷单"待修复"状态变更为"已关闭"
- [ ] 整改记录 #5 已追加
### 8.2 回归项(v1.1 既有功能不受影响)
- [ ] admin 调用 `/api/admin/sensitive-words` GET 仍返回完整词库
- [ ] admin 调用 `/api/admin/sensitive-words/reload` 仍可热加载
- [ ] admin 调用 `/api/admin/privacy-patterns/{id}/test` 仍可测试正则
- [ ] TC-通用-004 既有 23/31 通过用例不变
---
## 9. 实施路线(单点修复)
| 步骤 | 耗时 | 输出 |
|---|---|---|
| 改 sensitive_words.py2 行) | 2 min | 提交 `[BUG-通用-004]` commit |
| 新增 test_sensitive_words_auth.py6 条用例) | 15 min | pytest 通过 |
| docker compose restart backend | 1 min | 服务重启 |
| 容器内端到端 curl 验证(无 token / agent / admin | 10 min | 三组 HTTP 响应证据 |
| 源码 grep 验证 | 1 min | `grep require_admin sensitive_words.py` 输出 |
| BUG 单状态变更 + commit | 2 min | 已关闭 |
| **总计** | **~30 min** | |
---
## 10. 关联文档
| 文档 | 位置 | 关联点 |
|---|---|---|
| 前置 PRD v1.0 | `01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.0.archive.md` | v0.7.1 上线基础功能 |
| 前置技术方案 v1.0 | `02-技术文档/技术架构/技术方案-REQ-通用-004-敏感词检测-v1.0.archive.md` | §6.3 路由层表格已规定 admin 权限 |
| 前置任务说明书 v1.1 | `07-项目管理/任务说明书/任务说明书-03-v1.1-敏感词词库入库+后台UI.v1.1.archive.md` | v1.1 实施记录(**实施时漏加鉴权**) |
| PRD v1.2 草案(AI 化) | `01-产品文档/00-产品规划/PRD-REQ-通用-004-敏感词检测-v1.2-AI辅助.md` | 独立演进路线,**与本补丁无关** |
| 缺陷单 | `03-测试文档/05-缺陷单/BUG-通用-004-敏感词API无鉴权-001.md` | 触发本补丁 |
| 测试用例 | `03-测试文档/03-功能测试用例/TC-通用-004-敏感词检测.md` | 加 §10 鉴权章节 |
| 整改记录 | `04-运维文档/部署运维/00-文档规范化整改记录.md` | #5 整改条目 |
| 源鉴权依赖 | `src/backend/app/api/admin_api.py:50` | `require_admin` 定义参考 |
| 对照 admin 路由 | `src/backend/app/api/admin_api.py:50,76,101...` | 已加 require_admin 的同类实现 |
| 数据库迁移 | `src/backend/alembic/versions/056_add_moderation_tables.py` | v1.1 词库入库迁移(与本补丁无关) |
---
## 11. 变更日志
| 版本 | 日期 | 变更 | 变更人 |
|---|------|------|------|
| v1.0 | 2026-07-28 | 首次整理:v0.7.1 上线内容回溯为正式 PRD | 宋献 |
| v1.1 | 2026-07-28 | DB化 + 后台 UI + 审计日志 + 灰度开关(**实施时漏加鉴权**,BUG-通用-004) | 宋献 |
| **v1.1.1** | **2026-08-05** | **PATCH 级别安全补漏:13 端点全部 require_admin;与 v1.2 AI 化草案解耦** | **宋献 / Duckula** |
---
## 12. 备注
> **关键决策记录**
> - 2026-07-08:命中动作固定 WARNv1.0 决策,**保留**
> - 2026-07-28v1.1 上线(DB化),**实施时漏加鉴权**(BUG-通用-004)
> - 2026-07-28v1.2 AI 辅助运营草案(**独立演进路线,本补丁不阻塞**)
> - **2026-08-05v1.1.1 安全补漏上线**13 端点恢复 admin-only 访问;operator_id 审计列入 v1.1.2 或 v1.2 跟进
> **教训(写入产品文档规范候选铁律)**:
> - 任何 `APIRouter(prefix="/admin", ...)` **必须**显式声明 `dependencies=[Depends(require_admin)]`,除非有显式豁免(如审计 webhook)
> - 任务说明书 §5"完成标准"必须包含"鉴权维度验收",至少 1 条"非 admin 调用 → 401/403"用例
> - 测试用例 §10 鉴权维度必须独立成章,不能仅作功能测试附注