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

12 KiB
Raw Blame History

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.mdP0-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.pyAPIRouter 创建时未声明 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. 顶部 importsL33-41 之后)新增:

    from app.api.admin_api import require_admin
    
  2. APIRouter 创建L44)由:

    router = APIRouter(prefix="/admin", tags=["敏感词管理(v1.1"])
    

    改为:

    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_agenttoken 过期返回 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.pyDepends(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 鉴权维度必须独立成章,不能仅作功能测试附注