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
security(analytics): on the native-SQL strategy an inferred cube's relationship path reads the related object without that object's read admission or its row scope #20933
Filing gate: ① a product defect with a measured reach. Class a. Security family, and it takes the "could leak data" exception. Filed by the domain:services seat (#6021, session session_01XY5uCwTjZj7884yYtyur4H) from #20917's dev report (5919006124, out_of_scope_findings[2]). ⛔ Filed bare: grading and routing are triage's. ⛔ Not a claim.
⚠️Disclosure discipline. This card, and every comment and PR that follows it, carries no request body, header, field spelling or returned value. The measuring dev keeps the evidence in private scratch space.
What was measured
Measured by #20917's dev on the analytics cube read (what POST /api/v1/analytics/query relays), with the real SecurityPlugin, ObjectQL and SqlDriver. The measurement is private.
On the native-SQL strategy, a member reached through an inferred cube's relationship path reads the related object even when the caller holds no read grant on that object. It also reads related rows outside the caller's row scope on it.
The ObjectQL strategy refuses the first with 403 and buckets the second as restricted.
Mechanism, as the dev read it: the object set the analytics door admits and scopes (AnalyticsService.queryObjects / cubeObjects) omits the traversal alias. NativeSQLStrategy's synthetic join (qualifyAndRegisterJoin) then joins the related table with neither the admission nor the row scope. This is the relationship-path counterpart of #4467 (record-level scoping on the base object, closed).
Direction (proposed, triage's to set)
Every object a query reads through a relationship path joins the admitted and scoped object set, so the object-level admission and the row scope apply to it as they do to the base object and to declared joins. ⛔ No per-strategy copy of the rule.
Pins: on both strategies, a related object the caller holds no read grant on is refused, and related rows outside the caller's row scope are not read. A readable related object is the control.
Reader who acts
Triage's first grade. The positions are in packages/services/service-analytics (domain:services). It shares the package with #20917 / PR #20931, #20887 / PR #20916, #20889, #20912 and #20918.
Dedupe (queries run before filing, closed included)
Filing gate: ① a product defect with a measured reach. Class a. Security family, and it takes the "could leak data" exception. Filed by the
domain:servicesseat (#6021, sessionsession_01XY5uCwTjZj7884yYtyur4H) from #20917's dev report (5919006124,out_of_scope_findings[2]). ⛔ Filed bare: grading and routing are triage's. ⛔ Not a claim.What was measured
Measured by #20917's dev on the analytics cube read (what
POST /api/v1/analytics/queryrelays), with the realSecurityPlugin,ObjectQLandSqlDriver. The measurement is private.403and buckets the second as restricted.Mechanism, as the dev read it: the object set the analytics door admits and scopes (
AnalyticsService.queryObjects/cubeObjects) omits the traversal alias.NativeSQLStrategy's synthetic join (qualifyAndRegisterJoin) then joins the related table with neither the admission nor the row scope. This is the relationship-path counterpart of #4467 (record-level scoping on the base object, closed).Direction (proposed, triage's to set)
Reader who acts
Triage's first grade. The positions are in
packages/services/service-analytics(domain:services). It shares the package with #20917 / PR #20931, #20887 / PR #20916, #20889, #20912 and #20918.Dedupe (queries run before filing, closed included)
domain:services): the cube read and the analytics read scope answer{ relation: { field: value } }as the engine seam now serves it — as the caller, capped, one answer on every face #20887 and security(analytics): the native-SQL strategy answers a query naming a field the caller has no field-level read permission for, where the engine and the ObjectQL strategy refuse 403 #20917 (open) are the nested-relation form and the field gate. [finding] analytics NativeSQL answers a multi-value dimension one group per serialized array on SQLite and 500 on PostgreSQL, and acount_distinctover a JSON-stored field 2 / 500; the engine door #20808 adds does not see it #20912 (open) is a multi-value dimension. analytics: /analytics/query ignores record-level scoping — a member counts and reads dimension values of records they cannot read #4467, analytics: an ad-hoc/analytics/queryor/analytics/sqlrequest writes inferred and augmented cubes into the shared registry before admission, so a refused request still changes every member'smeta#20381, analytics: any member's inline dataset query replaces an authored cube for every user until restart, even when the query itself is refused 403 #20356, service-analytics: the NativeSQL execute face and the /analytics/sql echo bind a read-scope filter placeholder ({current_user_id}, an unknown {token}) as a literal string, where the ObjectQL face resolves it — one scope, different rows across faces #20075, service-analytics: the NativeSQL read-scope compiler and the/analytics/sqlecho compile two scope shapes the shared comparand faces refuse (plain-object comparand under$eq, null member in$in): one scope, two answers across faces #20018 and analytics 自动推断路径:inferCubeFromQuery的 stripPrefix 把关系穿越owner.region铸成基表列region—— 基表恰好有同名列时静默筛/分组错列(两个策略、两个请求键均如此) #5739 (closed) are other positions. None is the traversal alias missing from the admitted and scoped object set.Dedupe words:
analytics relationship path object admission·synthetic join unscoped·cubeObjects traversal aliasGenerated by Claude Code