Skip to content

Tenancy: Core outer join leaves the tenant-scoped right side unfiltered (cross-tenant counts) #506

Closed

Description

@antosubash

Tier 1: security / tenant isolation

Problem

A known gap is pinned as a strict xfail, framework/db/tests/test_query_filter_outer_joins.py:191:

@pytest.mark.xfail(strict=True, reason="pre-existing: Core outer join right side unfiltered")
async def test_core_outer_join_right_side_is_tenant_filtered(seeded):
    tag, link = _Tag.__table__, _TagLink.__table__
    stmt = select(tag.c.name, func.count()).select_from(tag.outerjoin(link, link.c.tag_id == tag.c.id))...

A pure Core outerjoin over Model.__table__ gets no tenant criteria in ON or in WHERE, so count(*) includes another tenant's rows. _where_able correctly keeps the nullable side out of WHERE, but nothing adds the ON criteria for Core tables.

CLAUDE.md says the soft-delete and tenant filters "cover every statement shape … and Core statements over Model.__table__ (GH #332)". This case contradicts that claim. Module authors writing aggregate reports are the ones most likely to hit it.

Fix

  • Inject the tenant (and soft-delete) predicate into the ON clause for Core Join objects whose right side is a tenant-scoped table, mirroring what the ORM path already does.
  • Once the predicate is in place, remove the xfail so the test fails on regression.
  • Until it is fixed, document the gap in docs/framework/multi-tenancy.md and soften the CLAUDE.md claim.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions