现状
examples(devicecell/ordercell)的 slice 级 contract 测试 把生成 handler 直接挂载执行
(mux.Handle(method+path, h) 或 h.ServeHTTP(rec, req)),而生成 handler 的 ServeHTTP
直接调 handle、绕过 auth.Mount 注入的 route gate (gate 仅在 RegisterRoutes 经
auth.Mount 生效)。因此 owner-scoped 端点(order:read/order:update/device:read/
device:consume,resource: id)的 403 / owner-deny 授权边界在 contract 测试层不被触发 。
证据(#2486 迁移后,以 orderquery 为例):
// examples/todoorder/cells/ordercell/slices/orderquery/contract_test.go:51-57
h := getv1 .NewHandler (svc , testResolver ())
mux := http .NewServeMux ()
mux .Handle (c .HTTP .Method + " " + c .HTTP .Path , h ) // h.ServeHTTP→handle,不经 auth.Mount gate
...
mux .ServeHTTP (rec , req ) // 无 auth context 也得 200
c .ValidateHTTPResponseRecorder (t , rec )
三维根因:
影响范围:
修复方向
三级方案种子:
最小 :在各 examples contract 测试补一行注释说明「直挂 handler、gate 见 listener_auth_test」
(部分已在 [authz] examples(devicecell/ordercell)HTTP 授权 contract-derived 迁移(#2355 续波 PR-A) #2486 内置 review 补,剩余 slice 补齐)。
彻底 :把 examples contract 测试改为经 RegisterRoutes(auth.Mount) 挂载 + 注入 test
Authorizer,新增「owner self → 200 / 非 owner → 403」用例,使 examples 在 contract 层
示范 owner-scoped 授权测试(照搬 corecells/accesscore 的 contract 测试范式)。
重构 :抽 examples/*/.../*test 共享的 contract-gate 测试 harness(mount via auth.Mount +
允许/拒绝 Authorizer 注入),统一两个 example cell 的授权测试样板。
附带小项(同批可做,范本打磨):
examples/iotdevice/cells/devicecell/slices/devicestatus/handler_test.go:34
testStatusAuthorizer 用字面量 "role:operator",应改 dto.RoleOperator 常量与 authorizer.go 统一。
examples/iotdevice/contracts/http/device/command/{enqueue,enqueue-async}/v1/contract.yaml
可加注释说明 path {id} 是 target device、非 caller,故 coarse device:command(admin/operator-only)
不声明 resource(设计正确,仅澄清意图)。
Files
examples/todoorder/cells/ordercell/slices/orderquery/contract_test.go:47
examples/todoorder/cells/ordercell/slices/ordercreate/contract_test.go:40
examples/todoorder/cells/ordercell/slices/orderconfirm/contract_test.go:39
examples/todoorder/cells/ordercell/slices/orderprojection/contract_test.go:34
examples/iotdevice/cells/devicecell/slices/devicestatus/contract_test.go:49
examples/iotdevice/cells/devicecell/slices/devicelist/contract_test.go:65
examples/iotdevice/cells/devicecell/slices/devicecommand/contract_test.go:52
Source
PR #2493 finding F4(测试 / 安全 / DX 三维度同源);Discovered via /ship #2486
现状
examples(devicecell/ordercell)的 slice 级 contract 测试把生成 handler 直接挂载执行
(
mux.Handle(method+path, h)或h.ServeHTTP(rec, req)),而生成 handler 的ServeHTTP直接调
handle、绕过auth.Mount注入的 route gate(gate 仅在RegisterRoutes经auth.Mount生效)。因此 owner-scoped 端点(order:read/order:update/device:read/device:consume,resource: id)的 403 / owner-deny 授权边界在 contract 测试层不被触发。证据(#2486 迁移后,以 orderquery 为例):
三维根因:
ServeHTTP),gate 在RegisterRoutes才生效;迁移前用
allowAllContractPolicy占位、迁移后用testResolver()占位,绕过行为两版一致。cells/*/listener_auth_test.go、cell_test.go经 cell.Init→RouteGroup→auth.Mount 走完整 gate,已有 403/owner-deny/cross-device 用例);
contract 测试只验 schema/handler 逻辑。
allowAllContractPolicy即同款绕过),[authz] examples(devicecell/ordercell)HTTP 授权 contract-derived 迁移(#2355 续波 PR-A) #2486 仅原样保留,非本 PR 回归。影响范围:
孤立看 contract 测试会误以为这些端点无鉴权要求。
devicecommand 的 contract 测试均同款绕过(Grep
ServeHTTP+testResolver≈ 7 slice)。修复方向
三级方案种子:
(部分已在 [authz] examples(devicecell/ordercell)HTTP 授权 contract-derived 迁移(#2355 续波 PR-A) #2486 内置 review 补,剩余 slice 补齐)。
RegisterRoutes(auth.Mount) 挂载 + 注入 testAuthorizer,新增「owner self → 200 / 非 owner → 403」用例,使 examples 在 contract 层
示范 owner-scoped 授权测试(照搬
corecells/accesscore的 contract 测试范式)。examples/*/.../*test共享的 contract-gate 测试 harness(mount via auth.Mount +允许/拒绝 Authorizer 注入),统一两个 example cell 的授权测试样板。
附带小项(同批可做,范本打磨):
examples/iotdevice/cells/devicecell/slices/devicestatus/handler_test.go:34testStatusAuthorizer用字面量"role:operator",应改dto.RoleOperator常量与 authorizer.go 统一。examples/iotdevice/contracts/http/device/command/{enqueue,enqueue-async}/v1/contract.yaml可加注释说明 path
{id}是 target device、非 caller,故 coarsedevice:command(admin/operator-only)不声明
resource(设计正确,仅澄清意图)。Files
Source
PR #2493 finding F4(测试 / 安全 / DX 三维度同源);Discovered via /ship #2486