Surfaced by the ADR-0096 execution-surface audit. A verb-coverage gap on the sharing axis: bulk writes are not owner-scoped on objects that rely on sharingModel:'private' (OWD) rather than an authored RLS policy.
Detail
For single-id writes, the sharing plugin runs canEdit and the security plugin does a pre-image row-level RLS check. Both are keyed off a single inferrable id:
packages/plugins/plugin-sharing/src/sharing-plugin.ts:387-412 — the write gate runs canEdit only if (id != null) (:391).
packages/plugins/plugin-security/src/security-plugin.ts:811-821 — the pre-image RLS check fires only for a single extractSingleId (:821 if (targetId != null)).
A update({ multi: true }) / deleteMany still routes through the update/delete middleware, and authored RLS where-policies are injected into the AST operation-agnostically (security-plugin.ts:1167) — so objects protected by an explicit RLS policy are fine. But objects whose protection is OWD private (owner-only) with no authored RLS policy get their owner scoping only from the sharing plugin's canEdit path, which never injects an owner where filter for writes and is skipped for multi-writes.
Impact: on a pure-private object, a member can update/delete with multi:true and affect other users' rows — the owner boundary that holds for single-row writes is absent for bulk writes.
Suggested fix
For multi writes on objects whose effective sharing model is owner-scoped, inject the owner (and share/hierarchy) predicate as a where filter into the bulk operation — the write analogue of the read filter sharing-service.ts buildReadFilter already produces — so bulk writes are constrained to rows the caller may edit, or deny multi-writes on such objects. Add a dogfood proof (a member bulk-updating a peer's private-object rows must fail).
Relationship to ADR-0096
Class B (a verb outside a check's coverage, not a missing-identity fall-open), but same theme — an execution/verb surface whose authorization story wasn't enumerated. Candidate row for the ADR-0096 D4 conformance matrix (bulk-write × sharing axis). Tracking under the ADR-0096 evidence base.
Refs: ADR-0096 (PR #2975).
Surfaced by the ADR-0096 execution-surface audit. A verb-coverage gap on the sharing axis: bulk writes are not owner-scoped on objects that rely on
sharingModel:'private'(OWD) rather than an authored RLS policy.Detail
For single-id writes, the sharing plugin runs
canEditand the security plugin does a pre-image row-level RLS check. Both are keyed off a single inferrable id:packages/plugins/plugin-sharing/src/sharing-plugin.ts:387-412— the write gate runscanEditonlyif (id != null)(:391).packages/plugins/plugin-security/src/security-plugin.ts:811-821— the pre-image RLS check fires only for a singleextractSingleId(:821 if (targetId != null)).A
update({ multi: true })/deleteManystill routes through theupdate/deletemiddleware, and authored RLSwhere-policies are injected into the AST operation-agnostically (security-plugin.ts:1167) — so objects protected by an explicit RLS policy are fine. But objects whose protection is OWDprivate(owner-only) with no authored RLS policy get their owner scoping only from the sharing plugin'scanEditpath, which never injects an ownerwherefilter for writes and is skipped for multi-writes.Impact: on a pure-
privateobject, a member canupdate/deletewithmulti:trueand affect other users' rows — the owner boundary that holds for single-row writes is absent for bulk writes.Suggested fix
For
multiwrites on objects whose effective sharing model is owner-scoped, inject the owner (and share/hierarchy) predicate as awherefilter into the bulk operation — the write analogue of the read filtersharing-service.ts buildReadFilteralready produces — so bulk writes are constrained to rows the caller may edit, or deny multi-writes on such objects. Add a dogfood proof (a member bulk-updating a peer's private-object rows must fail).Relationship to ADR-0096
Class B (a verb outside a check's coverage, not a missing-identity fall-open), but same theme — an execution/verb surface whose authorization story wasn't enumerated. Candidate row for the ADR-0096 D4 conformance matrix (bulk-write × sharing axis). Tracking under the ADR-0096 evidence base.
Refs: ADR-0096 (PR #2975).