目的:R5 修复后,回归面只有 admin/merchants 单端点、写端点零覆盖 ⇒ 补一张可复用的多端点矩阵,并先跑一轮试点验证矩阵可用。
本岗边界:方案/验收检测岗,不改任何后端代码;实测只走拒绝侧(不产生业务数据变更,探针副作用见 §五)。
四态口径引用剧本 §7;证据五要素引用 §8。
静态盘点(backend/app/routers/*.py,按「函数内出现 resolve_destination」计数):
| 类 | 数量 | 说明 |
|---|---|---|
| 消费点总行数 | 79 | 含同一函数内多次调用 |
| GET(读) | 34 | 跨目的地应 403 |
| POST | 16 | 写端点 |
| PUT | 3 | 写端点 |
| DELETE | 4 | 写端点 |
| 未归属到装饰器(helper/其它) | 22 | 需人工分派 |
写端点函数清单(23 个,去重后):admin_backend(create/update/delete cms_post、sign_merchant、update_merchant_status、intervene_order、create/update/delete product、add/delete component、admin_execute_refund)/admin_distribution(risk_ruling、admin_generate_bills)/admin_ops(create_merchant、create_temple、delete_temple)/miniapp_pay(prepay)/miniapp_workbench_redeem(redeem_arrival)/safety(trigger_sos、cancel_sos、submit_assessment、submit_health_check)。
| 组 | 用例 | 判据 | 安全等级 |
|---|---|---|---|
| R-1 读·跨目的地 | 受限 JWT(dest=1) + ?destination_id=3 | 403(200 ⇒ FAIL:串租户或未接线) | 安全(只读) |
| R-2 读·同目的地 | 受限 JWT(dest=1) + ?destination_id=1 | 200(非 200 ⇒ 端点路径错或未接线) | 安全(只读) |
| W-1 写·拒绝侧 | 受限 JWT(dest=1) + ?destination_id=3 + 最小合法体 | 非 2xx,且数据库计数零变更 | 拒绝侧不落库 ⇒ 不产生业务数据变更;若返回 2xx 立即停跑并报残留 |
W-1 的安全依据是既有判据:「越权硬断言=拒绝时数据库零变更」。故写端点无需造业务数据即可实测。
100.64.1.230:8089)| # | 端点 | 用例 | 期望 | 实测 | 判 |
|---|---|---|---|---|---|
| A1 | /api/miniapp/admin/merchants | 跨=3/自=1 | 403/200 | 403/200 | ✅ PASS |
| A2 | /api/admin/cms/posts | 跨=3/自=1 | 403/200 | 403/200 | ✅ PASS |
| A3 | /api/miniapp/admin/content/pois | 跨=3/自=1 | 403/200 | 200/200 | 🔴 FAIL |
| A4 | /api/admin/stats/overview | 跨=3/自=1 | 403/200 | 403/200 | ✅ PASS |
| A5 | /api/miniapp/admin/orders | 跨=3/自=1 | 403/200 | 404/404 | ⚪ 用例错(端点路径不存在,非缺陷) |
| B1 | POST /api/admin/cms/posts | 跨=3 | 403 + 零变更 | 403 | ✅ PASS |
| B2 | POST /api/miniapp/admin/merchants | 跨=3 | 403 + 零变更 | 🔴 200(落库一行) | 🔴 FAIL |
| B3 | POST /api/miniapp/admin/content/pois | 跨=3 | 403 + 零变更 | 🔴 200(落库一行) | 🔴 FAIL |
| B4 | POST /api/admin/products | 跨=3 | 403 + 零变更 | 403 | ✅ PASS |
数据库快照:cms_posts=0,merchants=31,pois=8 → cms_posts=0,merchants=32,pois=9(+2 行=探针副作用,见 §五)。
admin_ops.py:109ctx = await resolve_destination(user, requested=None) # ← requested 写死 None
row = await conn.fetchrow("INSERT INTO merchants (…) VALUES (…,$5) …", ctx.destination_id)
?destination_id=3,新建商户 id=30 实际落到 destination_id=1(SQL 直查确认)。requested=None 硬编码 5 个:admin_distribution.risk_ruling、admin_distribution.admin_generate_bills、admin_ops.admin_create_merchant、admin_ops.admin_create_temple、admin_ops.admin_delete_temple;resolve_destination(user),而签名 requested: int | None = None ⇒ 等价 None):miniapp_pay.prepay、miniapp_workbench_redeem.redeem_arrival、safety.{trigger_sos, cancel_sos, submit_assessment, submit_health_check};requested=destination_id 的 12 个(admin_backend 全部 12 个); ⇒ 🔴 11/23(48%)写端点不消费请求侧目的地。其中 miniapp_pay.prepay(支付下单)与 safety.*(SOS/安全上报)属高敏写端点,建议优先定性:是「产品本就按凭证锚定」还是「漏接校验」。
pois 表有 destination_id 列,故具备接线条件,属漏接而非无列)。content.py 未出现在 resolve_destination 的 20 个文件清单中 ⇒ 该域整块未接。/api/miniapp/admin/orders 不存在(404/404)⇒ 属本岗用例路径写错,不是产品缺陷;已在矩阵中标注替换端点。
| 对象 | 位置 | 状态 | 处理 |
|---|---|---|---|
merchants id=30 name=363regression-probe | dest=1 | 仍在库 | 无 DELETE 端点 ⇒ 请小齐清理 |
pois id=9 name=363regression-probe | dest=1 | 仍在库(DELETE 返回 200 但未真删,疑软删) | 请小齐清理 |
教训:即便只跑「拒绝侧」,一旦隔离失效就一定会落库 ⇒ 后续跑 W-1 组必须:①每次跑前先按名字 363regression-probe 查残留 ②优先选有 DELETE 端点且能真删的对象 ③确认删除后复查计数。
requested= 传参(机械比对,本岗可接,属读操作无风险)。署名:波波 2026-09-16 11:4x(WIP-363② · 多端点回归矩阵草案 + 首轮试点)