the boot check now reads the sockets that are served, not a list beside them
assertPermissionTotality exists to refuse a surface nobody has gated, and it was reading the wrong list: Object.keys(handlers) — the map of implementations — while Bun served a hand-written route table twenty lines away. The comment beside it said neither could be a stale copy of the other. That was false, and it had a live instance: /api/cliamp/ws and /api/cliamp/audio/ws stayed in the route table for two days after their handlers and registry claims were commented out. Not exploitable — isWsProviderAllowed finds no permission and 403s a member, while the owner upgrades onto a dead socket — but precisely the drift the guard is for, invisible to the guard. Now there is one declaration. WS_ROUTES maps path → provider, the route table is generated from it, and the check reads it. A socket route cannot exist without being checked. Two smaller guards arrived with it. A route naming a provider with no handler refuses to boot, with the path in the message, instead of 500-ing on first connect. And the provider union that was inline in upgradeWs is named, so the route table typechecks against what that function can actually serve — which caught my first attempt, where WS_ROUTES was Record<string, string>. sidecar and dev-proxy are listed explicitly as served-but-absent-from-WS_ROUTES, because they upgrade through their own handlers rather than upgradeWs. Named so the absence cannot read as an oversight. Half the TODO was stale and it is recorded as such rather than quietly closed: the eight red tests it cites have been green since cliamp was deleted on 2026-08-15. Deleting cliamp removed the symptom and left the shape, which is the more interesting half. Verified against the real prefix lists rather than by reasoning: the served set passes, one unclaimed provider is refused by name, all five generated routes answer 401, and the server boots. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
---
|
||||
id: multi-user/assertpermissiontotality-checks-the-wrong
|
||||
title: "`assertPermissionTotality` checks the wrong list, and `registry.test.ts` has been red since 2026-08-13"
|
||||
title: '`assertPermissionTotality` checks the wrong list, and `registry.test.ts` has been red since 2026-08-13'
|
||||
theme: multi-user
|
||||
status: open
|
||||
status: done
|
||||
created: 2026-08-13
|
||||
date_source: stated
|
||||
order: 12
|
||||
@@ -17,3 +17,27 @@ a dead socket. But the boot check that exists to stop exactly this cannot see it
|
||||
totality at the route table, and either delete the dead routes or restore their claims. The 8 failing
|
||||
tests in `registry.test.ts` are the same drift — `REAL_WS` still lists all nine providers as served,
|
||||
which is why nobody noticed. Found 2026-08-14.
|
||||
|
||||
---
|
||||
|
||||
**Fixed 2026-08-22.** Half of this item was already stale and half was still exactly true, which is worth
|
||||
separating because the stale half is what would have made someone close it.
|
||||
|
||||
Stale: the eight red tests in `registry.test.ts` are green, and have been since cliamp was deleted on
|
||||
2026-08-15 — that removed the live instance of the drift along with the routes.
|
||||
|
||||
Still true: the two lists were still two lists. `assertPermissionTotality` read `Object.keys(handlers)`, the
|
||||
map of implementations, while Bun served a hand-written route table twenty lines away. The comment beside
|
||||
the check claimed neither could be a stale copy of the other, and that was simply false.
|
||||
|
||||
The fix is one declaration. `WS_ROUTES` maps path → provider, the Bun route table is GENERATED from it, and
|
||||
the boot check reads it — so a socket route cannot exist without being checked. Two smaller guards came with
|
||||
it: a route naming a provider with no handler now refuses to boot with the path in the message, and the
|
||||
provider union that was inline in `upgradeWs` is named (`UpgradableProvider`) so the route table typechecks
|
||||
against what that function can actually serve.
|
||||
|
||||
`sidecar` and `dev-proxy` are listed explicitly as served-but-not-in-WS_ROUTES, because they upgrade through
|
||||
their own handlers. Named rather than omitted, so their absence cannot read as an oversight.
|
||||
|
||||
Verified by running the check against the real prefix lists: the served set passes, and adding one unclaimed
|
||||
provider is refused with `websocket provider 'cliamp-audio' is served but no permission claims it`.
|
||||
|
||||
Reference in New Issue
Block a user