Skip to content

Latest commit

 

History

History
34 lines (27 loc) · 1.8 KB

File metadata and controls

34 lines (27 loc) · 1.8 KB
@objectstack/service-analytics patch

fix(analytics): ObjectQLStrategy now enforces the read scope (RLS + tenant) (#3597)

ObjectQLStrategy never consumed getReadScope, so any analytics query served by that path ran with no RLS or tenant predicate — an authenticated caller received aggregates computed over every tenant's rows.

Both belts were off at once. The strategy dropped the pre-resolved read scope, and the engine could not compensate: the executeAggregate bridge passes no ExecutionContext, so plugin-security's principal-less fall-open skipped its own RLS injection. Only NativeSQLStrategy was ever wired for ADR-0021 D-C.

The exposure was not limited to exotic drivers. NativeSQLStrategy declines — handing the query to this path — on any date-bucketed query (timeDimensions[].granularity, the most common dashboard shape, on Postgres and SQLite too), on RAW_SQL_UNSUPPORTED (in-memory driver), and on federated objects.

The scope is composed with $and, never by key merge, so a caller filter naming the same field (e.g. organization_id) cannot displace the security predicate.

Behaviour change to be aware of: a query that references a joined object carrying its own read scope is now REJECTED on this path rather than run partially-scoped. engine.aggregate's where addresses the base object, so a per-join predicate cannot be expressed there; failing closed matches the posture already taken by resolveReadScopes and compileScopedFilterToSql. Such a query previously returned results that omitted the joined object's tenant predicate. Run it on a native-SQL driver (NativeSQLStrategy scopes each join), or drop the cross-object dimension/measure.

Deployments with no read-scope provider configured are unaffected — that path stays unscoped by documented contract.