feat(spec)!: RLS 策略 / 共享规则 / 岗位 schema 拒绝未知键(#4001 第 2 步) - #4099
Merged
Conversation
…positions (#4001 step 2) Second click of the unknown-key strictness ratchet (first: flow + permission, #4071), covering the remaining small security-class authoring surfaces from the ledger's next-target list: - security/rls.zod.ts: RowLevelSecurityPolicySchema is .strict() — a silently dropped key meant a row-level restriction was never compiled into the filter. Aliases: roles/role -> positions (ADR-0090 D3), withCheck -> check, condition/filter/where -> using. The runtime shapes (RLSUserContextSchema, RLSEvaluationResultSchema) stay tolerant; the retired `priority` keeps its tombstone. - security/sharing.zod.ts: the sharing-rule base, criteria extension, and sharedWith recipient are .strict() (strictness and the error map ride the base into the extension). Aliases: criteria -> condition, access/level -> accessLevel, recipient/shareWith/sharedTo -> sharedWith, enabled -> active; ownedBy carries the removed owner-type-rule prescription. - identity/position.zod.ts: PositionSchema is .strict() and gains the protection block + ADR-0010 runtime envelope, closing the sibling gap the #4071 ledger flagged (applyProtection stamps every registered type; position was the last schema that could not represent the stamp). Fourth finding for the ledger: position.test.ts asserted a fictional hierarchy — a pre-ADR-0090 `parent` key that only ever "passed" because .strip ate it (no position tree exists; hierarchy is the business-unit tree). The tests codified the strip-era fiction; rewritten to assert rejection with the flatness guidance. Verified: spec 6973 tests + tsc clean; all 12 check gates; plugin-security 677 / lint 546 / metadata 276 / platform-objects 239 / metadata-protocol 99; dogfood 72 files / 418 tests; showcase / crm / todo validate clean. Refs #4001 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0147tNF4Snk7Ry1KGt4a5PY4
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…bulary ratchet check-role-word flagged the new RLS alias example — spell it as "the pre-ADR-0090 D3 vocabulary -> positions" instead of naming the retired word. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0147tNF4Snk7Ry1KGt4a5PY4
os-zhuang
marked this pull request as ready for review
July 30, 2026 10:01
os-zhuang
added a commit
that referenced
this pull request
Jul 30, 2026
…4001 step 3) (#4119) Third click of the unknown-key strictness ratchet (flow + permission in #4071, RLS / sharing / position in #4099). Approval is a v17-new authoring surface — tightened while young, before stored volume exists: - ApprovalNodeConfigSchema, ApprovalNodeApproverSchema, ApprovalEscalationSchema and DecisionOutputDefSchema are .strict() with fixable errors via the shared strictUnknownKeyError factory. - ApprovalNodeConfig's guidance is the ADR-0019 re-home map: steps -> successive approval nodes on the canvas, entryCriteria -> the entering edge's condition, onApprove/onReject -> the approve/reject out-edges, rejectionBehavior -> a declared back-edge (ADR-0044) with maxRevisions. - The published JSON schema (getApprovalNodeConfigJsonSchema) carries additionalProperties:false into the Studio property form AND registerFlow's per-node config validation (#4027/#4040). Verified the #3746 hazard: z.toJSONSchema on the strict lazySchema does not throw. Verified: spec 6995 tests + tsc clean; all 12 check gates; plugin-approvals 326 / service-automation 457; dogfood 72 files / 418 tests; showcase / crm / todo validate clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#4001 棘轮的第二格(第一格:flow + permission,#4071),覆盖账本 next-target 清单里剩余的小型安全类可授权面,仍在 v17 RC 窗口内。
做了什么
security/rls.zod.ts—RowLevelSecurityPolicySchema.strict()。RLS 策略上被静默剥离的键 = 作者写的行级限制从未被编译进过滤器。别名:roles/role→positions(ADR-0090 D3 改名)、withCheck→check(PostgreSQL 拼法)、condition/filter/where→using。运行时形状(RLSUserContextSchema/RLSEvaluationResultSchema)保持宽松;退役的priority沿用既有retiredKey墓碑(POST /data/sharing/rules绕过SharingRuleSchema:criteria缺失或拼错静默变成"共享该对象全部记录",与 ADR-0049 "never seeded as a permissive match-all" 直接冲突 #3896)。security/sharing.zod.ts— 规则基座 + criteria 扩展 +sharedWith接收方全部.strict()(实测确认 zod v4 的.extend()会携带 catchall 和 error map,所以 strict 挂在基座上即可覆盖扩展)。别名里最有价值的一条:criteria→condition——持久化行拼作criteria_json,作者侧是 CELcondition,正是POST /data/sharing/rules绕过SharingRuleSchema:criteria缺失或拼错静默变成"共享该对象全部记录",与 ADR-0049 "never seeded as a permissive match-all" 直接冲突 #3896 里跑偏过的那对词。ownedBy带被移除的 owner 型规则处方。identity/position.zod.ts—PositionSchema.strict(),并补上protection块 + ADR-0010 运行时保护信封——关闭 feat(spec)!: 未知键在 flow / permission 授权 schema 上大声拒绝(#4001 Tier-A 切片) #4071 账本里挂账的同类隐患(applyProtection对每个已注册类型盖章,position 是最后一个无法表达盖章的 schema)。guidance 把permissionSets/users指向运行时绑定表、parent指向平结构规则。账本第 4 条 finding
position.test.ts一直在断言一个虚构的层级能力:ADR-0090 之前的「should accept position with parent」及四个「真实世界层级」示例都在传parent键——只因.strip吃掉了它才「通过」。position 树从未存在(ADR-0090 D3 定稿平结构;层级在 business-unit 树上)。测试在把剥离时代的幻觉固化成预期行为,已改写为断言拒绝 + 平结构指引。已记入账本 findings log。迁移
被拒的键此前全部被静默丢弃、从未有过运行时效果——删除或按错误提示改名,行为零变化。changeset(major)含完整 FROM→TO 表。
零破坏实证
@objectstack/spec:268 文件 / 6973 用例全过,tsc干净check:*闸门一次全过objectstack validate全过后续(不在本 PR)
账本 next-targets 顺延:
ui/app.zod.ts(导航递归 union 需单独设计错误呈现)→automation/approval.zod.ts→data/hook/datasource;长尾走 lint 告警层,v18 收口。Refs #4001
🤖 Generated with Claude Code
https://claude.ai/code/session_0147tNF4Snk7Ry1KGt4a5PY4
Generated by Claude Code