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
SharingRuleService.reconcile aborts a rule's whole pass when one grant is refused by the engine's system-write organization rule — that rule's stale revocations stay undone (residue of #14484) #14754
Filed by the director seat (objectstack #12708, summon #10, session session_01ShyhexkB2d1AeRZ85tgAAe) out of the contract review of PR #14726 (#14484). The PR discloses this shape itself (changeset paragraph "(a)"); it is carried here as the follow-up rather than blocking the landing, for the reasons in the review verdict. domain:services, pm:queue, S.
The shape
After #14484, sys_record_share is tenant-scoped in the #13491 ledger, so an organization-less system insert on it is refused loudly on a walled install (ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED). SharingService.grant resolves the organization on every path that can, but two paths resolve none:
a platform-global sharing rule (organization_id = null; its sweep runs unscoped) materialising a grant on an organization-less record;
a direct grant whose read of the shared record's organization failed (deliberately not substituted with the acting session's, the review's blocking fix).
On the rule path, SharingRuleService.reconcile / reconcileForRecord has no per-grant catch: the refusal propagates and that rule's reconcile aborts mid-loop. Grants already written in the pass stay; the remaining grants and the stale-row revocations of that pass do not happen. The boot backfill logs the rule and continues to the next; the write hooks catch so a user write is never failed.
Why it is a card
The revocation half is the security-relevant half: a stale over-grant of that rule persists across every pass that meets the same organization-less record, and that record keeps aborting the pass until it is repaired. The shape needs three things at once (walled install, platform-global rule, organization-less record in a tenant-scoped object), and the organization-less population is the one #13491 / #13564 are draining, so it is narrow; but it is reachable on existing walled deployments today (cloud#1232 measured organization-less rows), and before #14484 those passes completed (writing NULL rows). It is loud (logged), not silent, which is why it did not block the review.
What to build
A per-grant catch in both reconcile loops: a grant refused by the engine's organization rule is logged (rule, object, record id, the engine's code), counted in the pass result, and the pass continues — the remaining grants and, above all, the stale-row revocations still run.
Pin: a platform-global rule over two records, one organization-less, on an isolated posture: the organization-less grant is refused and counted, the other record's grant lands, the stale row of a third record is revoked in the same pass. Ablation: remove the catch, the revocation pin goes red.
Clause-② no (no accept-set change; a loop keeps going instead of aborting). @objectstack/plugin-sharing changeset patch.
Related: #14484 (the ruling), PR #14726 (the landing and its changeset paragraph (a)), #13491 (the ledger), #13564 (the reads), objectstack #12708 (director seat ledger).
Filed by the director seat (objectstack #12708, summon #10, session
session_01ShyhexkB2d1AeRZ85tgAAe) out of the contract review of PR #14726 (#14484). The PR discloses this shape itself (changeset paragraph "(a)"); it is carried here as the follow-up rather than blocking the landing, for the reasons in the review verdict.domain:services,pm:queue, S.The shape
After #14484,
sys_record_shareis tenant-scoped in the #13491 ledger, so an organization-less system insert on it is refused loudly on a walled install (ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED).SharingService.grantresolves the organization on every path that can, but two paths resolve none:organization_id = null; its sweep runs unscoped) materialising a grant on an organization-less record;On the rule path,
SharingRuleService.reconcile/reconcileForRecordhas no per-grant catch: the refusal propagates and that rule's reconcile aborts mid-loop. Grants already written in the pass stay; the remaining grants and the stale-row revocations of that pass do not happen. The boot backfill logs the rule and continues to the next; the write hooks catch so a user write is never failed.Why it is a card
The revocation half is the security-relevant half: a stale over-grant of that rule persists across every pass that meets the same organization-less record, and that record keeps aborting the pass until it is repaired. The shape needs three things at once (walled install, platform-global rule, organization-less record in a tenant-scoped object), and the organization-less population is the one #13491 / #13564 are draining, so it is narrow; but it is reachable on existing walled deployments today (cloud#1232 measured organization-less rows), and before #14484 those passes completed (writing NULL rows). It is loud (logged), not silent, which is why it did not block the review.
What to build
isolatedposture: the organization-less grant is refused and counted, the other record's grant lands, the stale row of a third record is revoked in the same pass. Ablation: remove the catch, the revocation pin goes red.read-failed ⇒ nullrule, or the eleven bare-context reads (measure: census the dependents of the SQL driver's orWhereNull tenant-wall carve-out before deciding its future (NULL org_id rows are globally visible on shared-DB walled deployments) #13564, with the maintainer).Clause-② no (no accept-set change; a loop keeps going instead of aborting).
@objectstack/plugin-sharingchangeset patch.Related: #14484 (the ruling), PR #14726 (the landing and its changeset paragraph (a)), #13491 (the ledger), #13564 (the reads), objectstack #12708 (director seat ledger).