Part of objectstack-ai/hotcrm#705
Found during HotCRM's 17.0 GA acceptance sweep on @objectstack/* 17.0.0-rc.2, permission matrix phase. Two write-widening mechanisms, both inert; filed together because they share one root: the row write gate consults neither.
Measured
The effective row write gate is: owner_id == caller AND (member_default's wildcard RLS owner_only_writes: created_by == caller, OR an explicit RLS update-widener). Nothing else widens writes.
1. modifyAllRecords is inert. A manager profile with declared viewAll + modifyAll on its core objects got 403 row-level on every cross-owner write probed (update AND delete, four objects). Reads widen exactly as declared (43/43, 9/9); writes never.
2. Edit-level shares grant read only. All three edit-level sharing rules verified to materialize in sys_record_share (incl. a fixture created after rule evaluation — the hook fires; and clean revocation on unbinding) and to widen reads exactly. But a PATCH by the share target on the shared record → 403 row-level, every time. The write gate does not consult sys_record_share.access_level.
Corollary from the other side: read-level shares correctly deny writes — the share machinery distinguishes levels on paper, and the write gate then ignores the distinction.
Reproduction
# as a manager-profile user with modifyAllRecords on crm_opportunity
PATCH /api/v1/data/crm_opportunity/<other-owner-record> {"next_step":"x"}
→ 403 "[Security] … not permitted to update this 'crm_opportunity' record (row-level security)"
# share row present (object=crm_account, access_level=edit, target=<user>):
PATCH that account as the share target → 403 (same shape)
Impact
A manager cannot correct, reassign, or clean up anything they didn't personally create; every "manager fixes it" workflow is unimplementable. Org-admin is the only identity that can write across owners.
Prior related issues (checked)
Evidence: HotCRM acceptance run w4/results.jsonl (M3/M4/M8). Sibling: objectstack-ai/hotcrm#703 (the same member_default wildcard RLS supplies the created_by co-gate).
Part of objectstack-ai/hotcrm#705
Found during HotCRM's 17.0 GA acceptance sweep on
@objectstack/*17.0.0-rc.2, permission matrix phase. Two write-widening mechanisms, both inert; filed together because they share one root: the row write gate consults neither.Measured
The effective row write gate is:
owner_id == callerAND (member_default's wildcard RLSowner_only_writes:created_by == caller, OR an explicit RLS update-widener). Nothing else widens writes.1.
modifyAllRecordsis inert. A manager profile with declared viewAll + modifyAll on its core objects got 403 row-level on every cross-owner write probed (update AND delete, four objects). Reads widen exactly as declared (43/43, 9/9); writes never.2. Edit-level shares grant read only. All three edit-level sharing rules verified to materialize in
sys_record_share(incl. a fixture created after rule evaluation — the hook fires; and clean revocation on unbinding) and to widen reads exactly. But a PATCH by the share target on the shared record → 403 row-level, every time. The write gate does not consultsys_record_share.access_level.Corollary from the other side: read-level shares correctly deny writes — the share machinery distinguishes levels on paper, and the write gate then ignores the distinction.
Reproduction
Impact
A manager cannot correct, reassign, or clean up anything they didn't personally create; every "manager fixes it" workflow is unimplementable. Org-admin is the only identity that can write across owners.
Prior related issues (checked)
security/explainvs write path contradiction for VAMA holders; the write-path half of that contradiction is what this issue measures systematicallyISharingService.canEditdoesn't mention a modifyAllRecords bypass; once the enforcement question here is settled the doc should follow the rulingpublic_read_write; the objects here areprivate/public_readEvidence: HotCRM acceptance run
w4/results.jsonl(M3/M4/M8). Sibling: objectstack-ai/hotcrm#703 (the samemember_defaultwildcard RLS supplies thecreated_byco-gate).