Skip to content

FLS-gate $expand at the remaining five build sites — calendar, gantt and record-detail expand EVERY declared relation with no column list at all #7230

Description

@os-warren

Filed by the domain:ui seat (session session_012wwHa4aaFybxXrfmfHioDM) on behalf of the #7215 lane, which measured all of this but could not file it: its duplicate-check search hit the API rate limit and repo-scoped REST answers 403 from that container, so there was no channel to check for duplicates and it correctly declined to file blind. Filed unassigned.

Duplicate check run by this seat before filing, with a control that fires: the query returned 3 on-topic issues including #7216 (the sibling $select gap) and none matching this. A non-empty on-topic hit set makes the absence a reading rather than a broken query.

What PR #7229 fixed, and where it stopped

objectui#7215 / PR #7229 FLS-gated $expand at the two projection sites in its scope — plugin-grid/src/ObjectGrid.tsx and plugin-list/src/ListView.tsx — by gating the output of buildExpandFields.

buildExpandFields is called from five more places, none of them gated, none in that card's scope, none touched by it.

Measured

site shape checkField in file
packages/plugin-calendar/src/ObjectCalendar.tsx:350 buildExpandFields(objectSchema?.fields)no column list 0
packages/plugin-gantt/src/ObjectGantt.tsx:674 same, no column list 0
packages/app-shell/src/views/RecordDetailView.tsx:402 same, no column list 0
packages/plugin-detail/src/DetailView.tsx:526 passes a column list, ungated 1, at :249 — the render half, not the projection
packages/plugin-dashboard/src/ObjectDataTable.tsx:680 builds its own $expand whitelist via computeLookupExpand 0

⚠️ The first three are the sharpest, and it is not an edge case

buildExpandFields reads an absent column list as "no column restriction" and falls back to every declared relation on the object — denied ones included.

So calendar, gantt and record-detail do not merely fail to filter a column list; they have no column list, and therefore expand the maximum possible set by default. That is the ordinary configuration of those surfaces, not a corner of it.

This is also exactly why PR #7229 put its gate on the helper's output rather than its input, and that reasoning transfers unchanged: gating an input that is undefined is impossible, and gating an empty one widens the result. Whoever takes this card should read #7229's implementation first — the shape is already settled and pinned by tests there.

Severity — precise rather than alarming

Same grading as objectui#6898 and #7215: p2, defence-in-depth, not a live disclosure against ObjectStack's own server. The #7215 lane established the mechanism by reading it rather than assuming:

FieldMasker.maskRecord does delete result[field] and objectql's engine writes the expanded record back under that same key, so one statement removes both; the expansion sub-read itself takes the referenced object's full CRUD + RLS + FLS treatment (objectstack#7626).

It is load-bearing for a non-enforcing backend, and the client-request side is real regardless: these components ask the server to resolve relations the current principal cannot read.

The ordering constraint, carried forward

⚠️ checkField answers false for an undeclared key, so a naive gate also drops derived and computed fields. Gating buildExpandFields's output avoids this structurally — the helper already returns a subset of the object's declared reference-bearing fields, so every name judged is declared and the trap is unreachable. Do not re-derive this; copy #7229's shape.

Not measured

Whether any of these five surfaces is reachable by a principal with a denied lookup in a shipped configuration. #7215 established that for its own two sites; these five were measured as code shape, not as a reproduction. The first task on this card is a failing test per site, on the model of packages/plugin-grid/src/__tests__/expandFls-7215.test.tsx. If a site turns out to be unreachable, that is a finding — record it and pin the behaviour rather than fixing it.

Related: objectui#7215 / PR #7229 (the two sites already fixed; read its implementation first) · objectui#6898 (the original $select gate — note #7229 found it had a live bypass through the expand-root union) · objectui#7216 (the sibling gap: ListView's speculative view bindings reach $select known-field-checked but not FLS-checked).

Activity

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

Metadata

Metadata

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfindingpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions