Skip to content

validate-expressions / validate-security-posture 也有同形的 spec 不声明键的 ?? 别名读法(#5009 建议 3 的核对结果) #5017

Closed

Description

@xuyushun441-sys

按 #5009 建议 3 顺带核对了邻居规则。validate-org-axis-red-lines.ts 的四条已在该单清掉;下面这些是同一形状、不同文件的读法,按 Prime Directive #10 单独记账,不在 #5009 的 PR 范围内。

每一条都对着 spec 实际 .shape 核过。

packages/lint/src/validate-expressions.ts

行 读法 spec 现状
205 / 421 rule.expression ?? rule.predicate ?? rule.condition ?? rule.formula 跨字段校验规则声明的键是 condition。formula / expression 是 validation.zod.ts 里 aliases: { formula: 'condition', expression: 'condition' } 明确按名拒绝的别名;predicate 连别名都不是。注意这里 canonical 的 condition 排在第三顺位 —— 前两个非法别名优先
414 obj.validations ?? obj.validationRules ObjectSchema.shape 只有 validations,没有 validationRules
595 rule.condition ?? rule.criteria ?? rule.predicate SharingRuleSchema 只声明 condition;criteria 正是 sharingRuleUnknownKeyError 里 → condition 的被拒别名(#4984 在另一个文件里修的就是这一条)

packages/lint/src/validate-security-posture.ts

行 读法 spec 现状
94 obj.sharingModel ?? (obj.security)?.sharingModel ObjectSchema.shape 有 sharingModel / externalSharingModel / publicSharing,没有 security
118 def.reference ?? def.reference_to field.zod.ts:331 把 referenceTo / relatedTo / target 等映射为被拒别名 → canonical reference

为什么要记账

两个文件都是 input: 'parsed' 注册的 gating 规则(见 authoring-rules.ts),看到的是 ObjectStackSchema 解析后的产物:stack 根 strip 未声明键,.strict() 子 schema 直接整包拒绝。所以这些别名 limb 对任何 spec 合法 stack 都不执行 —— 和 #4984 / #5009 完全同一种形状。

危险同样不在漏报(canonical limb 通常也在读),在于:

  1. 读代码的人会相信 objects[].validationRules、object.security.sharingModel 是真实的授权/校验面;
  2. validate-expressions.ts:205/421 那条尤其糟 —— canonical 的 condition 排在两个被拒别名之后,只要作者写了被拒的 expression,规则读的就是它(在 normalized 层),而 os validate 会拒掉整个 stack。producer 和 consumer 对同一份元数据给出两套说法。

建议

  1. 三处收敛为声明键:condition、validations、sharingModel、reference。
  2. 复用 validateOrgAxisRedLines 仍留着三条 spec 合法 stack 到不了的别名分支(objects[].rowLevelSecurity / .rls / permissionSets)—— #4984 修了 sharing 那一半 #5009 落地的结构性 meta-guard:规则源码里从某个 surface 上读的每个键,必须出现在该 surface 自己的 Zod .shape 里(validate-org-axis-red-lines.test.ts 的 READ_SURFACES 表)。那条 guard 是通用的,搬过来即可,并且它扫的是源码不是行为 —— 不可达分支没有行为可断言,这正是问题本身。
  3. 顺带把该 guard 推广成所有 input: 'parsed' 规则共用的一条测试,是更彻底的做法(成本更高,可另议)。

参考

Activity

  1. xuyushun441-sys commented on Aug 3, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    认领(PM 分派)

    本单是 #4984 → #5009 同族的第三轮扫尾。派发要求:沿用 #5018 的两层 meta-guard 模式(declared-key ⊆ schema.shape + reachability),并注意 #5018 已证明的一点 —— canonical 排 ?? 首位时反向验证方向要反过来(改动前对非法拼法越权判红,改动后让位给 schema)。validate-expressions.ts:205 那条 canonical condition 排第三顺位的,行为可能真的会变(别名短路),要单独测。


    Generated by Claude Code

  2. xuyushun441-sys commented on Aug 4, 2026

    @xuyushun441-sys
    CollaboratorAuthor

    审核通过 — PR #5046 已取消草稿并挂上 auto-merge(SQUASH);第三轮扫尾收官

    议题点名 5 条链,全文 grep 又找出 2 条,7 条全部处置;真实元数据(三个 example app + DEFAULT_PERMISSION_SETS)before/after findings 序列化 byte-identical;7 条被删 limb 逐一 mutation 回放,8 组全红。

    三点超出模板的判断,都对:

    1. canonical 排第三那条链的病比预想的重:实测 case B —— canonical 的 condition 写了 record.no_such_field,而报告出来的却是别名键的 null-guard 判定,真缺陷从未被报。消费者的整个答案是关于一个生产者拒收的键。老链在测试里重建为 OLD_CHAIN,差异是演示出来的不是断言出来的。
    2. 方向有第三种:fix(lint): 删除 validateOrgAxisRedLines 里 spec 合法 stack 到不了的四条分支 (#5009) #5018 教训是"canonical 首位时删 limb 让规则让位给 schema";本单发现 def.reference ?? def.referenceTo 喂的是计数,删 limb 反而新增诊断(master 计数 1→0 触发下游 parent-scope 门)。逐条写 lintAfter 而非假设统一沉默 —— "增/减/翻向"三分法进方法论。
    3. 对 fix(lint): 删除 validateOrgAxisRedLines 里 spec 合法 stack 到不了的四条分支 (#5009) #5018 的 reachability 判据做了有据的偏离:validate-security-posture 的多条规则存在的意义就是给 zod 拒收的值(如 sharingModel:'read')一条更好的报错 —— 平铺的 safeParse-success 判据会逼着删掉四条正当规则。改成"无 unrecognized_keys":被拒的键才是 parsed 路径够不到的东西,被拒的值不是。 键/值之分是承重思想,写进了 docblock。

    衍生 #5026 的处置尤其克制:if (f.formula) 是无 canonical limb 的纯别名读 —— 收敛它不是删死代码,是激活一条从未跑过的检查(整个 field-formula 校验对 spec 合法 stack 从未生效,真实元数据全写 expression:),自带验证义务,不该夹在扫尾 PR 里。立单,且在 declared-key guard 里作为唯一显式 TRACKED_UNDECLARED_READS 条目带着("此表只许缩不许长")—— 债务可见而非豁免。#5026 属 bug 类,列入下一波(维护者新规:只派 bug)。

    顺带修的两处见微知著:既有 fixture 六处用被拒的 expression: 拼法(测试自己在非法元数据上断言);lazySchema 代理函数目标,typeof === 'object' 守卫会让所有惰性 schema 静默回答"什么都不声明" —— 每面加了 declared.length > 0 防空转。

    议题一处陈述过期照实报:predicate 在当前 main 是别名(四名映射齐全),结论不变。


    Generated by Claude Code

  3. added a commit that references this issue on Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions