From 9246901a664905dab2feeb930b1d61ef8e0be580 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 07:03:35 +0000 Subject: [PATCH 1/3] fix(rest)!: an empty `?id=` on the app list is refused 400 naming the parameter instead of read as absent `GET /meta/app?id=` with an empty value skipped the app filter and served the caller's whole visible list. The shared list chain now refuses it after the per-caller gate with 400 VALIDATION_ERROR naming `id`, the answer the #27484 fix gives a blank `$searchFields`. A non-empty id, an absent id and the repeated-id 400 are unchanged. Co-Authored-By: Claude --- .changeset/28548-empty-app-id-refused.md | 19 +++++++++ .../rest/src/meta-app-publish-gate.test.ts | 41 ++++++++++++++++--- packages/rest/src/meta-item-read-gate.ts | 38 ++++++++++++++--- .../meta-list-projection-parity.test.ts | 5 +++ 4 files changed, 93 insertions(+), 10 deletions(-) create mode 100644 .changeset/28548-empty-app-id-refused.md diff --git a/.changeset/28548-empty-app-id-refused.md b/.changeset/28548-empty-app-id-refused.md new file mode 100644 index 00000000000..059d3c29543 --- /dev/null +++ b/.changeset/28548-empty-app-id-refused.md @@ -0,0 +1,19 @@ +--- +'@objectstack/rest': patch +--- + +fix(rest)!: `GET /meta/app?id=` with an empty value is refused 400 naming the parameter instead of being read as absent + +Clause-②: no (narrowing) — `GET /meta/app` and `GET /meta/apps` (on `RestServer` and on the runtime dispatcher, which share the list chain) refuse an empty `?id=` with `400 VALIDATION_ERROR`; they answered 200 with the caller's whole visible app list before. Nothing is widened: no export, answer member, error code or status is added. +Metadata-protocol: none + + + +**BREAKING** accept-set narrowing on the app list read (`@objectstack/rest`). + +- **Before.** `?id=` with an empty value was read as no filter, so a caller who asked for one app by a name that came out blank got every app it may see. +- **Now.** The list chain refuses it after the per-caller visibility gate: `400 VALIDATION_ERROR`, message `Query parameter 'id' names no app; …`, `param: 'id'`, and no list in the body. This is the answer #27484 gave a blank `$searchFields`. +- **Unchanged.** A non-empty `id` narrows as before (one app, or an empty list when it names none, including `' '`); no `id` lists every app the caller may see; a repeated `id` is still the arity 400; `?id=` on any other type is still ignored. +- **Who sees it.** An API client that sends `?id=` empty. `@objectstack/client` and the console send no `id` on this read. + +**The remedy.** Name the app, or leave `id` out to list them all. diff --git a/packages/rest/src/meta-app-publish-gate.test.ts b/packages/rest/src/meta-app-publish-gate.test.ts index 324eb79db13..973a491a6f8 100644 --- a/packages/rest/src/meta-app-publish-gate.test.ts +++ b/packages/rest/src/meta-app-publish-gate.test.ts @@ -283,11 +283,42 @@ describe('#7566 — `GET /meta/app?id=` narrows the list instead of being droppe const { rest } = setup(['manage_users']); expect(namesFrom((await getList(rest, 'app')).body).sort()).toEqual(['account', 'crm']); - // `?id=` (empty) is the "no filter" spelling an unset `