从 #4660 拆出,不阻塞该 PR 合并。#4660 按现状(默认含 import)落地,本 issue 追踪是否收窄。
需要裁决的一句话
system-data 桶的默认 affordance 目前是 create/edit/delete/import/exportCsv: true。import 是否应从默认值中移除,改为按对象显式 opt-in?
为什么单独拿出来
v16 的 managedBy: 'system' 默认 LOCKED,8 个成员各自用 userActions: { create, edit, delete } 重开写入——没有一个重开 import,所以 CSV 导入在 v16 解析为 false。v17 的 system-data 默认可写且这些 userActions 块已删除,于是 import 变成 true。
受影响的是三张 RBAC 关联表:
sys_user_position
sys_user_permission_set
sys_position_permission_set
后果:管理台上出现「CSV 批量绑定权限集 / 岗位」的入口。
不是什么
授权没有被绕过。 affordance 只决定 UI 入口是否出现;CSV 导入写的每一行仍然逐条经过 DelegatedAdminGate、RLS 与权限集裁决。一个无权授予某权限集的 admin,通过 CSV 也授不出去。
那么风险在哪
在杠杆,不在授权边界:逐行点选授权时,一次误操作影响一个人;一份错误 CSV 就是一次批量授权,且没有天然的复核节奏。这三张表恰好是整个 RBAC 的授予面。
溯源(为什么这条没被真正裁决过)
#3355 上 09:49 / 09:59 那两条确定 system-data 与默认 affordance 的评论,署名带 Claude Code 脚注,是早前的 agent 会话写的,不是维护者本人。实现 agent 据此把"默认含 import"当作已拍板事项执行,并主动把该后果标记为 security-adjacent、指出"裁决时可能未考虑批量绑定权限集这一具体场景"。它是对的——作出该裁决的是上游 agent。
PM 建议
桶默认给 create/edit/delete/exportCsv,不含 import;需要导入的对象写 userActions: { import: true } 显式开启。
理由是这样两边都不牺牲:桶依然完整描述其成员(8 个对象仍不需要写任何 userActions 回收权限,不重演 v16「每个成员各自抠回一个动词」的形状),同时把高杠杆的批量授予变成一次显式选择。
若裁决为"收窄"
改动很小,且已有 pin 兜底:
若裁决为"保持现状",关闭本 issue 即可,无需改动。
从 #4660 拆出,不阻塞该 PR 合并。#4660 按现状(默认含
import)落地,本 issue 追踪是否收窄。需要裁决的一句话
system-data桶的默认 affordance 目前是create/edit/delete/import/exportCsv: true。import是否应从默认值中移除,改为按对象显式 opt-in?为什么单独拿出来
v16 的
managedBy: 'system'默认 LOCKED,8 个成员各自用userActions: { create, edit, delete }重开写入——没有一个重开import,所以 CSV 导入在 v16 解析为false。v17 的system-data默认可写且这些userActions块已删除,于是import变成true。受影响的是三张 RBAC 关联表:
sys_user_positionsys_user_permission_setsys_position_permission_set后果:管理台上出现「CSV 批量绑定权限集 / 岗位」的入口。
不是什么
授权没有被绕过。 affordance 只决定 UI 入口是否出现;CSV 导入写的每一行仍然逐条经过
DelegatedAdminGate、RLS 与权限集裁决。一个无权授予某权限集的 admin,通过 CSV 也授不出去。那么风险在哪
在杠杆,不在授权边界:逐行点选授权时,一次误操作影响一个人;一份错误 CSV 就是一次批量授权,且没有天然的复核节奏。这三张表恰好是整个 RBAC 的授予面。
溯源(为什么这条没被真正裁决过)
#3355 上 09:49 / 09:59 那两条确定
system-data与默认 affordance 的评论,署名带 Claude Code 脚注,是早前的 agent 会话写的,不是维护者本人。实现 agent 据此把"默认含import"当作已拍板事项执行,并主动把该后果标记为 security-adjacent、指出"裁决时可能未考虑批量绑定权限集这一具体场景"。它是对的——作出该裁决的是上游 agent。PM 建议
桶默认给
create/edit/delete/exportCsv,不含import;需要导入的对象写userActions: { import: true }显式开启。理由是这样两边都不牺牲:桶依然完整描述其成员(8 个对象仍不需要写任何
userActions回收权限,不重演 v16「每个成员各自抠回一个动词」的形状),同时把高杠杆的批量授予变成一次显式选择。若裁决为"收窄"
改动很小,且已有 pin 兜底:
packages/spec/src/data/object.zod.ts的CRUD_AFFORDANCE_DEFAULTS中system-data行去掉import;managedBy: 'system'bucket →system-data(#3355) #4660 已在 4 个包中为该 flip 埋了逐对象 before/after 等价 pin,收窄后需同步这些 pin 的期望值——它们会直接变红,不会静默通过;userActions: { import: true }。若裁决为"保持现状",关闭本 issue 即可,无需改动。