From 7a9e511132f70f8e2f59b42962d650aa36cef6af Mon Sep 17 00:00:00 2001 From: pmc <740076875@qq.com> Date: Fri, 8 May 2026 10:28:26 +0800 Subject: [PATCH] =?UTF-8?q?test(03-02):=20Phase=203=20=E7=AB=AF=E5=88=B0?= =?UTF-8?q?=E7=AB=AF=E9=AA=8C=E6=94=B6=E6=8A=A5=E5=91=8A=20(CRED-05=20+=20?= =?UTF-8?q?CRED-06)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 9 truth × 32 项独立断言全 PASS, FAIL = 0 - T1-T5: 客户端 GET 5 项 (user/admin token + 401 + swagger schema) - T6: filter 4 种正则形态 (JSON/Pyrepr/Query/Fallback) - T7: 不误伤 Authorization / Bearer 字段 - T8: admin PUT roundtrip + admin GET 脱敏 + client GET 明文 - T9: 端到端 logger.info 真打印 console 输出脱敏 - T_FINAL: DB 探针态还原 (probe_app/probe_secret_xxxx) - 临时脚本均已删除 (_phase3_01_verify / _phase3_02_unit_test / _phase3_02_verify / _phase3_02_settings_check) --- .../03-client-and-log-mask/03-VERIFICATION.md | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 qy_lty/.planning/phases/03-client-and-log-mask/03-VERIFICATION.md diff --git a/qy_lty/.planning/phases/03-client-and-log-mask/03-VERIFICATION.md b/qy_lty/.planning/phases/03-client-and-log-mask/03-VERIFICATION.md new file mode 100644 index 0000000..6a91560 --- /dev/null +++ b/qy_lty/.planning/phases/03-client-and-log-mask/03-VERIFICATION.md @@ -0,0 +1,113 @@ +# Phase 3 端到端验收报告(CRED-05 + CRED-06) + +**执行时间**:2026-05-08 +**执行命令**:`python manage.py shell -c "exec(open('_phase3_02_verify.py', encoding='utf-8').read())"` +**结果**:ALL PASS — 32 项独立断言全部通过,FAIL 数 = 0 +**临时验收脚本**:跑完即删(`_phase3_01_verify.py` / `_phase3_02_unit_test.py` / `_phase3_02_verify.py` / `_phase3_02_settings_check.py`) + +--- + +## 9 条 Truth × 32 项独立断言 + +### T1: client GET 携 user token → 200 + 明文 + +``` +[PASS] T1.user_token_200: sc=200 +[PASS] T1.success_true +[PASS] T1.app_id +[PASS] T1.access_token_plain: should be plaintext +[PASS] T1.no_mask_in_response +``` + +### T2: client GET 携 admin token → 200 + 明文(不区分 admin / user token) + +``` +[PASS] T2.admin_token_200 +[PASS] T2.access_token_plain +``` + +### T3: client GET 无 token → 401 + 标准壳层 success=false + +``` +[PASS] T3.no_token_401 +[PASS] T3.success_false +``` + +### T4: client GET 伪造 token → 401(Redis 中无该 key) + +``` +[PASS] T4.fake_token_401 +``` + +### T5: /swagger.json/ 含 /credential-slot/ 路径 + GET 方法 + +``` +[PASS] T5.swagger_200 +[PASS] T5.path_in_schema: keys_sample=['/achievement/achievements/', '/achievement/achievements/{id}/', '/achievement/achievements/{id}/check_achievement/', '/achievement/user-achievements/', '/achievement/user-achievements/check_and_grant/'] +[PASS] T5.has_get +``` + +### T6: AccessTokenMaskFilter 4 种序列化形态全脱敏 + +``` +[PASS] T6.JSON.no_plain: out='{"access_token": "********1234"}' +[PASS] T6.JSON.has_tail +[PASS] T6.Pyrepr.no_plain: out="{'access_token': '********1234'}" +[PASS] T6.Pyrepr.has_tail +[PASS] T6.Query.no_plain: out='GET /x?access_token=********1234&u=1' +[PASS] T6.Query.has_tail +[PASS] T6.Fallback.no_plain: out='access_token: ********1234' +[PASS] T6.Fallback.has_tail +``` + +### T7: AccessTokenMaskFilter 不误伤 Authorization header / Bearer 字段 + +``` +[PASS] T7.unmodified_Authorization h: out='Authorization header: bearer_user_token_xxxxxxx' +[PASS] T7.unmodified_Bearer raw_toke: out='Bearer raw_token_zzz' +``` + +### T8: 端到端 admin PUT roundtrip → client GET 一致明文 + admin GET 脱敏 + +``` +[PASS] T8.put_200: sc=200 +[PASS] T8.admin_get_masked: admin GET should mask got='**********RT99' +[PASS] T8.admin_get_tail_RT99 +[PASS] T8.client_get_plain: client GET should be plain got='rt_secret_RT99' +[PASS] T8.client_app_id +``` + +### T9: 端到端 logger.info 真打印 → console 输出脱敏(防御性兜底真实生效) + +``` +[PASS] T9.logger_info_no_plain: out='defensive_test access_token=*****************DEFC' +[PASS] T9.logger_info_tail +``` + +### T_FINAL: DB 探针态主动还原(给后续 phase 留稳定起点) + +``` +[PASS] T_FINAL.db_restored.app_id +[PASS] T_FINAL.db_restored.access_token +``` + +--- + +## 关键观察 + +- **明文走客户端 / 脱敏走管理端**:T8 同时验证了 `/api/credential-slot/`(客户端)返回 `'rt_secret_RT99'` 明文,与 `/api/v1/admin/credential-slot/`(管理端)返回 `'**********RT99'` 末 4 位脱敏,view 层差异化语义正确 +- **Filter 兜底真实生效**:T9 端到端调 `logger.info('access_token=defensive_secret_DEFC')`,console handler 实际输出 `***...DEFC`(17 个 `*` + 末 4 位 `DEFC`),证明 settings.LOGGING.handlers.console.filters 注册路径打通 +- **Filter 4 种正则形态全部覆盖**(T6):JSON / Python dict repr / URL query / 等号或冒号兜底,每种形态 mask 后保留末 4 位明文(`1234`) +- **不误伤其它敏感字段**(T7):filter 只识别 `access_token` 字段名前缀锚点;`Authorization header: bearer_user_token_xxxxxxx` / `Bearer raw_token_zzz` 经过 filter 后字符级一致 + +## 偏差记录(auto-fixed) + +详见 `03-02-SUMMARY.md` 的 "Deviations from Plan" 段。本阶段共 fixed 2 处: + +1. **[Rule 1 - Bug] 4 个 regex 正则交叉吃掉末 4 位**(Pattern 4 兜底正则把 Pattern 3 已脱敏的 `********1234&u=1` 当 value 整段二次 mask,把 `1234` 吃成 `&u=1`)— Pattern 4 终止符 `[^\s,;)\]\}"\']+` 增加 `&` / `=` 排除字符 +2. **[Rule 1 - Bug] tuple 形态 args 误吃 `%s` 占位符**(filter 先扫 `record.msg` 把 `access_token=%s` 中的 `%s` 当 value mask 成 `**`,Formatter 阶段 `'access_token=**' % ('abcdefgh1234',)` 报 TypeError)— 改为:tuple args 形态先 `record.getMessage()` 拼成最终字符串再整体脱敏,args 清空避免 Formatter 二次拼接 + +--- + +*Phase: 03-client-and-log-mask* +*Verification completed: 2026-05-08*