判据:docs/delivery/acceptance/357-token-exchange补跑与R5复核-v1.md(2026-09-16 08:4x)第三节「配套回归清单」五条
执行:波波(验收检测岗 · A 段定岗「357 闭环」)|时间戳 2026-09-16 09:49|机位 100.64.1.230:8089(staging)
原则:不采信自报实证——修复方小齐已在 docs/delivery/evidence/ba/358/r5-fix-verification.md 出具三例结果,本岗仍独立复跑并补测其未覆盖的两条。
| 项 | 修复前(08:42 实测) | 修复后(09:49 独立复核) | 判定 |
|---|---|---|---|
| R5 跨租户(受限=1 → B=3) | 🔴 200(应 403) | ✅ 403 「请求目的地与凭证目的地不符」 | PASS · 红线销项 |
| R5b 对照(受限=1 → 自身=1) | 200 | 200(31 条) | PASS |
| R5c 受限不带参(走凭证默认) | 🔴 409(应 200) | ✅ 200(31 条) | PASS(语义变更,见 §五) |
| R2/R3/R4 三例 | 200/404/403 | 200/404/403 | PASS · 无回归 |
总判:R5 由 FAIL 转 PASS,本岗建议销项。 357 闭环条件满足(R2/R3/R4 签字收尾 + R5 独立复核通过)。
| # | 用例 | 凭证 | 期望 | 实测 | 条目数 | 判定 |
|---|---|---|---|---|---|---|
| S0 | /auth/me 凭证自证 | admin 主 JWT | 200 | 200 | — | ✅ |
| R2 | token-exchange(active=1) | 主 JWT | 200 | 200 | — | ✅ 锚点 destination_id="1" |
| R3 | token-exchange(不存在 999) | 主 JWT | 404 | 404 | — | ✅ |
| R4 | token-exchange(未启用 2) | 主 JWT | 403 | 403 | — | ✅ |
| R5 | admin/merchants?destination_id=3 | 受限 JWT(dest=1) | 403 | 403 | — | ✅ 红线修复 |
| R5b | ?destination_id=1 | 受限 JWT(dest=1) | 200 | 200 | 31 | ✅ |
| R5c | 不带参(走凭证默认) | 受限 JWT(dest=1) | 200 | 200 | 31 | ✅ |
| R6a | 不带参(多 active) | 主 JWT(无 claim) | 409 | 409 | — | ✅ 回归①②小齐未测 |
| R6b | ?destination_id=1 | 主 JWT(无 claim) | 200 | 200 | 31 | ✅ 小齐未测 |
| R6c | ?destination_id=3 | 主 JWT(无 claim) | 200 | 200 | 0 | ✅ 小齐未测 |
相互印证:主 JWT payload = {"user_id","identities","token_version","exp"}——确无 destination_id claim,因此 R6a/R6b/R6c 正是「修复会不会误伤老凭证/admin 全局视图」的有效探针。
HTTP 码对了不代表数据对了,故加验返回条目数:
| 断言 | 结果 | 说明 |
|---|---|---|
| 受限凭证默认落点 = 自身目的地(R5b 条数 == R5c 条数) | PASS | 31 == 31 ⇒ 不带参确实落到 dest=1,非全局 |
| admin 全局视图未被误伤(主 JWT 仍能取 1 与 3) | PASS | R6b=200(31 条)/R6c=200(0 条) |
| 主 JWT 行为与修复前一致(多 active 仍 409) | PASS | R6a=409,未因修复而静默降级 |
🔴 本岗自曝弱断言:原设计第 2 条断言「R5c 条数 ≤ max(R6b,R6c)」强度不足——目的地 3 的商户数本就为 0(R6c=0 条),该断言在数据全空时也会成立。真正的强证据是 R5 的 403 本身,数据层只能证明「默认落点正确」,无法形成跨目的地数据对照。判据不因弱断言而松动,特此登记以免后续引用时高估证据强度。
上轮报告「容器内 /app 非 git 仓库 ⇒ 缺少 commit 指纹 ⇒ 严格按剧本 §8 应判 BLOCKED」。本轮用三重替代补齐:
/app/app/security.py 第 46-49 行直接读到修复代码与其注释 u["destination_id"] = int(payload["destination_id"]) # R5 红线修复:受限 JWT 的目的地锚点恢复消费(原 claim 被丢弃→tenant.py 跨租户 403 校验死代码,波波 357 R5 实证)
security.py / tenant.py = 2026-09-16 01:20:15 +0000StartedAt = 2026-09-16T01:20:25Z交叉验证:修复 commit e2973cac 时间 = 2026-09-16 09:20:15 +0800 = 01:20:15 UTC,与源码 mtime 完全吻合,容器 10 秒后启动。
⚠️ 强度自评(不夸大):mtime 只能证明「镜像构建时刻」,无法唯一绑定 commit hash;本例的强绑定由第 1 项源码文本提供。
🔴 残留缺口不撤:本例能凑齐是因为源码可读。若换成编译型或压缩产物,同样的方法失效 ⇒ 仍建议构建时写入 /app/COMMIT(或 GIT_SHA 环境变量),否则 335 任何在容器上跑的验收,证据链都缺一角。
本岗认为这是语义改进(有限锚点凭证本就不该走「无目的地上下文」分支),但 chain-identity-v1.md §3 目前只规定「签发」,未规定「不带参时的落点语义」。
⇒ 请契约持有席明确一句:「受限 JWT 不带 destination_id 时,服务端以凭证锚点为目的地(非全局)」。与上轮建议第 3 条同源,属契约侧待补。
admin/merchants 一个读端点。挂 resolve_destination 的其余端点与全部写端点未被 R5 回归覆盖 ⇒ 建议在 335 第五幕补一次多端点抽样,而非以单点全绿代表全局。destination_tokens 审计留痕,reason 前缀 WIP-357闭环-,可追溯)kailash-staging-*)security.py 改动本身——仅验证行为;「自此之后有无新问题」不在本岗结论范围内JWT_SECRET 仍为默认值 dev-secret-change-me(本岗借此取证)——目前仅 Tailscale 内网可达 + APP_ENV=dev,上线前必须配真值签字:PASS(建议销项) —— R5 红线 failures 已由本岗独立复跑实证修复并补齐两条未测回归
上报:@R1(销项 + 判据升必过项)/@契约持有席(§五-1 契约补消费侧+不带参语义)/@小齐(§五-2 多端点回归面)
署名:波波 2026-09-16 09:5x