Repository navigation
Commit 692a4e4
fix(console): refuse alias-spelled Setup object lists for a caller the Setup app refuses (#29082)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbLjuGcBHnXMKngAM2U7tGSa)_
Fixes #28988
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:普通成员(没有 Setup
应用权限)用别名地址打开对象列表(`/apps/setup/sys_user`、`/apps/setup/showcase_task`、`…/view/ID`、`…/data`,以及会跳转过去的旧地址
`/apps/setup/system/users`)时,现在看到与规范地址
`/apps/com.objectstack.setup/sys_user` 相同的「无权访问此应用」页面。
- 为什么改:以前这些别名地址会套着默认应用(如 Showcase)的顶栏和侧栏显示列表,地址栏却写着
setup,像是进了管理后台;规范地址早已正确拒绝,两种拼写判定不一致。
- 风险与回滚:只改界面判定,服务端读权限不变;管理员不受影响(不发起探测)。记录页
`/apps/setup/OBJECT/record/ID` 按 objectui#12706
的未决产品决定保持可达,审批收件箱的链接不受影响。回滚即还原本 PR。
-
先看:`ui/packages/app-shell/src/console/AppContent.tsx`(`isObjectListAppRoute`
与 `appSurfaceAwaitsVerdict` 新增的 `isSetupObjectListRoute` 一支)。
- 两分钟验证:`pnpm --dir ui exec vitest run
packages/app-shell/src/console/__tests__/AppContent.setupObjectListVerdict-28988.test.tsx`,预期
12 个用例全绿。
- 需要你做的:无,席位落地。
## What changes
- `AppContent` asks the Setup app's access verdict on the object
**list** routes under the `setup` alias: `/apps/setup/OBJECT`,
`/apps/setup/OBJECT/view/ID` and `/apps/setup/OBJECT/data`. They join
the routes that already waited on it (`appSurfaceAwaitsVerdict`:
metadata, Setup's root, settings, marketplace). A `denied` verdict draws
`app-access-denied`, and nothing of the list mounts. Every other
verdict, and an admitted caller (Setup in the list, so nothing is
probed), render as before.
- A list route is found by route rank (`isObjectListAppRoute`), the way
`preloadAppRouteChunk` and `metadataReadsForAppRoute` already match: the
sub-path is matched against `APP_ROUTE_METADATA_READS` plus the host's
routes, and only `:objectName`, `:objectName/view/:viewId` and
`:objectName/data` count. So a static route (`search`, `reports`) or a
host route under the Setup segment (`system/approvals`, `system/users`)
is never read as an object list.
- The arm applies only under the `/apps/setup` pseudo-route
(`isSetupRoute`). An ordinary unmatched app already reaches the verdict
through `requestedAppMissing`, and its flow is unchanged.
## Why
For a member the server refuses the Setup app, `/apps/setup` is a
pseudo-route (`isSpecialRoute`), so an unmatched `setup` segment fell
back to the launcher's default app instead of asking the verdict. The
canonical spelling is an ordinary unmatched app and already drew the
refusal. The legacy `/apps/setup/system/users` forwards onto
`/apps/setup/sys_user`, so the member reached the list from a documented
URL.
## Tests
- New `AppContent.setupObjectListVerdict-28988.test.tsx`, a real
`ObjectStackAdapter` over a stubbed `fetch`, as in the #27507 and
objectui#12706 files:
- A refused caller gets `app-access-denied`, no `ConsoleLayout` and no
page mounted, on `/apps/setup/sys_user`, `sys_organization`,
`showcase_task`, `sys_user/view/all_users` and `sys_user/data`. The one
by-name probe is Setup's, and the URL is left as typed.
- The legacy `/apps/setup/system/users` forwards onto
`/apps/setup/sys_user`, which is refused.
- The package-id spelling draws the same screen.
- Controls: an admitted caller gets the list under Setup, unprobed; a
server without the app (404) keeps the default-app fallback; the record
page the approvals inbox links to stays reachable for a refused caller;
the host's `system/approvals` route is not read as a list; a list under
an app the caller may open renders, unprobed.
- Red on `main`: with `AppContent.tsx` at `ab085a04`, 6 of the 12 cases
fail (the five list routes and the legacy redirect). The package-id case
and the controls pass there.
- Ablation through `scripts/ablation-replace.mjs` (anchor 1 to 0, blob
restored to HEAD, `git diff HEAD` empty): the route-rank match replaced
by `appSubPath !== ''` (every Setup sub-route refused) reds 5 cases
across this file and the objectui#12706 file: both record-page controls,
both approvals-inbox controls, and the legacy redirect.
## Acceptance notes
- **The boundary is the claim's.** Record routes under the Setup segment
(`OBJECT/record/ID`, its `/edit` and `/page/NAME`, and the `OBJECT/ID`
shorthand that redirects to one) stay outside the gate, because the
approvals inbox links members there and objectui#12706 left that open as
a product decision. `OBJECT/new` (the create form) is not a list route
and is also left as it was; it still renders under the default app for a
refused caller. Noted, not filed.
- **The card's second symptom is not the same verdict.**
`/apps/com.example.showcase/system/settings` shows a member the empty
settings hub because the member may open Showcase: the app verdict is
`granted` there, and #27507 pins that route as rendered, unprobed (`the
settings route under an app the caller may open renders the page,
unprobed`). It is reported to the seat as an out-of-scope finding, not
fixed here.
## Verification
<details><summary>Local runs</summary>
All at head `0259ffcd` (base `ab085a04`; the 9 commits `main` gained
since touch nothing under `ui/packages/app-shell/src/console` or
`ui/apps/console/src`).
- Prerequisite build under the verify lock: `turbo run build
--filter='@object-ui/app-shell^...'` plus formula, sdui-parser, lint and
client, 64 of 64 tasks.
- The new file with the #27507 and objectui#12706 verdict files: Test
Files 3 passed, Tests 34 passed.
- Red on `main` (`AppContent.tsx` at `ab085a04`, new test unchanged):
Tests 6 failed, 6 passed.
- Every ui test file that imports `AppContent` (`git grep -l`, 53 in
`packages/app-shell`, 16 in `apps/console`, 1 in `packages/i18n`): Test
Files 70 passed, Tests 663 passed. A narrowing of app-shell's suite;
CI's Test UI shards run the rest.
- `turbo run type-check --filter=@object-ui/app-shell` (its script runs
`tsc -p tsconfig.test.json` too, so the new test is compiled): 65 of 65
tasks.
- `node scripts/pm/dispatch-gates.mjs --commands`: 34 derived commands,
each exit 0, among them `check-sdui-manifest.mjs`,
`check-changeset-no-major.mjs --base origin/main`,
`check:type-check-debt`, `check:published-files`, `check:nul-bytes` and
`pnpm --dir ui exec vitest run scripts/` (Test Files 169 passed, 2
skipped; Tests 4900 passed, 2 skipped). `--ran` reconciliation: 34
derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. The PR-body gates were run
again against this body before the pull request was opened.
- Not measured: a browser run of the built Console as a real member. The
card's reproduction is the server's own (`GET /api/v1/meta/app/setup`
answers 403 to a member), and the test drives the real adapter's probe
over that envelope.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(`packages/spec` not touched.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` is CI's run.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: not applicable.
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English (the 维护者速读 section is
the Chinese summary the repo asks for).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01QsR2jTJvw3sfvZA89BdmSR
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QsR2jTJvw3sfvZA89BdmSR)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent c749198 commit 692a4e4
3 files changed
Lines changed: 390 additions & 1 deletion
File tree
- .changeset
- ui/packages/app-shell/src/console
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
201 | 201 | | |
202 | 202 | | |
203 | 203 | | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
204 | 225 | | |
205 | 226 | | |
206 | 227 | | |
| |||
550 | 571 | | |
551 | 572 | | |
552 | 573 | | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
553 | 583 | | |
554 | 584 | | |
555 | 585 | | |
556 | 586 | | |
| 587 | + | |
557 | 588 | | |
558 | | - | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
559 | 592 | | |
560 | 593 | | |
561 | 594 | | |
| |||
0 commit comments