Skip to content

do_orm_execute tripwire requiring a visibility decision #815

Closed as not planned
Closed as not planned

Description

@bencap

Makes forgetting a visibility decision a loud failure at the query, complementing the traversal-site failure from #814.

Scope

The listener as specified in the epic — three sets, one subtraction:

  • involved — guarded entities this statement touches.
  • decided — entities that brought a with_loader_criteria. The reached path.
  • asserted — entities the caller declared it will assert_permission on instead. The named path.

The opt-out is entity-scoped, not statement-scoped. Loader criteria propagate to lazy loads; execution options do not — a filter follows the data downstream, a promise dies with the statement that made it.

Also lands the system principal for the worker.

Acceptance criteria

  • select(ScoreSet) with no visibility decision raises UnscopedQueryError.
  • select(ScoreSet).options(with_loader_criteria(ScoreSet, clause)) succeeds and filters.
  • Eager-loading calibrations with criteria on ScoreSet only raises, naming ScoreCalibration.
  • select(func.count()).select_from(ScoreCalibration) with no decision raises; with criteria it returns the visibility-correct count.
  • .execution_options(visibility_asserted=(ScoreSet,)) excuses only ScoreSet, and the exemption does not propagate to lazy loads from that result.
  • Queries touching no guarded entity are unaffected.
  • A regression test asserts the tripwire still fires, so a SQLAlchemy upgrade that breaks the private-API access fails CI rather than silently disarming enforcement.
  • Worker jobs carry a system principal whose clause evaluates to true() and attach it like any other caller; none use a raw opt-out.

Notes

Private API risk. statement._with_options and LoaderCriteriaOption._all_mappers() are not public API, and LoaderCriteriaOption lives in sqlalchemy.orm.util. A version bump could break the tripwire, and it would break open. The regression test is a hard requirement, not a nice-to-have.

Activity

  1. added theissue type on Aug 7, 2026
  2. changed the issue type fromtoon Aug 7, 2026
  3. bencap commented on Aug 7, 2026

    @bencap
    CollaboratorAuthor

    Not planned — superseded by the revision of #808 to Postgres row-level security.

    The tripwire existed to make a forgotten with_loader_criteria a loud failure. With enforcement in the database there is no per-query visibility decision to forget, so there is nothing for it to guard.

    It is also worth recording why this is a net safety improvement rather than a lateral move: the listener depended on statement._with_options and LoaderCriteriaOption._all_mappers(), neither of which is public API. A SQLAlchemy upgrade could have broken it, and it would have broken open — enforcement silently disabled with tests still green. The regression test in the original spec was a mitigation, not a guarantee.

    The equivalent risk under RLS is a misconfigured role, which is silent in the same way. That is covered by the startup enforcement assertion instead, which checks rolsuper, inherited rolbypassrls, NOINHERIT, and policy bodies at boot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    app: backendTask implementation touches the backend

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions