You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Part of #5493 (maintainer ruling 2026-08-08, recorded in #5493 comment 5226377985 and the chat ruling it links: Q1 = A, Q2 = A1).
Why (one measurement carries the whole card)
#5493's fix requires the sharing middleware to defer, before hard-refusing a by-id write, to "an app-authored RLS update-widener admits this row by declaration". No reachable surface answers that today, and the cheap proxy is measurably a security hole: probe E-A (#5493 comment 5226364929) shows a creator-but-no-longer-owner is admitted by the platform created_by floor and refused by sharing with an envelope byte-identical to #5493's — a deferral keyed on "composed RLS admits" would hand transferred records back to their former creators. Separating the two requires policy provenance (isPlatformOwnershipFloorPolicy), which is deliberately private to plugin-security (platform-ownership-policies.ts, ADR-0105 D3 keying).
What (ruled shape — Q1 = A, Q2 = A1)
packages/spec/src/contracts/security-service.ts (ISecurityService) gains a verdict-shaped, by-id method:
checkAuthoredRowWrite(object, recordId, operation, context) returning 'admit' | 'abstain' (exact name/signature per contract conventions — implementer's call, trade-offs in the PR body);
admit iff at least one applicable non-floor (app-authored) RLS policy matches the row for this operation;
abstain in every other case — including a missing method, an unresolvable probe, or a throw. Fail-closed: a deployment without this method behaves byte-for-byte as today. (Same discipline feat(sharing): ISharingService 的每行写判定补三态(放行/不表态/拒绝)(#6428) #6564 pinned for SharingWriteVerdict: a failure is never a widening.)
Semantic pin the implementation must carry (the E-A hole, as a test): a principal matching only the platform floor (creator of a since-transferred row, no authored policy) gets abstain, never admit.
Q2 note, ruled: verdict-shaped by-id ONLY. The bulk-path half of the family is deliberately excluded and tracked separately (see the sibling card filed alongside this one); do not widen this surface toward filters.
This card is the head: this lands → #5493 step 2 (plugin-sharing consumption + the control-pin flip in row-write-widener-composition.test.ts) dispatches in the identity lane. #5493 carries Blocked-by: pointing here.
Two-seat surface by nature (spec contract + plugin-security implementation). Precedent for keeping them in ONE PR to avoid a declared-but-unimplemented window: ISharingService 写判定补三态(放行/不表态/拒绝)—— #5492 裁决 B 案的 step1,二态 canEdit 已实测产出 fail-open #6428 (triage designated a single claimant via the cross-domain exception path). Filed by the identity seat per the cross-seat transfer protocol; packages/spec has one owner (domain:spec seat) — routing labels left to the triage seat.
Part of #5493 (maintainer ruling 2026-08-08, recorded in #5493 comment 5226377985 and the chat ruling it links: Q1 = A, Q2 = A1).
Why (one measurement carries the whole card)
#5493's fix requires the sharing middleware to defer, before hard-refusing a by-id write, to "an app-authored RLS update-widener admits this row by declaration". No reachable surface answers that today, and the cheap proxy is measurably a security hole: probe E-A (#5493 comment 5226364929) shows a creator-but-no-longer-owner is admitted by the platform
created_byfloor and refused by sharing with an envelope byte-identical to #5493's — a deferral keyed on "composed RLS admits" would hand transferred records back to their former creators. Separating the two requires policy provenance (isPlatformOwnershipFloorPolicy), which is deliberately private to plugin-security (platform-ownership-policies.ts, ADR-0105 D3 keying).What (ruled shape — Q1 = A, Q2 = A1)
packages/spec/src/contracts/security-service.ts(ISecurityService) gains a verdict-shaped, by-id method:checkAuthoredRowWrite(object, recordId, operation, context)returning'admit' | 'abstain'(exact name/signature per contract conventions — implementer's call, trade-offs in the PR body);admitiff at least one applicable non-floor (app-authored) RLS policy matches the row for this operation;abstainin every other case — including a missing method, an unresolvable probe, or a throw. Fail-closed: a deployment without this method behaves byte-for-byte as today. (Same discipline feat(sharing): ISharingService 的每行写判定补三态(放行/不表态/拒绝)(#6428) #6564 pinned forSharingWriteVerdict: a failure is never a widening.)packages/plugins/plugin-security/src/security-plugin.tsimplements it on the registered service, reusing the private provenance split fix(plugin-security): enforce both declared write-wideners; the platform baseline becomes explicit-allow (#5492, #5491) #6684 landed (computeRlsFilterinternals /RlsFilterOptions.dropPlatformOwnershipFloor/platform-ownership-policies.ts) — no second implementation of RLS evaluation anywhere.abstain, neveradmit.Ordering (contract-first, the #6428 chain shape)
This card is the head: this lands → #5493 step 2 (plugin-sharing consumption + the control-pin flip in
row-write-widener-composition.test.ts) dispatches in the identity lane. #5493 carriesBlocked-by:pointing here.Boundaries
plugin-sharingedits (that is On objects that carry sharing rules, sharing middleware answers FORBIDDEN before RLS update-wideners are consulted — the identical widener works on rule-less objects #5493 step 2, identity lane).packages/spechas one owner (domain:specseat) — routing labels left to the triage seat.