Skip to content

fix(viewer): scope template routes under /w/<wsid>/ (close open global GET/DELETE)#180

Merged
ivanmkc merged 1 commit into
masterfrom
fix/template-auth-scope
Jun 11, 2026
Merged

fix(viewer): scope template routes under /w/<wsid>/ (close open global GET/DELETE)#180
ivanmkc merged 1 commit into
masterfrom
fix/template-auth-scope

Conversation

@ivanmkc

@ivanmkc ivanmkc commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Problem (found in code review of #179)

The template library's read + delete routes were origin-rooted and ungated — they sat above the /w/<wsid> dispatch with no wsid and no token. Since GET /templates returned every template's id+name, any anonymous caller could enumerate the whole global library and DELETE /template/<id> to wipe it. The deployed prod shares its origin with the public /w/demo/, so this was publicly reachable. The code comment "the unguessable id gates it" was false (the id was handed out by the catalog).

Reproduced (before): anon GET /templates200 (all ids), anon DELETE /template/<id>204, library wiped.

Fix

Move list/get/delete under /w/<wsid>/ so the unguessable workspace id is the capability (consistent with clear/share), and exclude the read-only demo (isReadOnlyWsid) so the public showcase can't enumerate or mutate it. Create was already wsid-scoped + demo-blocked. The library stays global (any of your workspaces sees every template — cross-session reuse by id preserved); only the access path is gated.

  • server.ts: drop the top-level routes; add GET /w/<wsid>/templates + GET|DELETE /w/<wsid>/template/<id>, demo-excluded; fix the comment.
  • cli/template.ts: list/get/delete use ${base} (…/w/<wsid>) — drops the origin derivation.
  • client/viewer.ts: popover fetches ${apiBase}/templates + /template/<id>.

Verification

Reproduced (after): anon GET /templates404, anon DELETE /template/<id>404, demo GET /w/demo/templates404, legit GET /w/<wsid>/templates200, library count unchanged (not wiped).

  • Unit: server.test.ts gains a template library suite (create/list/get/delete, missing-board 404, 405, demo 403/404, origin routes gone, __templates__ not servable) → server 76; CLI 8 (wsid-scoped paths).
  • e2e: template.e2e adds 3 auth-scoping assertions → 14/14; rich 62, board-sort 12.
  • Build exit 0 (viewer + CLI); NUL-byte check clean.

Follow-up to #179 (already merged/deployed); will redeploy on merge.

…l GET/DELETE)

Code review of #179 found the template library's read/delete routes were
ORIGIN-rooted and ungated: they sat above the /w/<wsid> dispatch with no wsid
and no token. Because GET /templates returned every template id+name, any
anonymous caller (the public demo shares the deployment's origin) could
enumerate the whole global library and DELETE /template/<id> to wipe it — the
"unguessable id gates it" comment was false.

Fix: move list/get/delete under /w/<wsid>/ so the unguessable workspace id is
the capability (consistent with clear/share), and exclude the read-only demo
(isReadOnlyWsid) so the public showcase can't enumerate or mutate the library.
Create was already wsid-scoped + demo-blocked. The library stays GLOBAL (any of
your workspaces sees every template — cross-session reuse by id is preserved);
only the access path is now gated.

- server.ts: remove the top-level /templates and /template/<id> routes; add
  GET /w/<wsid>/templates and GET|DELETE /w/<wsid>/template/<id>, both
  demo-excluded; fix the misleading comment.
- cli/template.ts: list/get/delete now use ${base} (…/w/<wsid>) instead of
  deriving the origin — simpler and matches the new routes.
- client/viewer.ts: the Templates popover fetches ${apiBase}/templates and
  /template/<id>.

Tests: server.test.ts gains a "template library" suite (create/list/get/delete,
missing-board 404, 405, demo 403/404, origin-rooted routes gone, __templates__
not servable); CLI test asserts the wsid-scoped paths; template.e2e asserts the
origin routes are gone + demo can't enumerate. Full chain: server 76, CLI 8,
e2e rich 62 / board-sort 12 / template 14.
@ivanmkc ivanmkc merged commit 215e29b into master Jun 11, 2026
5 checks passed
@ivanmkc ivanmkc deleted the fix/template-auth-scope branch June 11, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants