WIP-356③ · 344「336 切换器运行时三正反例」机位代跑复核底稿(波波)

版本:v1.1(v1=代跑前判据底稿;v1.1=代跑结果已回传,追加 §六 复核结论)
任务卡:目标=复核 344 机位代跑结果;范围=只核证与出判据,不改 344 脚本/后端/证据(跨域属主限制);批准来源=小冈 R1 派工 WIP-356(00:0x)/义父连轴令;验收=判据可机械执行、结论有四态、缺陷登记到条。

一、当前状态:BLOCKED-未代跑(不是 FAIL,也不是 PASS)

项真值
344 证据目录现有文件336切换器-运行时三正反例预演.sh/G7-2-主理人5任务-admin消费侧正式用例.md/README.md/build.log/typecheck.log
是否有代跑结果🔴 无(无输出 txt/log;build.log 是构建日志,与运行时三正反例无关)
判定BLOCKED-未代跑:无对象可复核。判 FAIL 会冤枉交付人(缺的是机位环境,不是交付)
催办:请 R1 在机位(ADMIN_TOKEN + 可访 100.64.1.230:8089)执行并把输出回传本目录;回传后本岗按 §三 判据复核。

二、代跑前的静态复核(本岗已完成,机位照此执行可少跑一次)

2.1 契约 ↔ 实现 ↔ 脚本 三者逐条对账 ✅ 全部一致

脚本用例期望auth_chain.py:108 实现契约 chain-identity-v1 §3结论
正例:切换 active 目的地200 + 受限 JWTcreate_token(..., destination_id=dest["id"], scope=...)§3 定义返回 token✅ 一致
负例1:不存在(123456789)404「目的地不存在」if not dest: raise 404 "目的地不存在"同✅ 一致
负例2:未启用(非 active)403「目的地未启用」raise 403 "目的地未启用,不得签发访问凭证"同✅ 一致

2.2 🔴 脚本四处缺陷(代跑前建议先修,否则结果不可用)

#缺陷后果建议修法
S1负例1/2 只判 HTTP 码、不判 body若将来端点被摘除或路径变更,FastAPI 默认 404/403 会假 PASS加 body 断言:404 须含「目的地不存在」;403 须含「目的地未启用」
S2第 3 例无未启用数据时 CODE=skip ⇒ 走 else 分支 fail+1环境缺数被记成 FAIL,与脚本自述「不伪造」原则冲突增设 blocked 计数,skip 不计 fail
S3末尾恒 exit 0无论失败多少,门禁/CI 无法据此拦截;上一轮本仓实跑 exit 1 是因 ADMIN_TOKEN:? 提前退出,不是用例失败改 exit $((fail>0))
S4依赖机位 python3 解析 JSON机位 py 版本过低可能导致解析失败被误读跑前 python3 -V 登记(AST 类工具另需 ≥3.12)
说明:S1-S3 属脚本健壮性问题,344 交付人自述「机位待跑、不在本地伪造」是诚实的;本岗按缺陷登记,不判 FAIL。

三、代跑结果复核判据(R1 回传后本岗逐条执行)

四态判定:PASS/PASS-附条件/BLOCKED/FAIL。证据五要素缺任一 ⇒ 判 BLOCKED,不判 PASS。

(五要素:命令原文、完整回显、时间戳、hostname、相关版本号)

#复核项PASS 判据BLOCKED 判据FAIL 判据
R1输出五要素齐全五项全有缺任一—
R2正例(active)HTTP 200 且返回 data.token 非空,且该 token 解出 destination_id=目标环境无 active 目的地(须登记造数需求,不伪造)200 但 token 无 destination_id(⇒ 隔离未生效)
R3负例1(不存在)404 且 body 含「目的地不存在」端点不可达404 但 body 为 FastAPI 默认 Not Found(⇒ 假 PASS,见 S1)
R4负例2(未启用)403 且 body 含「目的地未启用」环境无未启用目的地(不判 FAIL,见 S2)返回 200(⇒ 未启用目的地也能换凭证,红线级)
R5跨租户(新增,本轮补)用 A 目的地受限 JWT 显式请求 B 目的地 ⇒ 403「与凭证目的地不符」token 未带 destination_id(走的是 admin 直连路径,非受限 JWT)返回 200 ⇒ 跨租户防线失效,红线级
R6与 352 联动受限 JWT 访问已接 destination 的端点(当前 13 条)返回集随目的地变化端点未接(BLOCKED-待 352)已接但不变化 ⇒ FAIL
R5 是本次新增:原脚本三例覆盖不到「换到 A 后能否看 B」,而这恰是切换器存在的核心风险(见 §四)。

四、🔴 复核期发现:切换器的授权维度缺口(与 WIP-352 联动,报 R1 裁决)

token-exchange 实现完整(防串租户设计正确),但它只做「声明目的地」,不做「授权目的地」:

  1. ✅ 契约路径成立:先 token-exchange 换带 destination_id 的受限 JWT ⇒ 之后访问走 resolve_destination 令牌分支,跨租户 403 生效。
  2. 🔴 缺口 A(无授权表):exchange_token 只校验目的地存在且 active,不校验该用户是否有权访问该目的地;三域亦零授权表(memberships 仅用于 miniapp/workbench 侧)⇒ 任何已登录 admin 可换取任意 active 目的地的凭证。
  3. 🔴 缺口 B(可绕过 token-exchange):未换凭证时,直接用 admin JWT 带 requested 参数访问已接端点亦可通过——因 get_current_user 返回字段不含 destination_id(security.py:33-47),resolve_destination 的跨租户 403(if token_dest is not None)恒不触发;只有 requested 为空时才撞 409(_default_destination 多目的地防串租户,文案即「先做 token 交换」)。
若产品意图当前实现须补动作
admin = 平台全局视角(本就该看全部)✅ 成立契约明写「admin 面不按目的地鉴权」+ 记审计(谁何时切到哪个目的地)
admin = 按目的地隔离🔴 不成立①admin 端点强制受限 JWT(拒绝无 destination_id 的凭证)②或建管理员—目的地授权表
本岗不单方判 FAIL:这是产品题。裁决前,§三 R5 判 BLOCKED-待裁决;裁决为「按目的地隔离」后,R5 转为必过项。
完整机制推演:docs/contracts/admin-a端三大域消费侧核对表-v1.md §二·B;剧本:docs/plans/335-drill-playbook.md §5·C。

五、下一步

  1. R1:机位执行(建议先用 §二·2.2 修好的脚本)→ 回传输出至 docs/delivery/evidence/ba/344/。
  2. 本岗:收到即按 §三 六条判据复核并出签字(预计 15 分钟内)。
  3. 小齐(352):§四 缺口 A/B 若裁决为「按目的地隔离」,则 352 的「绑定」需从过滤升级为鉴权(强制受限 JWT)。
本岗边界:只出判据与复核底稿,未改 344 任何文件、未改后端任何代码。

六、复核结论(2026-09-16 00:3x · 代跑结果已回传 docs/delivery/evidence/ba/344/switcher-l3-run.md)

6.1 四态签字

项判定
344 三正反例(代跑)✅ PASS-附条件(fixture 补齐后 404/403/200 4/4 全绿)
证据五要素⚠️ 附条件:有时间戳/环境/凭证/request_id 逐条可溯;缺命令原文、hostname、镜像或 commit 版本 ⇒ 补此三项即转无条件 PASS
R5 跨租户(本岗新增判据)🔴 BLOCKED-未覆盖(见 6.2)
本岗 356③✅ 收口:代跑前静态复核 + 结果复核均已完成

6.2 🔴 复核发现一:代跑跑的不是 344 脚本里的 token-exchange(口径偏差,须登记)

344 脚本原定本次代跑实测
端点POST /api/miniapp/auth/chain/token-exchangeGET /api/miniapp/admin/merchants?destination_id=N
机制换受限 JWT(带 destination_id)admin 直连 JWT + requested 参数(不换凭证)
语义336 切换器「换凭证」「带参读运营面」

⇒ 契约路径(受限 JWT)本次零覆盖:R2「正例须返回带 destination_id 的受限 JWT」、R5「A 凭证请求 B 应 403」均未测。

⇒ 代跑记录自述「任务原文 URL /api/miniapp/admin/ops/merchants 实测路由级 404,修正为 /api/miniapp/admin/merchants」

—— 该路径勘误成立(admin_ops.py:20 prefix 确为 /api/miniapp/admin,无 /ops 层级),但它是派工口径与 344 脚本口径的偏差,不是脚本错。

⇒ 建议:切换器语义若要闭环,须补跑 token-exchange 三例(本岗判据 R2/R5 已备)。在补跑前,不得据本次结果宣称「336 切换器已验证」。

6.3 🔴 复核发现二:本次代跑反而实证了「缺口 B」

代跑全程用 admin JWT 带 requested,未换受限 JWT 即读到 d=1 的 31 条商户 ⇒ 与本文档 §四「缺口 B:不换凭证直接带 requested 即可绕过 token-exchange」完全吻合。

⇒ 该缺口由「静态推演」升级为「运行时实证」。请 R1 在 §四 裁决时据此定级。

6.4 联动影响:staging 现已有 2 个目的地,但仍不足触发 409

需再插一个 active 目的地才具备 409 触发条件 —— 这恰好是 335 演练第一幕该做的造数,请 R1 提前准备。

6.5 种子纪律小记

destinations 表结构为 id / name / anchor_poi / status,无 is_test 列;代跑记录称「is_test 种子纪律内」,实际以 name 前缀标记(id=1 用 [测试]…、id=2 用 测试…(机位fixture),前缀不统一)。

⇒ 建议:统一 [测试] 前缀,或给表补 is_test 列(属后端属主域,本岗只登记不改)。

本岗边界:只复核与出判据,未改 344 证据目录、未改后端、未改 staging 数据。