Skip to content

validateOrgAxisRedLines 读的 sharing-rule 键是 spec 拒收的:ADR-0105 D6 ① 在 criteria 路径上从不触发 #4984

Description

@xuyushun441-sys

发现于 #4698 的实现过程。同一缺陷类,只是方向反过来:不是 producer 写了没人读的键,而是 consumer 读了没人写的键。

事实

packages/lint/src/validate-org-axis-red-lines.ts 判 ADR-0105 D6 ①(禁止沿 org 树做权限继承)时,对 sharing rule 这样取值:

asArray(cfg.sharingRules ?? cfg.sharing).forEach((rule, rIndex) => {
  const criteria = JSON.stringify(rule.criteria ?? rule.filter ?? '');
  const sharedTo = JSON.stringify(rule.sharedTo ?? rule.recipient ?? '');
  if (criteria.includes(ORG_PARENT_FIELD) || sharedTo.includes(ORG_PARENT_FIELD)) { /* error */ }
});

但 SharingRuleSchema(packages/spec/src/security/sharing.zod.ts)是 .strict(),声明的键集是:

name, label, description, object, active, accessLevel, sharedWith, type, condition

criteria / filter / sharedTo / recipient 全部只作为被拒别名出现在 sharingRuleUnknownKeyError 的 aliases 映射里 —— 那是给报错信息用的处方,不是被接受的键。

实测(build 后的 spec):

condition => ACCEPTED keys=name,object,active,accessLevel,sharedWith,type,condition
criteria  => REJECTED: Invalid input
sharedTo  => REJECTED: Invalid input

后果

validateOrgAxisRedLines 在 registry 里是 input: 'parsed',即跑在 Zod parse 之后。对任何 spec-valid 的 stack,rule.criteria / rule.filter / rule.sharedTo / rule.recipient 恒为 undefined,JSON.stringify(undefined ?? '') 得 '""',includes('parent_organization_id') 恒为 false。

于是:一条声明为 error、以 ADR-0105 D6 红线为依据的 gate,在 sharing-rule criteria 这条路径上永远不会触发。 作者写

defineSharingRule({
  name: 'hq_sees_children',
  type: 'criteria',
  object: 'work_order',
  sharedWith: { type: 'team', value: 'hq' },
  condition: "record.parent_organization_id == 'org_hq'",   // ← 正是 D6 ① 禁的形状
})

会一路绿灯通过 os validate / os build / os lint。

同一文件里 RLS 那两段(permissions[].rowLevelSecurity[].using/check、objects[].rowLevelSecurity[])读的键是对的,只有 sharing-rule 这段错。ORG_AXIS_CROSS_ORG_BU_GRANT(②)也读 rule.sharedTo ?? rule.recipient,同样恒空 —— 那条同样从不触发。

修法

把两处取值改到 spec 的键上:rule.condition(而不是 criteria/filter)、rule.sharedWith(而不是 sharedTo/recipient)。

两点值得在修的时候一起想清楚,不要机械替换:

  1. 是否保留别名读法。 这条规则也会被 os lint 在 normalized(pre-parse)层跑一次,那一层理论上还能看见作者写的别名。但按 Prime Directive Add comprehensive test suite for Zod schema validation #12,别名不该在 consumer 侧用 ?? 容忍 —— 别名已经在 strictUnknownKeyError 里有明确处方并被 reject,parse 就是那道门。建议只读 canonical 键,别名交给 spec 的拒收信息。
  2. condition 是 ExpressionInputSchema。 parse 之后它是 { dialect: 'cel', source } 信封,pre-parse 时可能是裸字符串。两种形状都要能取到 source(可参照 validate/lint have no check for "declared but never read" metadata — three instances found in one app in a day #4698 那条新规则里的 toCompilerInput)。

回归测试

现有的 validate-org-axis-red-lines.test.ts 里 sharing-rule 那几例用的正是 criteria / sharedTo,所以测试是绿的而规则是死的 —— 测试 fixture 和 schema 一起漂移了。修的时候必须把 fixture 也改成 spec-valid 的形状,否则换个键名照样测不到真东西。建议加一条元测试:每个 sharing-rule fixture 先过一遍 SharingRuleSchema.safeParse,不通过就 fail —— 这类 fixture-schema 漂移只有这样才不会再来一次。

相关:#4698(母议题)、ADR-0105 D6、ADR-0057 D5。

Activity

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