发现于 #4983 的实现过程(为 permissions[].rowLevelSecurity[].using/.check 立 authoring gate 时,照着 validateOrgAxisRedLines 的遍历核对可授权面)。按 Prime Directive #10 单独记账 —— 不在那一单的范围内,且该文件当时正被 #4991 占用。
事实
packages/lint/src/validate-org-axis-red-lines.ts(main,含 #4984 与 #5004 之后)仍有三处 ?? 别名读法:
const permissionSets = asArray(cfg.permissions ?? cfg.permissionSets);
...
asArray(object.rowLevelSecurity ?? object.rls).forEach(...)
...
asArray(cfg.sharingRules ?? cfg.sharing).forEach(...) // 两处
对照 spec 实际声明的键:
| 读法 |
spec 现状 |
cfg.permissions |
✅ StackSchema 唯一声明的 permission-set 键(stack.zod.ts) |
cfg.permissionSets |
❌ 未声明 |
object.rowLevelSecurity |
❌ ObjectSchema 根本没有这个键 —— rowLevelSecurity 只声明在 PermissionSetSchema 上(permission.zod.ts),authorable-surface.json 里也只有 security/PermissionSet:rowLevelSecurity 一条 |
object.rls |
❌ 未声明;rls 只是 PERMISSION_SET_KEY_ALIASES 里 → rowLevelSecurity 的被拒别名 |
cfg.sharingRules |
✅ 声明 |
cfg.sharing |
❌ 未声明 |
后果
该规则是 input: 'parsed'。Zod object 默认 strip 未声明键,所以在 os validate / os build 这两道门上,permissionSets / objects[].rowLevelSecurity / objects[].rls / sharing 在规则看到 stack 之前就已经不存在了 —— 这几条分支对任何 spec 合法的 stack 都永远不执行。只有 os lint(不 parse,跑 normalized 层)才可能命中,而能命中的恰恰是 os validate / os build 会拒绝的 stack。
这正是 #4984 的缺陷形状:红线读了被拒的别名,于是对每一个 schema 接受的 stack 都是惰性的。#4984 修掉了 sharing rule 那一半(criteria / filter / sharedTo / recipient),并在文件里写下了理由 ——
Alias tolerance belongs at the schema's refusal, not in a consumer (Prime Directive #12).
—— 但同一文件里另外三处 ?? 没有一起清掉。其中 objects[].rowLevelSecurity 那一整段(约 20 行,连同 objects[N].rowLevelSecurity[M].using 这条 path 格式)是整块死代码:对象级 RLS 从来不是可授权面。
危险不在漏报,在于误导:该段落 + validate-org-axis-red-lines.test.ts 里对应的用例,会让下一个作者(或 AI 作者)合理地相信 objects[].rowLevelSecurity 是一个真实的授权面并照着写,而运行期没有任何东西读它。
建议
- 三处
?? 收敛为声明键:cfg.permissions、cfg.sharingRules。
- 删掉
objects[].rowLevelSecurity ?? objects[].rls 那一整段遍历(以及测试里对应的两条用例),或者——如果对象级 RLS 是想要的授权面——那是一次 spec 变更(给 ObjectSchema 加键 + 运行期读者),不是 lint 里的一条 ??。二选一,别两头都不落地。
- 顺带核对:
validate-security-posture.ts 等邻居规则是否有同形别名读法。
参考
发现于 #4983 的实现过程(为
permissions[].rowLevelSecurity[].using/.check立 authoring gate 时,照着validateOrgAxisRedLines的遍历核对可授权面)。按 Prime Directive #10 单独记账 —— 不在那一单的范围内,且该文件当时正被 #4991 占用。事实
packages/lint/src/validate-org-axis-red-lines.ts(main,含 #4984 与 #5004 之后)仍有三处??别名读法:对照 spec 实际声明的键:
cfg.permissionsStackSchema唯一声明的 permission-set 键(stack.zod.ts)cfg.permissionSetsobject.rowLevelSecurityObjectSchema根本没有这个键 ——rowLevelSecurity只声明在PermissionSetSchema上(permission.zod.ts),authorable-surface.json里也只有security/PermissionSet:rowLevelSecurity一条object.rlsrls只是PERMISSION_SET_KEY_ALIASES里 →rowLevelSecurity的被拒别名cfg.sharingRulescfg.sharing后果
该规则是
input: 'parsed'。Zod object 默认 strip 未声明键,所以在os validate/os build这两道门上,permissionSets/objects[].rowLevelSecurity/objects[].rls/sharing在规则看到 stack 之前就已经不存在了 —— 这几条分支对任何 spec 合法的 stack 都永远不执行。只有os lint(不 parse,跑 normalized 层)才可能命中,而能命中的恰恰是os validate/os build会拒绝的 stack。这正是 #4984 的缺陷形状:红线读了被拒的别名,于是对每一个 schema 接受的 stack 都是惰性的。#4984 修掉了 sharing rule 那一半(
criteria/filter/sharedTo/recipient),并在文件里写下了理由 ———— 但同一文件里另外三处
??没有一起清掉。其中objects[].rowLevelSecurity那一整段(约 20 行,连同objects[N].rowLevelSecurity[M].using这条 path 格式)是整块死代码:对象级 RLS 从来不是可授权面。危险不在漏报,在于误导:该段落 +
validate-org-axis-red-lines.test.ts里对应的用例,会让下一个作者(或 AI 作者)合理地相信objects[].rowLevelSecurity是一个真实的授权面并照着写,而运行期没有任何东西读它。建议
??收敛为声明键:cfg.permissions、cfg.sharingRules。objects[].rowLevelSecurity ?? objects[].rls那一整段遍历(以及测试里对应的两条用例),或者——如果对象级 RLS 是想要的授权面——那是一次 spec 变更(给ObjectSchema加键 + 运行期读者),不是 lint 里的一条??。二选一,别两头都不落地。validate-security-posture.ts等邻居规则是否有同形别名读法。参考
unit_and_subordinates也判红 (#4991) #5004(同文件的覆盖面修复)validate-rls-predicate-enforceability.ts采取了相反做法:只读cfg.permissions,并用一条测试钉住permissionSets/objects[].rowLevelSecurity判绿,理由写在该文件## Scope段。