Skip to content

fix(lint): 删除 validateOrgAxisRedLines 里 spec 合法 stack 到不了的四条分支 (#5009) - #5018

Merged
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-5009-org-axis-dead-branches
Aug 4, 2026
Merged

fix(lint): 删除 validateOrgAxisRedLines 里 spec 合法 stack 到不了的四条分支 (#5009)#5018
xuyushun441-sys merged 1 commit into
mainfrom
claude/issue-5009-org-axis-dead-branches

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Fixes #5009

validate-org-axis-red-lines.ts 注册为 input: 'parsed' —— 它看到的是 ObjectStackSchema 解析后的产物。#4984 修掉了 sharing rule 字段那一层的 ?? 别名读法,同一文件里还留着四条同形分支(议题点名三条,核对时又发现第四条),读的键 spec 都不声明。

逐条核实与处置

每一条都用真实 schema 的 .shapesafeParse 实测核过,不是靠读注释。

原读法 spec 事实(实测) 处置
cfg.permissions ?? cfg.permissionSets ObjectStackSchema.shapepermissionSets。stack 根 strip 未声明键:safeParse({ manifest, permissionSets: [...] }) 成功,但返回的 data 里没有该键 收敛为 cfg.permissions
cfg.sharingRules ?? cfg.sharing(两处) 同上,sharing 也被 strip 收敛为 cfg.sharingRules
str(rule.object ?? rule.objectName)(议题未列,同形第四条) SharingRuleSchema.strict(),按名拒绝:Unrecognized key(s) on this sharing rule: objectName``;而 object必填,解析过的规则上不可能缺 收敛为 str(rule.object)
asArray(object.rowLevelSecurity ?? object.rls) 整段(约 20 行) ObjectSchema.shape 两个键都没有,且 ObjectSchema.strict() —— 带对象级 RLS 的 stack 在 os validate / os build整包拒绝:Unrecognized key(s) on this object: rowLevelSecurity`` 删除

对象级 RLS 从来不是可授权面:authorable-surface.json 里唯一一条是 security/PermissionSet:rowLevelSecurity。删除依据(schema 名 + 该键不存在的证明)已写进 commit message 和规则自己的 ## Scope 段。

危险不在死代码,在误导

这四条对任何 spec 合法 stack 都不执行,所以漏报为零。真正的代价是:那段死代码连 objects[N].rowLevelSecurity[M].using 的诊断 path 都写好了,足以让下一位作者(人或 AI)相信对象级 RLS 是真实授权面并照着写更多代码 —— #5008 的 dev 差点就照它复制了一条同样惰性的遍历。

别名容忍属于 producer 的拒绝,不属于 consumer(Prime Directive #12)。它还把一条指名道姓的 schema 拒绝,悄悄换成了一条永不触发的 gate。

反向验证

  • 删除的分支:把新测试跑在改动前的实现上 —— 29 条由 safeParse fixture 驱动的断言全绿,8 条转红的全部是 (a) 扫源码的 meta-guard 或 (b) 喂非 spec 合法 stack 的新钉子测试。即:删除前后对全部 spec 合法 fixture 行为完全一致,它本来就死。
  • canonical 化的三条:这里要如实说明,与议题预设的 "before 判绿 / after 判红" 不同 —— canonical 键本来就是 ?? 的第一顺位,spec 合法的违规 stack 改动前已经判红。别名 limb 只是额外在 schema 会拒绝的形状上可达。所以正确的反向验证是反过来的:改动前对 permissionSets / objectName 拼法的非法 stack 会判红,改动后判绿,并由钉子测试证明 schema 才是那道门。(validate-expressions.ts:205 那条就没这么幸运 —— canonical 排第三顺位,见 validate-expressions / validate-security-posture 也有同形的 spec 不声明键的 ?? 别名读法(#5009 建议 3 的核对结果) #5017。)
  • 变异验证:四条分支各自加回去跑一遍,每条都至少让两条测试转红。

meta-guard 全覆盖(#4992 模式)

留下的每条遍历分支都要有过 safeParse 的 fixture 能走到;走不到的不允许存在。

  1. declared-key guard —— 规则源码里从 stack / permission set / RLS policy / object / sharing rule 上读的每一个键,必须出现在对应 schema 自己的 Zod .shape 里。扫源码而不是扫行为是刻意的:不可达分支根本没有行为可断言,这正是问题本身。计算键(policy[clause])另用一条断言把 ['using', 'check'] 钉在 RowLevelSecurityPolicySchema 上。
  2. reachability guard(议题提到的"覆盖率式结构性断言",本 PR 落地了)—— 从源码里抽出每个 findings.push 调用点的 rule:path: 模板,断言现存三个点各自被至少一条过 safeParse 的 fixture 触达;并反向断言没有 fixture 产出源码未声明的 path。加回删掉的第四个点会同时触发数量断言和可达性断言。
  3. 规则 ① 的 fixture 现在也走 PermissionSetSchema.safeParse(此前只有 sharing rule 和 object fixture 有这层保护)—— 顺带暴露出旧 fixture 缺 objects / operation,并不 spec 合法。

真实元数据零新红

  • examples/default-permission-sets.ts没有 parent_organization_id,规则 ① 无从触发;
  • examples 里唯一的 rowLevelSecurity 用法在 permission set 上(app-showcase/src/security/permission-sets.ts:57),正是保留的那条分支;
  • 出现的 sharing: 都在 view 定义内(inquiry.view.ts / lead.view.ts),不是 stack 根键,本规则从不读它。

改动纯粹是减法,findings 集合单调不增,不可能新增红。

顺带核对(不在本 PR)

议题建议 3 要求核对邻居规则。validate-expressions.ts(三处)与 validate-security-posture.ts(两处)有同形别名读法,其中 validate-expressions.ts:205/421 更糟 —— canonical 的 condition 排在两个被拒别名之后。已按 Prime Directive #10 记为 #5017(未认领),不在本 PR 修。

验证

pnpm --filter @objectstack/lint test     → 55 files, 1029 passed | 4 skipped
pnpm --filter @objectstack/lint typecheck → tsc --noEmit, clean
npx eslint <两个改动文件>                  → clean

Changeset:.changeset/org-axis-dead-alias-branches.md(@objectstack/lint patch)。改动限于 packages/lint


Generated by Claude Code

该规则注册为 `input: 'parsed'`,看到的是 `ObjectStackSchema` 解析后的产物。
#4984 修掉了 sharing rule 字段层的 `??` 别名读法,同一文件里还留着四条同形
分支,读的键 spec 都不声明 —— 逐条对着 schema 的 `.shape` 与 `safeParse`
实测核过:

- `cfg.permissions ?? cfg.permissionSets` → `cfg.permissions`。
  `ObjectStackSchema.shape` 无 `permissionSets`;stack 根 strip 未声明键,
  实测 `safeParse({ manifest, permissionSets: [...] })` 成功但 `data` 里
  没有该键 —— 规则看到 stack 之前它已经不存在。
- `cfg.sharingRules ?? cfg.sharing`(两处)→ `cfg.sharingRules`。同上。
- `str(rule.object ?? rule.objectName)` → `str(rule.object)`。
  `SharingRuleSchema` 是 `.strict()`,`objectName` 被按名拒绝
  ("Unrecognized key(s) on this sharing rule: `objectName`");`object`
  又是必填,解析过的规则上不可能缺。
- `asArray(object.rowLevelSecurity ?? object.rls)` 整段遍历(约 20 行)
  **删除**。依据:`ObjectSchema.shape` 两个键都没有(实测键表里只有
  `sharingModel` / `access` / `tenancy` 等,无 `rowLevelSecurity`、无 `rls`),
  且 `ObjectSchema` 是 `.strict()` —— 带对象级 RLS 的 stack 在
  `os validate` / `os build` 被整包拒绝,报 "Unrecognized key(s) on this
  object: `rowLevelSecurity`"。对象级 RLS 从来不是可授权面
  (`authorable-surface.json` 里只有 `security/PermissionSet:rowLevelSecurity`)。

对任何 spec 合法的 stack,判定结果不变 —— 反向验证:新测试跑在改动前的
实现上,29 条由 `safeParse` fixture 驱动的断言全绿,8 条转红的全部是
(a) 扫源码的 meta-guard,或 (b) 喂非 spec 合法 stack 的新钉子测试。

代价从来不是漏报,是误导:那段死代码连 `objects[N].rowLevelSecurity[M].using`
的诊断 path 都写好了,足以让下一位作者相信对象级 RLS 是真实授权面并照着写
(#5008 差点如此)。别名容忍属于 producer 的拒绝,不属于 consumer(Prime
Directive #12)。

meta-guard(#4992 模式),让下一条死分支在 review 前就红:

- declared-key guard:规则源码里从 stack / permission set / RLS policy /
  object / sharing rule 上读的每个键,必须出现在对应 schema 自己的 `.shape`
  里。扫源码而非行为是刻意的 —— 不可达分支没有行为可断言。
- reachability guard:每个 `findings.push` 调用点都必须被至少一条过
  `safeParse` 的 fixture 触达(现存三个点,全覆盖)。
- 规则 ① 的 fixture 现在也走 `PermissionSetSchema.safeParse`。

四条分支各自做过变异验证:加回任意一条,至少两条测试转红。

真实元数据零新红:examples/ 与 default-permission-sets 中无
`parent_organization_id`,唯一的 `rowLevelSecurity` 用法在 permission set
(保留的那条分支)上,`sharing:` 出现在 view 定义内而非 stack 根。

邻居规则同形别名读法已另行记账为 #5017(未认领),不在本 PR 范围。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018iARDqtrhQgz6fVHDeDkbQ
@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 3, 2026 8:42pm

Request Review

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/lint.

3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/automation/hook-bodies.mdx (via @objectstack/lint)
  • content/docs/permissions/authorization.mdx (via @objectstack/lint)
  • content/docs/releases/v17.mdx (via @objectstack/lint)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

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

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

2 participants