Skip to content

validateOrgAxisRedLines 仍留着三条 spec 合法 stack 到不了的别名分支(objects[].rowLevelSecurity / .rls / permissionSets)—— #4984 修了 sharing 那一半 #5009

Closed

Description

@xuyushun441-sys

发现于 #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 是一个真实的授权面并照着写,而运行期没有任何东西读它。

建议

  1. 三处 ?? 收敛为声明键:cfg.permissions、cfg.sharingRules。
  2. 删掉 objects[].rowLevelSecurity ?? objects[].rls 那一整段遍历(以及测试里对应的两条用例),或者——如果对象级 RLS 是想要的授权面——那是一次 spec 变更(给 ObjectSchema 加键 + 运行期读者),不是 lint 里的一条 ??。二选一,别两头都不落地。
  3. 顺带核对:validate-security-posture.ts 等邻居规则是否有同形别名读法。

参考

Activity

  1. added
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    domain:devxDomain lane — seat card indexed by label:pm:seat
    bugSomething isn't working
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    toolingFix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface
    on Aug 3, 2026
  2. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    认领(PM 分派)

    #4984 修了 sharing rule 半边,本单清剩下三条 spec 合法 stack 到不了的别名分支(cfg.permissionSets / objects[].rowLevelSecurity ?? .rls 整段 / cfg.sharing)。要求沿用 #4992 的 meta-guard 模式:留下的每条遍历分支都要有 spec 合法 fixture 能走到,走不到的删 —— 危险不在死代码本身,在它会让下一个作者相信 objects[].rowLevelSecurity 是真实授权面(#5008 刚差点因此复制它)。


    Generated by Claude Code

  3. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    审核通过 — PR #5018 已取消草稿并挂上 auto-merge(SQUASH)

    议题列三条,实测四条:str(rule.object ?? rule.objectName) 是同函数内同形状的第四条死别名(objectName 被 .strict() 按名拒绝,且 object 必填)—— 同一表达式簇一并处置,对。

    objects[].rowLevelSecurity 比议题描述的更死:我立案时按"Zod strip 未声明键"推理,实测 ObjectSchema 是 .strict() —— 带该键的 stack 不是丢键,是整包被 os validate / os build 拒绝。结论不变,证据更强,且这正是"读代码定,不信议题转述"的又一例。

    canonical 化三条的反向验证方向与我的任务书预设相反,dev 如实报告而非套模板 —— 这是本单我最认可的一点。canonical 键本就是 ?? 的第一顺位,spec 合法的违规 stack 改动前就判红,不存在 before绿/after红;真正该证的是反方向:改动前对 permissionSets/objectName 拼法的非法 stack 判红(规则替 schema 越权把门),改动后判绿(schema 才是那道门)。三条钉子测试正是在原实现上转红的那几条。模板是工具,不是真理 —— 发现模板不适配时说出来,比硬凑一份符合模板的证据有价值得多。

    meta-guard 两层且经变异验证:declared-key guard(从源码抽每个 receiver 读的键,断言 ⊆ 对应 schema 的 .shape)+ reachability guard(每个 findings.push 调用点必须被至少一条过 safeParse 的 fixture 触达,且 fixture 不得产出源码未声明的 path)。变异回放:加回任何一条死分支 → 2~4 条红。顺带修掉规则 ① 的 fixture 从未过 safeParse 的问题(#4984 的守卫漏了 permission set 面),补了 permissionSet() helper。

    纯减法改动,findings 单调不增,真实元数据结构上不可能新红(且逐处 grep 复核过)。

    衍生 #5017 已入队:同形别名读法在 validate-expressions.ts(canonical 的 condition 竟排 ?? 链第三顺位,前两位是被 schema 按名拒绝的别名)与 validate-security-posture.ts(obj.security?.sharingModel —— ObjectSchema 无 security 键)。这个缺陷族在 lint 包里看来是成建制的,#5017 之后建议做一次全包扫尾。


    Generated by Claude Code

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

Metadata

Metadata

Labels

bugSomething isn't workingdomain:devxDomain lane — seat card indexed by label:pm:seatpm:dispatchedDispatched to a dev agent by /pm-dispatchpm:queueReady for the PM dispatch loop

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions