Skip to content

「这个 membership 是不是管理员」有两种拼写,大小写敏感性不同:isOrgOrPlatformAdminrole='Owner' 答否,等级尺答是 #5942

Description

@baozhoutao

观察类发现(finding),来自 #5892 / PR #5939 的实现过程。今天没有用户会撞上:两种拼写在所有小写取值上答案一致,而 UI 与 better-auth 写入的都是小写。记录下来是因为它是一条安全路径上的静默分歧。

两种拼写

  1. 等级尺(唯一那把,packages/plugins/plugin-auth/src/invitation-role-cap.ts):parseOrgRoles().trim().toLowerCase(),orgRoleGrade() 据此评级。PR feat(plugin-auth): break-glass 守卫 —— ban 不得停用最后一个管理员(ADR-0024 D5.2) #5939 新导出的 isOrgAdminGrade() 就是它,break-glass ban 守卫用它数管理员。

  2. 手抄版(packages/plugins/plugin-auth/src/auth-manager.ts:3625-3634,isOrgOrPlatformAdmin):

raw.split(',').map((s) => s.trim()).some((r) => r === 'owner' || r === 'admin')

不转小写。这段是 /sso/register 管理员门禁的判据(ADR-0024,fail-closed)。

分歧

sys_member.role 若存进 Owner / ADMIN(导入、外部写入、手工 SQL 都可能),同一行会被:

  • break-glass ban 守卫算作管理员(于是可能允许 ban 掉另一个真管理员 —— 它以为还剩一个);
  • /sso/register 门禁算作非管理员(于是拒绝一个本该允许的注册)。

两个方向的错都不响 —— 没有任何一处会报「这两处不一致」。

为什么现在只是观察

sys_member.role 的可选值来自 BUILTIN_MEMBERSHIP_ROLE_OPTIONS(ADR-0108 的封闭词表),值全为小写;better-auth organization 插件写入的也是小写。所以要造出分歧,得有一条绕过表单的写入。没有查到这样的生产路径。

可能的收口

isOrgOrPlatformAdmin 里那 6 行换成 isOrgAdminGrade(m.role)(语义相同,额外获得大小写与数组拼写的处理),这样「哪种 membership 算管理员」在 plugin-auth 内只剩一个答案。#5939 没有顺手做:那是另一条安全路径上的方法,不属于 #5892 的范围。

顺带记录:platform_admin 的推导目前有三处独立实现 —— packages/core/src/security/resolve-authz-context.ts(权威)、auth-manager.tscustomSessionisOrgOrPlatformAdmin、以及 #5939 的守卫(反方向枚举,resolveAuthzContext 按 user 查,回答不了「谁是管理员」这个集合问题)。四处今天一致,但和上面同属一类风险。

参考


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions