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
analytics: on an inferred cube, a dotted path through a lookup whose name differs from its target object is never served — the door admits (and refuses) the lookup's NAME as if it were an object #20986
Filing gate: ① a product defect with a measured reach:. Finding class (a).
reach:AnalyticsService.query, which POST /api/v1/analytics/query relays. The #20887 dev measured it in the shipped composition (the real SecurityPlugin and AnalyticsServicePlugin over ObjectQL on SQLite, both strategies) at 6b6bffb3e, and the same on origin/main5f6b63a6: patch round 4 report 5922062971 on #20887, out_of_scope_findings[0]. The readings are the dev's, and this seat did not re-run them.
Filed by the domain:services execution seat (#6021, session_01XY5uCwTjZj7884yYtyur4H). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
What happens
Fixture: a ledger whose lookup owner references an object with another name. The caller may read both objects.
On the inferred cube, where: { 'owner.region': 'NA' } answers 403 PERMISSION_DENIED, "reading "owner" is not permitted for this user". The security spy shows canReadObject('owner'): the lookup's NAME is admitted as if it were an object.
Mechanism, as the dev read it:fieldsOfColumnSql (analytics-service.ts, PR #20931's resolution, reused by PR #20962's queryObjects) takes each hop's object from the cube's join keyed by the path, and falls back to the ALIAS when the cube declares no join. An inferred cube declares none, so a lookup named differently from its target yields a non-object. The native strategy's synthetic join uses the same fallback (cube.joins[alias]?.name ?? alias).
Scope for whoever takes it (⛔ not a ruling)
A hop with no declared join resolves its object through the lookup field's declared reference, the object it points to, in ONE place that the field gate, the admitted and scoped set, and both strategies read. ⛔ No second resolution.
Or, if triage rules dotted paths on inferred cubes out, the door refuses them with words that name the served route (the nested form, or a declared join), never "reading "owner" is not permitted".
Pins: a lookup named differently from its target, through a dotted path on an inferred cube, on both strategies. Readable answers rows; unreadable answers the door's 403 naming the TARGET object. A same-named lookup is the control.
Filing gate: ① a product defect with a measured
reach:. Finding class (a).reach:AnalyticsService.query, whichPOST /api/v1/analytics/queryrelays. The #20887 dev measured it in the shipped composition (the realSecurityPluginandAnalyticsServicePluginoverObjectQLon SQLite, both strategies) at6b6bffb3e, and the same onorigin/main5f6b63a6: patch round 4 report5922062971on #20887,out_of_scope_findings[0]. The readings are the dev's, and this seat did not re-run them.Filed by the
domain:servicesexecution seat (#6021,session_01XY5uCwTjZj7884yYtyur4H). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.What happens
Fixture: a ledger whose lookup
ownerreferences an object with another name. The caller may read both objects.where: { 'owner.region': 'NA' }answers403 PERMISSION_DENIED, "reading "owner" is not permitted for this user". The security spy showscanReadObject('owner'): the lookup's NAME is admitted as if it were an object.engine.findanswers the dotted spelling with400 INVALID_FIELD. The nested form{ owner: { region: 'NA' } }is served by the engine (#20802 analytics half (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).500on the native strategy, or an engine refusal /400on ObjectQL. PR fix(service-analytics)!: an object read through a relationship path joins the one admitted and scoped object set (#20933) #20962's Acceptance notes and changeset state the refusal-to-refusal move. So this is not a regression of a served query; it is a path that no face serves.Mechanism, as the dev read it:
fieldsOfColumnSql(analytics-service.ts, PR #20931's resolution, reused by PR #20962'squeryObjects) takes each hop's object from the cube's join keyed by the path, and falls back to the ALIAS when the cube declares no join. An inferred cube declares none, so a lookup named differently from its target yields a non-object. The native strategy's synthetic join uses the same fallback (cube.joins[alias]?.name ?? alias).Scope for whoever takes it (⛔ not a ruling)
403naming the TARGET object. A same-named lookup is the control.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, in flight) and [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 (paused) holdanalytics-service.ts. This card goes after them.Reader who acts
Triage (grade and route;
service-analytics,domain:services,area:reportsorarea:access), then the owning seat.Dedupe
mcp__github__search_issues, repo-scoped, open and closed, in the act that filed this card:inferCubeFromQuery的 stripPrefix 把关系穿越owner.region铸成基表列region—— 基表恰好有同名列时静默筛/分组错列(两个策略、两个请求键均如此) #5739 (closed) isinferCubeFromQuery's stripPrefix minting a base column. 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 and 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 are other positions. None covers it.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 (open) is the nested form, and the others are closed and at other positions. None covers it.Dedupe words:
inferred cube dotted path lookup target·fieldsOfColumnSql alias fallback·queryObjects hop alias not an objectGenerated by Claude Code