Description
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 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 }) / 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).
Activity
docs(adr): ADR-0096 — add enumerated evidence + D4 matrix seed
fix(security): close reports IDOR, RAG retrieval fall-open, and bulk-…
ADR-0096 + fixes for the 3 exploitable execution-surface holes (#2975)
harden owner_id guard + bulk-write seam from adversarial review (#3004,
fix(security): guard owner_id anchor + scope bulk writes to owner-vis…
Merge origin/main into claude/public-form-owner-bypass-ds4orf
fix(security): scope bulk-write predicate guard to caller filter + de…
docs(spec): HookContext.input 契约表改成引擎真正构造的形状 (objectstack-ai#5273) (o…