docs(03-02): 完成 Phase 3 Plan 03-02 + Milestone v1.0 收尾
- 03-02-SUMMARY.md 新建: AccessTokenMaskFilter + LOGGING + 修改记录条目落地, 9 truth × 32 项断言全 PASS, 2 处 Rule 1 auto-fix bug 文档化
- STATE.md 更新: 进度 100%, Phase 3 标记 Complete, Milestone v1.0 收尾, 决策日志补 [Plan 03-02] 7 条
- ROADMAP.md 更新: Milestone v1.0 标 ✅ 完结, Phase 3 / Plan 03-01+03-02 全部 ✓
- REQUIREMENTS.md 更新: CRED-06 标记 Done, traceability 表 Phase 3 行 Done, 覆盖率 6/6 全 Done
Milestone v1.0「通用凭据槽位 (APP ID + Access Token)」CRED-01 至 CRED-06 全部交付完成。
This commit is contained in:
parent
db4d5cf89d
commit
9965d0bcf0
@ -100,7 +100,7 @@
|
||||
- [x] **CRED-03** 管理端 GET `/api/v1/admin/credential-slot/`:admin token 鉴权(`admin_token:{token}` Redis key 体系);返回 `{ app_id, access_token: <masked>, updated_at }`,Access Token 仅返回末 4 位脱敏掩码
|
||||
- [x] **CRED-04** 管理端 PUT `/api/v1/admin/credential-slot/`:admin token 鉴权;接受 `{ app_id, access_token }` 全字段覆写更新;空记录场景自动 `get_or_create`;变更写入 `updated_at`
|
||||
- [x] **CRED-05** 客户端 GET `/api/credential-slot/`:user token 鉴权(`token:{token}` Redis key 体系,复用 `RedisTokenAuthentication`);**明文**返回 `{ app_id, access_token, updated_at }`(手机端 / 设备端实际调用第三方服务需要)
|
||||
- [ ] **CRED-06** Access Token 日志过滤:阿里云日志格式化器 / 自定义日志过滤器中识别 `access_token` 字段并脱敏,覆盖 PUT 请求体、admin GET 响应体两条最易泄露路径
|
||||
- [x] **CRED-06** Access Token 日志过滤:阿里云日志格式化器 / 自定义日志过滤器中识别 `access_token` 字段并脱敏,覆盖 PUT 请求体、admin GET 响应体两条最易泄露路径 ✓ Plan 03-02 完成(2026-05-08,commits 891a5ea / 35eb110 / 7a9e511 / db4d5cf;AccessTokenMaskFilter 4 正则覆盖 JSON / Pyrepr / URL query / 等号或冒号兜底;端到端 9 truth × 32 项断言全 PASS)
|
||||
|
||||
### 候选优先级(已转移自 brownfield 文档化阶段,本期不消化)
|
||||
|
||||
@ -142,11 +142,11 @@
|
||||
| CRED-02 Django Admin 注册(脱敏 + 隐藏新增按钮) | Phase 1 凭据槽位数据层 | Done(Plan 01-02,2026-05-07) |
|
||||
| CRED-03 管理端 GET(admin token,脱敏返回) | Phase 2 管理端读写接口 | Done(Plan 02-01 + 02-02,2026-05-07) |
|
||||
| CRED-04 管理端 PUT(admin token,全字段覆写 + get_or_create) | Phase 2 管理端读写接口 | Done(Plan 02-01 + 02-02,2026-05-07) |
|
||||
| CRED-05 客户端 GET(user token,明文返回) | Phase 3 客户端读取与日志脱敏 | Pending |
|
||||
| CRED-06 Access Token 阿里云日志过滤 | Phase 3 客户端读取与日志脱敏 | Pending |
|
||||
| CRED-05 客户端 GET(user token,明文返回) | Phase 3 客户端读取与日志脱敏 | Done(Plan 03-01,2026-05-08) |
|
||||
| CRED-06 Access Token 阿里云日志过滤 | Phase 3 客户端读取与日志脱敏 | Done(Plan 03-02,2026-05-08) |
|
||||
|
||||
**覆盖率**:6/6 Active 需求映射到 Phase ✓(无孤儿,无重复)
|
||||
**覆盖率**:6/6 Active 需求映射到 Phase ✓(无孤儿,无重复);**6/6 全部 Done — Milestone v1.0 完结**
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2026-05-07 — Phase 2 完成(Plan 02-01 + Plan 02-02 全部交付,CRED-03 / CRED-04 标记 Done);下一步启动 Phase 3 规划(CRED-05 客户端 GET 明文 + CRED-06 阿里云日志脱敏)*
|
||||
*Last updated: 2026-05-08 — Phase 3 完成(Plan 03-01 + Plan 03-02 全部交付,CRED-05 / CRED-06 标记 Done);Milestone v1.0「通用凭据槽位(APP ID + Access Token)」CRED-01 至 CRED-06 全部交付;下一周期 milestone 候选评估见上方「候选优先级」段*
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
## Milestones
|
||||
|
||||
- 🚧 **v1.0 通用凭据槽位** — Phase 1-3(启动 2026-05-07)
|
||||
- ✅ **v1.0 通用凭据槽位** — Phase 1-3 全部交付(2026-05-07 启动 / 2026-05-08 完结,CRED-01 至 CRED-06 全 Done)
|
||||
|
||||
## Phases
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
- [x] **Phase 1: 凭据槽位数据层** — 落地 `CredentialSlot` 单例模型 + 迁移 + Django Admin 注册(脱敏 + 隐藏新增按钮)✓ 2026-05-07 完成(Plan 01-01 + 01-02)
|
||||
- [x] **Phase 2: 管理端读写接口** — 在 `/api/v1/admin/` 暴露凭据槽位 GET(脱敏)/ PUT(覆写)端点,admin token 鉴权 ✓ 2026-05-07 完成(Plan 02-01 + 02-02)
|
||||
- [ ] **Phase 3: 客户端读取与日志脱敏** — 在 `/api/credential-slot/` 暴露明文读取端点(user token 鉴权),并在阿里云日志链路过滤 `access_token`
|
||||
- [x] **Phase 3: 客户端读取与日志脱敏** — 在 `/api/credential-slot/` 暴露明文读取端点(user token 鉴权),并在阿里云日志链路过滤 `access_token` ✓ 2026-05-08 完成(Plan 03-01 + 03-02)
|
||||
|
||||
## Phase Details
|
||||
|
||||
@ -58,8 +58,8 @@
|
||||
3. 在生产日志(阿里云日志服务)中检索 Phase 2 / Phase 3 的请求轨迹:`PUT /api/v1/admin/credential-slot/` 请求体里的 `access_token` 字段被脱敏;管理端 `GET` 响应体里的 `access_token` 同样脱敏;客户端明文 GET 端点的响应体不写入日志(或同样脱敏),无任何位置暴露完整 Access Token 明文
|
||||
4. 端到端验证:管理后台用 PUT 写入一组凭据 → 手机端调用客户端 GET 拿到的 `app_id` / `access_token` 与管理端写入的一致(往返一致性成立)
|
||||
**Plans:** 2 plans
|
||||
- [x] 03-01-PLAN.md — 客户端凭据槽位 GET 接口(CRED-05:CredentialSlotClientView 明文返回 + /api/credential-slot/ 路由注册)
|
||||
- [ ] 03-02-PLAN.md — 阿里云日志 access_token 脱敏(CRED-06:AccessTokenMaskFilter + LOGGING 配置 + 修改记录)
|
||||
- [x] 03-01-PLAN.md — 客户端凭据槽位 GET 接口(CRED-05:CredentialSlotClientView 明文返回 + /api/credential-slot/ 路由注册)✓ 2026-05-08(commits 5269a08 / 50dcf1c / a58980f)
|
||||
- [x] 03-02-PLAN.md — 阿里云日志 access_token 脱敏(CRED-06:AccessTokenMaskFilter + LOGGING 配置 + 修改记录)✓ 2026-05-08(commits 891a5ea / 35eb110 / 7a9e511 / db4d5cf;端到端 9 truth × 32 项断言全 PASS)
|
||||
|
||||
## Progress
|
||||
|
||||
@ -70,8 +70,11 @@ Phase 按数值顺序执行:1 → 2 → 3(如出现紧急插入,记为 1.1
|
||||
|-------|----------------|--------|-----------|
|
||||
| 1. 凭据槽位数据层 | 2/2 | ✓ Complete | 2026-05-07 |
|
||||
| 2. 管理端读写接口 | 2/2 | ✓ Complete | 2026-05-07 |
|
||||
| 3. 客户端读取与日志脱敏 | 0/2 | Not started | - |
|
||||
| 3. 客户端读取与日志脱敏 | 2/2 | ✓ Complete | 2026-05-08 |
|
||||
|
||||
**Milestone v1.0 总进度:6/6 plans (100%) — 完结 ✓**
|
||||
|
||||
---
|
||||
|
||||
*生成时间:2026-05-07,Milestone v1.0「通用凭据槽位(APP ID + Access Token)」启动*
|
||||
*最后更新:2026-05-08,Milestone v1.0 完结(CRED-01 至 CRED-06 全部交付)*
|
||||
|
||||
@ -2,21 +2,21 @@
|
||||
gsd_state_version: 1.0
|
||||
milestone: v1.0
|
||||
milestone_name: 通用凭据槽位
|
||||
status: Phase 3 Plan 03-01 完成(CRED-05 客户端 GET 已落地),等待执行 Plan 03-02
|
||||
stopped_at: Plan 03-01 完成(CRED-05 客户端 GET 落地,6 truth × 15 断言全 PASS);下一步 Plan 03-02(CRED-06 日志脱敏)
|
||||
last_updated: "2026-05-08T02:17:57.222Z"
|
||||
status: Milestone v1.0 完成 — CRED-01 至 CRED-06 全部交付(Phase 1+2+3 全部 Done);下一周期 milestone 候选评估
|
||||
stopped_at: Plan 03-02 完成(CRED-06 日志脱敏 filter 落地,9 truth × 32 断言全 PASS);Milestone v1.0 已收尾
|
||||
last_updated: "2026-05-08T02:33:19.667Z"
|
||||
last_activity: 2026-05-08
|
||||
progress:
|
||||
total_phases: 3
|
||||
completed_phases: 2
|
||||
completed_phases: 3
|
||||
total_plans: 6
|
||||
completed_plans: 5
|
||||
percent: 83
|
||||
completed_plans: 6
|
||||
percent: 100
|
||||
---
|
||||
|
||||
# Project State — QY LTY Backend
|
||||
|
||||
**最后更新**: 2026-05-07(Phase 2 完成:Plan 02-01 + 02-02 全部交付,CRED-03 / CRED-04 标记 Done;端到端 8 条 success criteria 全 PASS;两端修改记录互引闭环)
|
||||
**最后更新**: 2026-05-08(Phase 3 Plan 03-02 完成:AccessTokenMaskFilter 落地,CRED-06 标记 Done;端到端 9 truth × 32 断言全 PASS;Milestone v1.0「通用凭据槽位」CRED-01~06 全部交付)
|
||||
|
||||
## 项目引用
|
||||
|
||||
@ -24,26 +24,26 @@ progress:
|
||||
|
||||
**核心价值**:设备端与手机端通过同一个 user_id 实时互通——`device_{user_id}` 分组语义必须始终成立。
|
||||
|
||||
**当前重点**:Milestone v1.0 通用凭据槽位(APP ID + Access Token)— Phase 1 + Phase 2 全部完成;下一步启动 Phase 3 规划(CRED-05 客户端 GET 明文 + CRED-06 阿里云日志脱敏)。
|
||||
**当前重点**:Milestone v1.0 通用凭据槽位(APP ID + Access Token)— **全部完成**。下一周期候选 milestone 评估见 `REQUIREMENTS.md` 候选优先级段。
|
||||
|
||||
## 当前位置
|
||||
|
||||
```
|
||||
Phase: 3 of 3(客户端读取与日志脱敏)— In Progress
|
||||
Plan: 01 of 02(CRED-05 客户端 GET 接口)— Complete ✓
|
||||
Status: Plan 03-01 完成,等待执行 Plan 03-02(CRED-06 日志脱敏)
|
||||
Phase: 3 of 3(客户端读取与日志脱敏)— Complete ✓
|
||||
Plan: 2 of 02(CRED-06 阿里云日志脱敏)— Complete ✓
|
||||
Status: Milestone v1.0 完结,等待下一周期 milestone 立项
|
||||
Last activity: 2026-05-08
|
||||
```
|
||||
|
||||
Progress: [████████░░] 83%(已完成 plan:5/6 — Phase 1 全部 + Phase 2 全部 + Phase 3 Plan 01)
|
||||
Progress: [██████████] 100%(已完成 plan:6/6 — Phase 1 全部 + Phase 2 全部 + Phase 3 全部)
|
||||
|
||||
## 性能指标
|
||||
|
||||
**速度:**
|
||||
|
||||
- 已完成 plan 数:5
|
||||
- 平均耗时:~398 s(顺序执行模式)
|
||||
- 总执行时间:1990 s(Plan 01-01: 184 s + Plan 01-02: ~600 s + Plan 02-01: 216 s + Plan 02-02: ~720 s + Plan 03-01: 270 s)
|
||||
- 已完成 plan 数:6
|
||||
- 平均耗时:~414 s(顺序执行模式)
|
||||
- 总执行时间:2484 s(Plan 01-01: 184 s + Plan 01-02: ~600 s + Plan 02-01: 216 s + Plan 02-02: ~720 s + Plan 03-01: 270 s + Plan 03-02: 494 s)
|
||||
|
||||
**按 Phase:**
|
||||
|
||||
@ -51,12 +51,12 @@ Progress: [████████░░] 83%(已完成 plan:5/6 — Phase
|
||||
|-------|-------|--------|----------|
|
||||
| 1 | 2/2 | 784 s | 392 s |
|
||||
| 2 | 2/2 | 936 s | 468 s |
|
||||
| 3 | 1/2 | 270 s | 270 s |
|
||||
| 3 | 2/2 | 764 s | 382 s |
|
||||
|
||||
**最近趋势:**
|
||||
|
||||
- 最近 5 个 plan:01-01(184 s,3 task)/ 01-02(~600 s,4 task + checkpoint 验收)/ 02-01(216 s,3 task / 3 commit / 3 文件)/ 02-02(~720 s,2 task / 2 commit / 1 创建 + 2 修改文件 + 端到端 28 项断言)/ 03-01(270 s,3 task / 2 commit / 2 修改文件 + 1 临时验收脚本未入 git + 端到端 15 项断言)
|
||||
- 趋势:纯 auto 代码落地 plan 速度稳定 200-270 s;Plan 03-01 是 1:1 复刻 Phase 2 admin view 模板的最简形态(删 _ensure_admin / _build_response_data / def put,仅保留 GET 明文),加上端到端验收,落地速度接近 Plan 02-01;3 处 Windows 环境兼容偏差全部 auto-fixed
|
||||
- 最近 6 个 plan:01-01(184 s,3 task)/ 01-02(~600 s,4 task + checkpoint 验收)/ 02-01(216 s,3 task / 3 commit / 3 文件)/ 02-02(~720 s,2 task / 2 commit / 1 创建 + 2 修改文件 + 端到端 28 项断言)/ 03-01(270 s,3 task / 2 commit / 2 修改文件 + 1 临时验收脚本未入 git + 端到端 15 项断言)/ 03-02(494 s,4 task / 4 commit / 2 创建 + 2 修改文件 + 端到端 32 项断言 + 2 处 Rule 1 auto-fix bug)
|
||||
- 趋势:03-02 略慢于 03-01(494 vs 270 s),原因是 plan 内置 2 处 bug(Pattern 4 兜底 regex 吃尾 + tuple args 形态破坏 %s 占位符)需要现场调试 + auto-fix;nett 端到端覆盖更广(32 vs 15 项断言);Milestone v1.0 整体节奏稳定 200-720 s/plan
|
||||
|
||||
*每完成一个 plan 后更新*
|
||||
|
||||
@ -95,6 +95,13 @@ Progress: [████████░░] 83%(已完成 plan:5/6 — Phase
|
||||
- **[Plan 03-01]** 客户端响应 schema 独立命名 _credential_slot_client_data_schema,access_token description 显式标注「明文」,与 admin 端 _credential_slot_data_schema 对照避免混用脱敏掩码语义
|
||||
- **[Plan 03-01]** 不调用 logger.info / logger.debug 输出 access_token,未引入新泄露源;Plan 03-02 的 AccessTokenMaskFilter 是兜底防御
|
||||
- **[Plan 03-01]** 验收脚本 `_phase3_01_verify.py` 不入 git,留在仓库根;Plan 03-02 Task 4 末尾统一删除 + 一并写两端 docs/修改记录.md 互引条目
|
||||
- **[Plan 03-02]** AccessTokenMaskFilter 挂在 LOGGING.handlers(aliyun + console)而非 loggers 段:挂 logger 仅过滤直接通过该 logger 的 record,挂 handler 才统一覆盖所有 logger → handler 路径
|
||||
- **[Plan 03-02]** dictConfig filter 注册用 `()` 工厂语法不用 `class`:dictConfig 标准对 filter 与 handler 语法不互通
|
||||
- **[Plan 03-02]** 4 个 regex 不合并成 1 个大 regex:可读性 + group 数差异(JSON/Pyrepr 是 3 group / Query/Fallback 是 2 group),合并会让 _sub 变脆
|
||||
- **[Plan 03-02]** filter 仅识别 access_token 字段名前缀锚点,不脱敏裸 token / Authorization / Bearer:那是另一类敏感数据,留 v2.x 候选优先级处理
|
||||
- **[Plan 03-02]** Pattern 4 兜底 regex 终止符增加 `&` / `=` 排除:避免 Pattern 3 的输出 `access_token=********1234&u=1` 被 Pattern 4 把 `********1234&u=1` 整段二次 mask 把末 4 位 `1234` 吃成 `&u=1`(auto-fix Rule 1 Bug)
|
||||
- **[Plan 03-02]** tuple args 形态走 `record.getMessage()` 预拼接后 `args=None` 再脱敏:避免 Formatter `%` 拼接时占位符被 mask 吃掉触发 TypeError(auto-fix Rule 1 Bug)
|
||||
- **[Plan 03-02]** 不写 qy-lty-admin/docs/修改记录.md 互引:CONTEXT 锁定 + RESEARCH 实证;客户端给 Unity 用,qy-lty-admin 不消费 /api/credential-slot/
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@ -122,18 +129,19 @@ Progress: [████████░░] 83%(已完成 plan:5/6 — Phase
|
||||
## 下一步
|
||||
|
||||
```
|
||||
执行 Phase 3 Plan 02:CRED-06 阿里云日志脱敏 + 两端修改记录互引 + 临时验收脚本删除
|
||||
Milestone v1.0 已完结。下一步:评估下一周期 milestone 候选(见 REQUIREMENTS.md 候选优先级段);运行 /gsd-complete-milestone 收口(如启用)
|
||||
```
|
||||
|
||||
Phase 3 Plan 03-01 已完成(commits 5269a08 / 50dcf1c):
|
||||
Phase 3 Plan 03-02 已完成(commits 891a5ea / 35eb110 / 7a9e511 / db4d5cf):
|
||||
|
||||
- Task 1:`CredentialSlotClientView` + `_credential_slot_client_data_schema` 追加到 `aiapp/views.py` 末尾(commit 5269a08)
|
||||
- Task 2:`/api/credential-slot/` 路由注册到 `qy_lty/urls.py:api_urlpatterns`(commit 50dcf1c)
|
||||
- Task 3:端到端 6 条 truth × 15 项独立断言全 PASS — Django test client 跑 user/admin token 双 200 + 401 × 2 + Swagger schema + DB 探针态(脚本 `_phase3_01_verify.py` 留在仓库根未入 git)
|
||||
- Task 1:`common/logging/__init__.py`(空 package marker)+ `common/logging/filters.py`(106 行 AccessTokenMaskFilter,4 正则 + tuple args 处理 + filter() 方法)(commit 891a5ea)
|
||||
- Task 2:`qy_lty/settings.py:LOGGING` 注册 `filters` 段(dictConfig `()` 工厂语法)+ `handlers.aliyun` / `handlers.console` 各挂 `filters: ['access_token_mask']`,loggers 段 5 条 logger 完全未动(commit 35eb110)
|
||||
- Task 3:端到端 9 条 truth × 32 项独立断言全 PASS — 覆盖 03-01 的 5 条 client view + filter 4 形态 + 不误伤 Authorization + admin/client roundtrip + 端到端 logger.info 真打印 console 脱敏 + DB 探针态还原(commit 7a9e511,03-VERIFICATION.md 落地)
|
||||
- Task 4:`docs/修改记录.md` 顶部追加 [2026-05-08] Phase 3 条目(覆盖 5 处文件 + CRED-05/06 + 跨项目联动「无」明示)(commit db4d5cf)
|
||||
|
||||
URL `/api/credential-slot/` GET 已端到端验证(明文返回 access_token);DB 探针态保持 `probe_app` / `probe_secret_xxxx`;CRED-05 已在 REQUIREMENTS.md 标记 Done。
|
||||
CRED-06 已在 REQUIREMENTS.md 标记 Done;DB 探针态保持 `pk=1 / app_id='probe_app' / access_token='probe_secret_xxxx'`;临时验收脚本(5 个)全部删除。
|
||||
|
||||
下一步执行 Plan 03-02:CRED-06(`common/logging/filters.py:AccessTokenMaskFilter` + `qy_lty/settings.py:LOGGING.filters/handlers` + 两端 docs/修改记录.md 互引 + 删除 `_phase3_01_verify.py`)。
|
||||
**Milestone v1.0「通用凭据槽位(APP ID + Access Token)」CRED-01 至 CRED-06 全部交付完成**。下一周期候选:见上方「Deferred Items」段。
|
||||
|
||||
## 工作流配置
|
||||
|
||||
@ -163,10 +171,10 @@ CLAUDE.md 两条强制规则(任何 phase 都必须遵守):
|
||||
|
||||
## Session Continuity
|
||||
|
||||
Last session: 2026-05-08T02:17:57.220Z
|
||||
Stopped at: Plan 03-01 完成(CRED-05 客户端 GET 落地,6 truth × 15 断言全 PASS);下一步 Plan 03-02(CRED-06 日志脱敏)
|
||||
Last session: 2026-05-08T02:30:22Z
|
||||
Stopped at: Plan 03-02 完成(CRED-06 日志脱敏 filter 落地,9 truth × 32 断言全 PASS);Milestone v1.0 已收尾,等待下一周期 milestone 立项
|
||||
Resume file: None
|
||||
|
||||
---
|
||||
|
||||
*由 /gsd-execute-phase 顺序执行器于 2026-05-07 更新(Plan 02-02 完成时点)*
|
||||
*由 /gsd-execute-phase 顺序执行器于 2026-05-08 更新(Plan 03-02 完成时点;Milestone v1.0 完结)*
|
||||
|
||||
243
qy_lty/.planning/phases/03-client-and-log-mask/03-02-SUMMARY.md
Normal file
243
qy_lty/.planning/phases/03-client-and-log-mask/03-02-SUMMARY.md
Normal file
@ -0,0 +1,243 @@
|
||||
---
|
||||
phase: 03-client-and-log-mask
|
||||
plan: 02
|
||||
subsystem: logging
|
||||
tags: [logging, dictconfig, filter, mask-token, regex, defensive-mitigation, milestone-v1.0-complete]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- phase: 01-credential-data-layer
|
||||
provides: common.utils.mask_token / aiapp.models.CredentialSlot
|
||||
- phase: 02-admin-rest
|
||||
provides: aiapp.views.CredentialSlotAdminView(端到端 T8 admin PUT 走它)+ DB 探针态 pk=1/probe_app/probe_secret_xxxx
|
||||
- phase: 03-01
|
||||
provides: aiapp.views.CredentialSlotClientView + /api/credential-slot/ 路由(端到端 T1/T2 走它)
|
||||
provides:
|
||||
- common.logging 子包 + common.logging.filters.AccessTokenMaskFilter(logging.Filter 子类,4 正则覆盖 JSON / Pyrepr / URL query / 等号或冒号兜底)
|
||||
- settings.LOGGING.filters.access_token_mask(dictConfig 工厂语法 () 注册)
|
||||
- settings.LOGGING.handlers.aliyun.filters / handlers.console.filters(filter 挂在 handler 层,统一覆盖所有 logger → handler 路径)
|
||||
- docs/修改记录.md 顶部 [2026-05-08] Phase 3 条目(覆盖 5 处文件 + CRED-05/06 + 跨项目联动「无」)
|
||||
- .planning/phases/03-client-and-log-mask/03-VERIFICATION.md(9 truth × 32 项独立断言全 PASS 报告)
|
||||
affects:
|
||||
- 任何后续 view / middleware / 业务代码内 logger.info/debug/warning 调用:含 access_token 字段的字符串 / dict args / tuple args 形态会被自动脱敏;不再依赖每个调用点自律
|
||||
- Milestone v1.0「通用凭据槽位」:CRED-01 至 CRED-06 全部 Done,本期目标圆满达成
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "logging.Filter 子类挂在 LOGGING.handlers 层(不挂 loggers 层)—— 一次注册全链路覆盖"
|
||||
- "dictConfig 工厂语法 () 用于 filter(区别于 handler 用 class)"
|
||||
- "正则 pattern 终止符设计:Pattern 4 兜底 regex 必须排除 & / = 等 URL 分隔符,避免重复 mask Pattern 3 的输出"
|
||||
- "tuple args 形态先 record.getMessage() 拼成最终字符串再整体脱敏 + args=None:避免 Formatter 阶段 % 格式化时占位符被吃后报 TypeError"
|
||||
- "防御性兜底而非修复式补丁:当前仓库无既有泄露路径,filter 是为未来代码改动留的安全网"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- common/logging/__init__.py(空文件,0 字节,包 marker)
|
||||
- common/logging/filters.py(106 行:AccessTokenMaskFilter 类 + 4 个 _PATTERNS 正则 + _NEEDLE 短路 + _sub / _mask_in_text / filter 方法)
|
||||
- .planning/phases/03-client-and-log-mask/03-VERIFICATION.md(端到端 9 truth × 32 PASS 报告)
|
||||
- .planning/phases/03-client-and-log-mask/03-02-SUMMARY.md(本文件)
|
||||
modified:
|
||||
- qy_lty/settings.py(LOGGING 字典:新增 filters 段(4 行)+ aliyun handler 加 filters: ['access_token_mask'](1 行)+ console handler 加 filters: ['access_token_mask'](1 行)+ 4 行注释;前后行差 +11 行)
|
||||
- docs/修改记录.md(顶部追加 [2026-05-08] Phase 3 条目,紧邻 [2026-05-07] Phase 2 之上;+22 行)
|
||||
deleted:
|
||||
- _phase3_01_verify.py(plan 03-01 创建的临时验收脚本,本 plan Task 3 末尾统一删除)
|
||||
- _phase3_02_unit_test.py(Task 1 临时单元测试,验完即删)
|
||||
- _phase3_02_verify.py(Task 3 端到端验收脚本,验完即删 + 输出落 03-VERIFICATION.md)
|
||||
- _phase3_02_settings_check.py(Task 2 验收 helper,验完即删)
|
||||
- _phase3_admin_mtime.txt(Task 4 mtime baseline 临时文件,验完即删)
|
||||
|
||||
key-decisions:
|
||||
- "[Plan 03-02] AccessTokenMaskFilter 挂在 LOGGING.handlers(aliyun + console)而非 loggers 段:挂 logger 仅过滤直接通过该 logger 的 record,挂 handler 才统一覆盖所有 logger → handler 路径(per RESEARCH Pitfall 1)"
|
||||
- "[Plan 03-02] dictConfig filter 注册用 () 工厂语法不用 class:dictConfig 标准对 filter 与 handler 语法不互通(per RESEARCH Pitfall 5)"
|
||||
- "[Plan 03-02] 4 个 regex 不合并成 1 个大 regex:可读性 + group 数差异(JSON/Pyrepr 是 3 group / Query/Fallback 是 2 group),合并会让 _sub 变脆"
|
||||
- "[Plan 03-02] filter 仅识别 access_token 字段名前缀锚点,不脱敏裸 token / Authorization / Bearer:那是另一类敏感数据,留 v2.x 候选优先级处理(per RESEARCH Pitfall 3)"
|
||||
- "[Plan 03-02] tuple args 形态走 record.getMessage() 预拼接后 args=None 再脱敏:避免 Formatter % 拼接时占位符被 mask 吃掉触发 TypeError(auto-fix Rule 1,详见 Deviations)"
|
||||
- "[Plan 03-02] Pattern 4 兜底 regex 终止符增加 & / = 排除:避免 Pattern 3 的输出 access_token=********1234&u=1 被 Pattern 4 把 ********1234&u=1 整段二次 mask 把末 4 位 1234 吃成 &u=1(auto-fix Rule 1,详见 Deviations)"
|
||||
- "[Plan 03-02] 端到端验收脚本走 python manage.py shell -c \"exec(open(...).read())\" 而非 < 重定向:Windows PowerShell 下 < 触发逐行 REPL 执行,缩进块全部 IndentationError(plan 03-01 已踩坑)"
|
||||
- "[Plan 03-02] 验收脚本 print 用 [PASS] / [FAIL] 而非 ✓ / ✗ 标记:Windows GBK 控制台无法编码 Unicode 字符(plan 03-01 已踩坑)"
|
||||
- "[Plan 03-02] 不写 qy-lty-admin/docs/修改记录.md 互引:CONTEXT 锁定 + RESEARCH 实证;客户端给 Unity 用(LTY_Project / LTY_App_Project_URP),qy-lty-admin 不消费 /api/credential-slot/(管理端走 Phase 2 落地的 /api/v1/admin/credential-slot/)"
|
||||
- "[Plan 03-02] 临时验收脚本(_phase3_01_verify.py / _phase3_02_unit_test.py / _phase3_02_verify.py / _phase3_02_settings_check.py / _phase3_admin_mtime.txt)一律不入 git:与 Phase 2 / Plan 03-01 一致,是一次性证据生成器"
|
||||
|
||||
patterns-established:
|
||||
- "logging.Filter 子类 + dictConfig () 工厂语法 + 挂 handler 不挂 logger 的脱敏 filter 模式(未来其它字段如 Bearer / api_secret 想脱敏时直接复刻)"
|
||||
- "正则交叉吃尾的兜底 regex 终止符设计法则:兜底 regex 必须排除前置 regex 的边界字符,避免链式调用时把已脱敏的输出再次当 value"
|
||||
- "tuple args 形态 LogRecord 处理:getMessage() 预拼 + args=None,避开占位符与 mask 输出的 % 格式化冲突"
|
||||
|
||||
requirements-completed:
|
||||
- CRED-06
|
||||
|
||||
# Metrics
|
||||
duration: 8min14s
|
||||
completed: 2026-05-08
|
||||
---
|
||||
|
||||
# Phase 3 Plan 03-02:阿里云日志 access_token 脱敏 filter Summary
|
||||
|
||||
**AccessTokenMaskFilter 4 正则脱敏 filter 落地(dictConfig () 工厂语法注册到 LOGGING.handlers.aliyun + console),9 truth × 32 项独立断言全 PASS(CRED-05 + CRED-06 整合验收);docs/修改记录.md 顶部追加 Phase 3 条目,跨项目联动「无」明示;Milestone v1.0「通用凭据槽位」CRED-01~06 至此全部 Done**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** ~8.2 min(含 2 处 Rule 1 auto-fix bug)
|
||||
- **Started:** 2026-05-08T02:22:08Z
|
||||
- **Completed:** 2026-05-08T02:30:22Z
|
||||
- **Tasks:** 4(包 + filter / settings / 端到端验收 / 修改记录条目)
|
||||
- **Files modified:** 2(qy_lty/settings.py + docs/修改记录.md)
|
||||
- **Files created:** 4(common/logging/__init__.py + common/logging/filters.py + 03-VERIFICATION.md + 03-02-SUMMARY.md)
|
||||
- **Files deleted:** 5 临时(_phase3_01_verify.py + _phase3_02_unit_test.py + _phase3_02_verify.py + _phase3_02_settings_check.py + _phase3_admin_mtime.txt)
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- **CRED-06 落地完成**:`AccessTokenMaskFilter(logging.Filter)` 已注册到 `qy_lty.settings.LOGGING.filters` 并由 `handlers.aliyun` / `handlers.console` 引用;任何 view / middleware / 第三方库的 `logger.info(包含 access_token 明文的字符串)` 都会被脱敏成 `mask_token(value)` 输出
|
||||
- **9 truth 全 PASS(32 项独立断言)**:
|
||||
- T1-T5(5 truth × 14 项断言):plan 03-01 客户端 GET 5 项验证(user/admin token 200 + 401 × 2 + swagger schema)
|
||||
- T6(4 项):filter 4 种正则形态(JSON / Pyrepr / Query / Fallback)伪 LogRecord 处理后含末 4 位 `1234`、不含完整明文 `abcdefgh`
|
||||
- T7(2 项):filter 不误伤 `Authorization header: bearer_user_token_xxxxxxx` / `Bearer raw_token_zzz` 等非 access_token 字段
|
||||
- T8(5 项):端到端 admin PUT roundtrip → admin GET 脱敏 `**********RT99` + client GET 明文 `rt_secret_RT99` + app_id 一致
|
||||
- T9(2 项):端到端 `logger.info('access_token=defensive_secret_DEFC')` → console 输出 `*****************DEFC` 脱敏(防御性兜底真实生效)
|
||||
- T_FINAL(2 项):DB 探针态主动还原 `pk=1 / app_id='probe_app' / access_token='probe_secret_xxxx'`
|
||||
- **DB 探针态保持稳定**:脚本前后 `pk=1, app_id='probe_app', access_token='probe_secret_xxxx'`,给后续工作(无论是下一周期 milestone 还是 v2.x 评估)留下与 Phase 1/2/3-01 一致的稳定起点
|
||||
- **Phase 1 / Phase 2 / Plan 03-01 既有代码 0 改动**:`CredentialSlot` 模型 / Admin 注册 / `CredentialSlotAdminView` / `CredentialSlotClientView` / 既有 5 条 logger 配置完全未动;imports 段未变
|
||||
- **Milestone v1.0 完结**:CRED-01(单例 model)+ CRED-02(Admin 注册)+ CRED-03(admin GET 脱敏)+ CRED-04(admin PUT 覆写)+ CRED-05(客户端 GET 明文)+ CRED-06(日志脱敏 filter)全部 Done
|
||||
|
||||
## Task Commits
|
||||
|
||||
每个 task 原子提交(中文 commit message):
|
||||
|
||||
1. **Task 1: 新建 common/logging/ 包 + AccessTokenMaskFilter** — `891a5ea` (feat) — common/logging/__init__.py(0 字节)+ common/logging/filters.py(106 行)
|
||||
2. **Task 2: settings.py LOGGING 注册 access_token_mask filter** — `35eb110` (feat) — LOGGING 字典 +11 行(filters 段 + 2 个 handler 各 1 行 filters 引用 + 注释)
|
||||
3. **Task 3: Phase 3 端到端验收报告** — `7a9e511` (test) — 03-VERIFICATION.md 113 行(9 truth × 32 项断言)
|
||||
4. **Task 4: docs/修改记录.md 追加 Phase 3 条目** — `db4d5cf` (docs) — +22 行(5 文件 + CRED-05/06 + 跨项目联动「无」)
|
||||
|
||||
**Plan metadata:** 待 final commit(本 SUMMARY + STATE.md / ROADMAP.md / REQUIREMENTS.md)
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| 文件 | 操作 | 行数变化 | 说明 |
|
||||
|------|------|----------|------|
|
||||
| `common/logging/__init__.py` | 新建 | 0 字节(empty) | package marker |
|
||||
| `common/logging/filters.py` | 新建 | 106 行 | AccessTokenMaskFilter 类 + 4 _PATTERNS + _NEEDLE 短路 + _sub/_mask_in_text/filter 方法 |
|
||||
| `qy_lty/settings.py` | 修改 | LOGGING 块 +11 行(375-394 区间,filters 段 + 2 处 'filters' key) | dictConfig () 工厂语法注册 + handlers 引用 |
|
||||
| `docs/修改记录.md` | 修改 | 顶部 +22 行(行 26-47 新条目) | [2026-05-08] Phase 3 条目,紧邻 [2026-05-07] Phase 2 之上 |
|
||||
| `.planning/phases/03-client-and-log-mask/03-VERIFICATION.md` | 新建 | 113 行 | 9 truth × 32 项 PASS 报告 |
|
||||
| `.planning/phases/03-client-and-log-mask/03-02-SUMMARY.md` | 新建 | 本文件 | 本 plan summary |
|
||||
|
||||
## 防御性兜底语义说明
|
||||
|
||||
**RESEARCH 实证(已读 03-CONTEXT.md / 03-RESEARCH.md)**:当前仓库**没有**任何代码 logger 输出 `CredentialSlot.access_token` 明文:
|
||||
|
||||
- `StandardResponseMiddleware`(`common/middleware.py`)只统一包装响应壳层,不打日志
|
||||
- `CredentialSlotAdminView` / `CredentialSlotClientView` 都不显式 `logger.info(serializer.data)`
|
||||
- Django 默认 access log 不含请求 / 响应 body
|
||||
- `aliyun_log_python_sdk` 集成(`common/aliyun_logging.py`)只 emit `record.getMessage()`,没有自动 dump request
|
||||
|
||||
**所以 CRED-06 的真实价值是「防御性兜底」**,不是「修补现有泄露」:
|
||||
|
||||
- Phase 1 + Phase 2 + Phase 3 的 view 让 access_token 进入「内存中可被随手 dump」的状态
|
||||
- 任何后续开发者写 `logger.info(f"PUT body: {request.data}")` 类型代码就会立即把 access_token 明文打到阿里云日志服务
|
||||
- `AccessTokenMaskFilter` 在 LOGGING.handlers 层兜底拦截,不依赖每个调用点自律
|
||||
- **未来代码改动天然安全** —— 即使新人在 view 里写 `logger.debug(self.request.data)` 也不会泄露
|
||||
|
||||
## Decisions Made
|
||||
|
||||
见 frontmatter `key-decisions` 字段,已 10 条决策全部归档。
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 1 - Bug] Pattern 4 兜底 regex 把 Pattern 3 已脱敏的输出二次 mask 吃掉末 4 位**
|
||||
|
||||
- **Found during:** Task 1 跑 `_phase3_02_unit_test.py` 首次 verify
|
||||
- **Issue:** Plan 给的 Pattern 4 终止符是 `[^\s,;)\]\}"\']+`,对 Query 形态 `access_token=abcdefgh1234&u=1`:
|
||||
1. Pattern 3 `(access_token=)([^&\s"\']+)` 先 match → `access_token=********1234&u=1`
|
||||
2. Pattern 4 `(access_token\s*[:=]\s*)([^\s,;)\]\}"\']+)` 再 match → 因为 `&` / `=` 不在排除集,value = `********1234&u=1`(16 字符),mask_token 输出 `************` + 末 4 位 `&u=1` = `************&u=1`,把 `1234` 吃没了
|
||||
- **Fix:** Pattern 4 终止符增加 `&` / `=` 排除字符 → `[^\s,;)\]\}"\'&=]+`;保证 Pattern 4 不再吞掉 Pattern 3 的输出尾部
|
||||
- **Files modified:** common/logging/filters.py(行 47-52,Pattern 4 regex + 5 行注释解释为什么排除 `&=`)
|
||||
- **Verification:** `_phase3_02_unit_test.py` 复跑 Query 形态 OUTPUT = `'GET /x?access_token=********1234&u=1'`(含末 4 位 `1234`)
|
||||
- **Committed in:** 891a5ea(与 Task 1 主体一并提交)
|
||||
|
||||
**2. [Rule 1 - Bug] tuple args 形态把 `%s` 占位符当 access_token 的 value 吃掉,触发 Formatter TypeError**
|
||||
|
||||
- **Found during:** Task 1 跑 `_phase3_02_unit_test.py` 第 5 项断言(tuple args)
|
||||
- **Issue:** Plan 给的 filter() 实现先扫 `record.msg` 再扫 `record.args`:
|
||||
1. msg = `'access_token=%s'`,args = `('abcdefgh1234',)`
|
||||
2. 扫 msg:Pattern 4 match `access_token=` + value = `%s`(`%` 与 `s` 都不在排除集),mask_token('%s') = `**`(短于 4 全脱)。msg 变成 `'access_token=**'`
|
||||
3. 扫 args:tuple 中 `'abcdefgh1234'` 不含 `access_token` 字面量,`_NEEDLE not in text.lower()` 短路返回,不变
|
||||
4. Formatter 阶段:`'access_token=**' % ('abcdefgh1234',)` → 占位符 `%s` 已被 `**` 替换,args 仍有 1 元素 → `TypeError: not all arguments converted during string formatting`
|
||||
- **Fix:** 改 filter() 处理逻辑为:
|
||||
- tuple args 形态:先 `record.getMessage()` 拼出最终字符串(msg + args % 格式化结果),再整体走 `_mask_in_text` 脱敏,最后 `record.args = None` 避免 Formatter 二次拼接
|
||||
- 其它形态(无 args / dict args):保持原 plan 逻辑分别处理 msg 和 args
|
||||
- **Files modified:** common/logging/filters.py(行 79-111,filter 方法重构;增加 try/except getMessage 失败时 graceful return True 不影响其它 handler)
|
||||
- **Verification:** `_phase3_02_unit_test.py` 复跑 OUTPUT = `'access_token=********1234'`(tuple args 末 4 位保留),全 9 项 ALL UNIT TESTS PASS
|
||||
- **Committed in:** 891a5ea(与 Task 1 主体一并提交,因为 bug 在 Task 1 内被发现且未引入 git)
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 2 auto-fixed(Rule 1 Bug × 2)
|
||||
**Impact on plan:** 两处偏差均为 plan 给的 regex / filter 实现逻辑漏洞,端到端 9 truth × 32 项 PASS 全过;plan acceptance criteria 全部达成;CRED-06 防御性兜底语义正确
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
无业务 / 架构问题。仅遇到上述 2 处 Plan 内置 bug,已自动修复并文档化。
|
||||
|
||||
## Stub Status
|
||||
|
||||
无。本 plan 落地的 filter + LOGGING 配置全部是真实生效的代码路径:
|
||||
|
||||
- `common/logging/filters.py:AccessTokenMaskFilter` 已挂载到 settings.LOGGING.handlers,T9 端到端 `logger.info` 真实输出验证脱敏生效
|
||||
- 不存在 placeholder / 硬编码 / mock 数据 / 空函数
|
||||
|
||||
## User Setup Required
|
||||
|
||||
无 —— 不引入新依赖、不要求新环境变量、不需要外部服务配置;现有 `aliyun-log-python-sdk` 已在 Phase 0 安装
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Milestone v1.0「通用凭据槽位」至此全部完成**:
|
||||
|
||||
- CRED-01(Phase 1 Plan 01-01):单例 CredentialSlot 模型 + 迁移 ✓
|
||||
- CRED-02(Phase 1 Plan 01-02):Django Admin 注册 + 脱敏 ✓
|
||||
- CRED-03(Phase 2 Plan 02-01):管理端 GET 脱敏 ✓
|
||||
- CRED-04(Phase 2 Plan 02-01):管理端 PUT 覆写 ✓
|
||||
- CRED-05(Phase 3 Plan 03-01):客户端 GET 明文 ✓
|
||||
- CRED-06(Phase 3 Plan 03-02):阿里云日志脱敏 filter ✓
|
||||
|
||||
**下一周期候选 milestone**(详见 `.planning/REQUIREMENTS.md`「候选优先级」段):
|
||||
|
||||
- HIGH:ACH-02 成就解锁条件校验缺失 / SMS 验证码无频率限制 / 收紧 DEBUG / CORS_ALLOW_ALL_ORIGINS / 移除测试 MAC 硬编码 / 测试基础设施搭建(pytest 体系)
|
||||
- MEDIUM:好感度 P2/P3/P4 / Python 3.8 → 3.11/3.12 升级 / 拆分 device_interaction/views.py(1867 行)
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- [x] common/logging/__init__.py 存在,0 字节空文件 (FOUND, 0 bytes)
|
||||
- [x] common/logging/filters.py 含 `class AccessTokenMaskFilter(logging.Filter)` (FOUND)
|
||||
- [x] common/logging/filters.py 含 4 个 _PATTERNS 正则 (FOUND, len = 4)
|
||||
- [x] qy_lty/settings.py 含 `'access_token_mask'` filter 注册 (FOUND)
|
||||
- [x] qy_lty/settings.py 含 `'()': 'common.logging.filters.AccessTokenMaskFilter'` 工厂语法 (FOUND)
|
||||
- [x] qy_lty/settings.py 含 `['access_token_mask']` ≥ 2 处(aliyun + console) (FOUND, count = 2)
|
||||
- [x] docs/修改记录.md 顶部含 `[2026-05-08] Phase 3` 条目 (FOUND, idx=253)
|
||||
- [x] docs/修改记录.md Phase 3 条目位于 Phase 2 之上 (Phase 3 idx=253 < Phase 2 idx=4465)
|
||||
- [x] docs/修改记录.md Phase 3 条目含 5 处文件路径 + CRED-05 + CRED-06 (FOUND)
|
||||
- [x] docs/修改记录.md Phase 3 条目跨项目联动字段写「无 — 客户端给 Unity 用」(FOUND)
|
||||
- [x] qy-lty-admin/docs/修改记录.md mtime 未变(baseline 1778166405.4241624 = current)
|
||||
- [x] commit 891a5ea in git log (FOUND)
|
||||
- [x] commit 35eb110 in git log (FOUND)
|
||||
- [x] commit 7a9e511 in git log (FOUND)
|
||||
- [x] commit db4d5cf in git log (FOUND)
|
||||
- [x] _phase3_02_verify.py 输出 ALL PASS(32 PASS / 0 FAIL)
|
||||
- [x] _phase3_02_unit_test.py 输出 ALL UNIT TESTS PASS
|
||||
- [x] DB 探针态 pk=1 / app_id='probe_app' / access_token='probe_secret_xxxx' 保持
|
||||
- [x] 5 个临时验收脚本均已删除(git status 干净)
|
||||
- [x] python manage.py shell 启动无 ValueError(dictConfig 工厂语法正确)
|
||||
|
||||
## TDD Gate Compliance
|
||||
|
||||
本 plan frontmatter `type: execute`,非 TDD plan。无 RED/GREEN/REFACTOR 强制顺序要求。但实际操作上 Task 1 写完 filter + 单元测试同步落地,Task 1 commit 既含 filter 主体又验证 4 个正则形态 + tuple args 形态行为;与轻量 TDD 实质等价。
|
||||
|
||||
---
|
||||
*Phase: 03-client-and-log-mask*
|
||||
*Plan: 02*
|
||||
*Completed: 2026-05-08*
|
||||
*Milestone v1.0「通用凭据槽位(APP ID + Access Token)」at this point: ALL DONE — CRED-01 to CRED-06 全部交付*
|
||||
Loading…
x
Reference in New Issue
Block a user