Observation
SharingRuleService.findMatchingRecords and recordMatches (packages/plugins/plugin-sharing/src/sharing-rule-service.ts, the criteria half of rule evaluation) run the rule's criteria query under a bare SYSTEM_CTX with no tenant scope, for every rule — including org-stamped ones:
constrows=awaitthis.engine.find(rule.object_name,{
filter,fields: ['id'],limit: 5000,context: SYSTEM_CTX,});The recipient half is already org-aware — expandRecipient threads rule.organization_id into TeamGraphService / BusinessUnitGraphService / PositionGraphService. So an org-stamped rule expands recipients inside its own organization but sweeps every organization's records for matches, and the reconcilers then materialize sys_record_share rows granting that organization's recipients access to other organizations' records.
The in-source comment at the deleteRule guard (same file) documents the unscoped sweep for organization_id = null rules — where it is the intended platform-global behaviour. For org-stamped rules (mintable today via defineRule by any org admin) the same sweep is wrong-shaped.
Why this is an observation, not a measured breach
Under a walled posture the Layer-0 tenant wall AND-composes over sharing's Layer-1 widening, so a cross-org sys_record_share row cannot actually open a read across the wall — the rows are inert. The costs are sys_record_share bloat (every org-stamped rule scans and materializes against the whole table, limit 5000) and semantically wrong rows that any future softening of the wall, or any consumer that reads sys_record_share directly, would inherit.
Multiplier
Issue 10103 (ruled Option C, per-organization catalog materialization) will turn the seeded org-less rules into N per-org copies; each copy then sweeps the whole table, multiplying the wrong-shaped rows by the organization count.
Remedy sketch
Thread rule.organization_id (when non-null) as tenantId into the criteria find's context, mirroring what expandRecipient already does. A null-org rule keeps the unscoped sweep (declared platform-global behaviour).
Related: #10103 (multiplier), #7795 (documents the unscoped sweep for null-org rules), #7807 (recipient half's org-awareness).
Generated by Claude Code
Observation
SharingRuleService.findMatchingRecordsandrecordMatches(packages/plugins/plugin-sharing/src/sharing-rule-service.ts, the criteria half of rule evaluation) run the rule's criteria query under a bareSYSTEM_CTXwith no tenant scope, for every rule — including org-stamped ones:The recipient half is already org-aware —
expandRecipientthreadsrule.organization_idintoTeamGraphService/BusinessUnitGraphService/PositionGraphService. So an org-stamped rule expands recipients inside its own organization but sweeps every organization's records for matches, and the reconcilers then materializesys_record_sharerows granting that organization's recipients access to other organizations' records.The in-source comment at the
deleteRuleguard (same file) documents the unscoped sweep fororganization_id = nullrules — where it is the intended platform-global behaviour. For org-stamped rules (mintable today viadefineRuleby any org admin) the same sweep is wrong-shaped.Why this is an observation, not a measured breach
Under a walled posture the Layer-0 tenant wall AND-composes over sharing's Layer-1 widening, so a cross-org
sys_record_sharerow cannot actually open a read across the wall — the rows are inert. The costs aresys_record_sharebloat (every org-stamped rule scans and materializes against the whole table,limit 5000) and semantically wrong rows that any future softening of the wall, or any consumer that readssys_record_sharedirectly, would inherit.Multiplier
Issue 10103 (ruled Option C, per-organization catalog materialization) will turn the seeded org-less rules into N per-org copies; each copy then sweeps the whole table, multiplying the wrong-shaped rows by the organization count.
Remedy sketch
Thread
rule.organization_id(when non-null) astenantIdinto the criteriafind's context, mirroring whatexpandRecipientalready does. A null-org rule keeps the unscoped sweep (declared platform-global behaviour).Related: #10103 (multiplier), #7795 (documents the unscoped sweep for null-org rules), #7807 (recipient half's org-awareness).
Generated by Claude Code