Skip to content

POST /data/sharing/rules 绕过 SharingRuleSchema:criteria 缺失或拼错静默变成"共享该对象全部记录",与 ADR-0049 "never seeded as a permissive match-all" 直接冲突 #3896

Description

@os-zhuang

从 #3878 的同类扫查里出来的。这一条不是"可能是有意设计"—— 规范里已经写死了不许这样,只是 REST 入口没走那条规范。

声明的不变式

packages/spec/src/security/sharing.zod.ts:

  • SharingRuleSchema = CriteriaSharingRuleSchema,其中 condition 是必填(:104)。
  • 文件头 :22:"criteria is the single enforced rule form"。
  • SharingRuleSchema 的文档注释写得更直接:"A condition the compiler cannot lower (functions, cross-object traversal) is skipped and logged — never seeded as a permissive match-all (ADR-0049)."

也就是说:编译不出谓词的规则宁可不生效,也绝不能变成"匹配全部"。 seed 路径按这条执行。

REST 入口绕过了它

POST /api/v1/data/sharing/rules:

  1. packages/rest/src/rest-server.ts:5786 —— 逐字段 pluck,criteria: body.criteria,没有任何 schema 校验。SharingRuleSchema 在这条路上从未被调用。
  2. packages/plugins/plugin-sharing/src/sharing-rule-service.ts:87-92 —— defineRule 校验 name / label / object / recipientType / recipientId,唯独不校验 criteria。
  3. 同文件 :98-100 —— input.criteria == null ? null : JSON.stringify(...),缺失就存 criteria_json: null。
  4. 求值时 :264:
    const filter = (rule.criteria ?? {}) as any;
    const rows = await this.engine.find(rule.object_name, {
        filter, fields: ['id'], limit: 5000, context: SYSTEM_CTX,
    });
    空 filter + SYSTEM_CTX → 该对象全部记录(上限 5000)逐条 expandRecipient 授予收件人。
    recordMatches(:280)同理:{ ...(criteria ?? {}), id: recordId } 对任意记录都命中。

触发它不需要恶意,只需要打错一个字母

POST /api/v1/data/sharing/rules
{
  "name": "won_deals", "label": "Won Deals", "object": "crm_opportunity",
  "recipientType": "position", "recipientId": "sales_rep",
  "criterias": { "stage": "won" }
}

意图是共享"已赢单"。实际是把 crm_opportunity 的全部记录共享给 sales_rep。返回 201,没有 warning,criteria 这个多出来的键被静默丢弃 —— 因为入口是 pluck 不是 parse。

同样的结果还可以由"忘了带 criteria"、"criteria 传了 null"、"前端表单没填完就提交"产生。

#3850 刚让"在 Setup 里授述的规则真正生效、关掉真正撤销"落地,这条路径现在是活的。

修法

入口接上已有的 SharingRuleSchema(或至少:criteria 缺失/为空对象时 defineRule 直接 VALIDATION_FAILED,与 name/label 等同级对待)。Zod 的默认对象模式会剥掉 criterias 这类未知键 —— 若希望拼错也能被指出来,用 .strict()。

需要一并裁定的:空 criteria 是否存在合法语义。规范目前的答案是"不存在"(condition 必填、单一 enforced 形态)。如果确实要保留 owner-based 那样的"全对象共享",它应当是一个显式的规则类型,而不是"字段没填"的副作用 —— 这正是 ADR-0049 那句话要防的东西。

门:一条断言"criteria 缺失的规则不产生任何 sys_record_share"的用例;目前 plugin-sharing 的测试没有覆盖空 criteria 这一支。

核对于 origin/main @ 93f267f。

Activity

  1. self-assigned this
    on Jul 29, 2026
  2. added a commit that references this issue on Jul 30, 2026
    ccd9397
  3. 4 remaining items

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions