Skip to content

Four live adapter.find calls pass an unprefixed query option — and no-query-params-under-options gates only the sibling half of the class #5458

Description

@os-zhuang

Moved from objectstack-ai/objectstack#10470 by the triage seat (file-at-destination rule: every named site and the rule that should gate them live in this repo). Original filed 2026-08-21 by os-zhuang from the objectstack-ai/objectstack#10288 dev seat. Body reproduced verbatim below with bare issue refs qualified.


Census taken at objectui c40f3b8ca21ddc19e05682f1719fcf16d4ce7fba (objectstack pins 9a3daf8d37ad973a621e5edd276fe32467f90684; re-confirm at the pin before acting).

The class

QueryParams (packages/types/src/data.ts) declares only $-prefixed keys, and ObjectStackAdapter.convertQueryParams (packages/data-objectstack/src/index.ts) builds its outgoing options by copying exactly those. Any other key reaches no branch and is dropped — no throw, no warning. QueryParams also carries an index signature, so the type system accepts both spellings equally.

The consequence is an unbounded read, not a truncated one: the platform's GET list route has no default page size (objectstack packages/client/src/index.ts, pinned in its client.test.ts), so an absent top returns the entire match set. A dropped cap is therefore invisible until the object is large.

The four live sites

file call dropped key
apps/console/src/sdui-workbench-preview.tsx:65 find(objectName, { top: 200 }) top
packages/app-shell/src/views/ObjectView.tsx:1561 find(…, { limit: 0 }) limit
packages/app-shell/src/views/metadata-admin/AssignedUsersSection.tsx:109 find('sys_permission_set', { $filter: {…}, limit: 1 }) limit
content/docs/guide/react-pages.md:129 find(…, { filters: [['status','=','open']] }) filters

ObjectView.tsx:1561's limit: 0 is worth reading twice: $top: 0 is honoured end to end as "no records" (objectstack-ai/objectstack#6485 pinned it), so if that call means what it says, dropping the key inverts it from no rows to every row.

AssignedUsersSection.tsx:109 is a one-line-away neighbour of three correct calls ($top: 500, $top: 200, $top: 1000 on L124/131/136) — the shape a reviewer's eye slides over.

sdui-workbench-preview.tsx:66 also reads all.records off the result, with no .data arm. find() resolves to a QueryResult (data, never records), so that preview lists nothing regardless of the cap. skills/objectui/guides/data-integration.md:268 publishes the same return result.records; to skill consumers.

Why a rule, and which one

eslint-rules/no-query-params-under-options.js already gates the sibling shape — find(obj, { options: { $top: 100 } }) — and its own header makes the argument for mechanising this family: the mistake type-checks, it publishes, the symptom looks like a data problem rather than a code problem, "so a review catches it once and then misses the next one." It cites two live instances by two authors (object-timeline, objectui#4009 / objectstack-ai/objectstack#7137; object-kanban, objectui#4025).

The rule bans a $-prefixed key under options. It does not look at a bare key at the top level, which is the other half of the same class — and the half with four live sites. Extending it (or adding a sibling rule) is the cheap move: the AST walk already lands on the params object literal of a find/findOne call, the fix is a second predicate, and the contract lives in the same repo as the rule.

Suggested shape, narrow on purpose: flag a static key that is a known query-option name without its $ (top, skip, filter, filters, select, orderby, sort, expand, search, count, limit, offset) in the second argument of an adapter.find / dataSource.find / …findOne call. Not "any unprefixed key" — adapters legitimately take adapter-specific params, which is why the index signature exists.

An objectstack-side gate was considered for objectstack-ai/objectstack#10288 and rejected: QueryParams is objectui's contract, so a checker there would have to hard-code another repo's key list and become a second source of truth for it. The rule belongs next to the type it enforces.

Related

Activity

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

Metadata

Metadata

Assignees

Labels

domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions