[QA] §13/§14/§17/§18 — anon access to "auth-only" routes doesn't match the checklist (no leak, but inconsistent gating)
Sections: §13 Calendar, §14 Roadmap, §17 Profile, §18 Admin
Severity: low → moderate (escalated 2026-05-27 after reviewing /roadmap content; see "Update" below).
The other three routes remain low-severity (no data exposure). /roadmap exposes internal
planning and a handful of items that telegraph specific security gaps.
Found via: §13/14/17/18 manual mechanism check (paste URL as anon, observe).
Summary
The QA checklist says anon visitors to /calendar, /roadmap, /profile, and
/admin are "redirected to /auth." None of the four actually does that. The four
routes split into three different behaviors, none of which are a clean redirect:
| Route |
Anon behavior |
Data exposure? |
/calendar |
Renders the calendar feature directly |
None observed (UI only) |
/roadmap |
Renders the full Development Roadmap — feature/bug/task lists pulled from GitHub via the github-roadmap edge function |
Yes — see "Update" below. Internal sprint plan + a handful of items that explicitly call out security gaps |
/profile |
Stays on /profile; renders an inline sign-in widget (AccessGate pattern) |
None |
/admin |
Renders the Admin Console shell; "System Alerts" tab shows "All systems healthy. No unresolved critical alerts." Other tabs show "no access." |
None observed (no PII, no user lists, no access requests) |
The sidebar correctly gates all four with a click-time redirect to /auth. The bug is
that clicking the link and pasting the URL behave differently. Anyone who lands
on these routes via a bookmark, browser autocomplete, an external link, or a typed URL
bypasses the lock entirely.
Why this is worth fixing even without a leak
- Honesty: the sidebar shows a lock icon and redirects; the address bar doesn't.
Users get conflicting signals about whether they're allowed to be there.
- Spec drift: the checklist promises a redirect; the app doesn't deliver it. Either
the spec or the app should change so they agree.
- Defense in depth: today there's no data on these pages anon can see, but the
only gate is "RLS + empty queries returning nothing useful." If anyone later adds
data to one of these pages without remembering to add a route guard, it leaks
silently. A real guard (route-level redirect OR a consistent AccessGate on every
gated route) catches that class of mistake at the door.
/admin specifically: "System Alerts: all healthy" is itself a (very small) leak
— it confirms the alerts subsystem exists and is healthy to anyone who can type the
URL. Low impact in isolation; bad pattern if it becomes the rule.
Update (2026-05-27): /roadmap content review
Skimmed the rendered roadmap as anon. The page shows 80+ GitHub items grouped under
Features / Bugs / Tasks. Most of it is in the "internal sprint planning that some
teams expose on purpose" category — but a small subset is the kind of content
typically kept behind a login:
Items that explicitly advertise security gaps:
These don't leak data, but together they advertise a list of soft spots to anyone
who reads the page. That's the textbook reason "open security backlog" is normally
gated.
Internal planning visible to anon (lower concern, but unusual to expose):
Fix options for /roadmap specifically
Either of these closes the exposure; the team picks based on transparency stance:
- Gate the page (same fix as the other three routes — redirect or AccessGate).
- Keep the page public, but filter the data source. Add a label/tag convention
(e.g. internal, security) and have the github-roadmap edge function exclude
items with those labels before returning to the client. Cheap once the convention
exists; preserves a public-facing roadmap of features without the security/
planning bycatch.
Option 2 is good if "we want a public roadmap" is the actual product intent. Option 1
is the right default if not.
Suggested fix (one of)
- Make the gating consistent and route-level. Either redirect to
/auth on
direct visit (matches the checklist wording) or render the same AccessGate panel
/profile already uses, on all four routes. Pick one and apply uniformly.
- Fix the docs. If "anon can see the UI shell, just no data" is the intended
model, the checklist should say so — and the sidebar shouldn't pretend otherwise.
Acceptance criteria
Notes
- These were found because direct-URL gating was never tested in §3 (sidebar-clicks
only). Future route additions should verify both entry points.
- This is separate from the
ci-auth helper bug (which blocks all signed-in tests).
The fix here doesn't require login fixtures to work — it's about what anon sees.
[QA] §13/§14/§17/§18 — anon access to "auth-only" routes doesn't match the checklist (no leak, but inconsistent gating)
Sections: §13 Calendar, §14 Roadmap, §17 Profile, §18 Admin
Severity: low → moderate (escalated 2026-05-27 after reviewing
/roadmapcontent; see "Update" below).The other three routes remain low-severity (no data exposure).
/roadmapexposes internalplanning and a handful of items that telegraph specific security gaps.
Found via: §13/14/17/18 manual mechanism check (paste URL as anon, observe).
Summary
The QA checklist says anon visitors to
/calendar,/roadmap,/profile, and/adminare "redirected to/auth." None of the four actually does that. The fourroutes split into three different behaviors, none of which are a clean redirect:
/calendar/roadmapgithub-roadmapedge function/profile/profile; renders an inline sign-in widget (AccessGate pattern)/adminThe sidebar correctly gates all four with a click-time redirect to
/auth. The bug isthat clicking the link and pasting the URL behave differently. Anyone who lands
on these routes via a bookmark, browser autocomplete, an external link, or a typed URL
bypasses the lock entirely.
Why this is worth fixing even without a leak
Users get conflicting signals about whether they're allowed to be there.
the spec or the app should change so they agree.
only gate is "RLS + empty queries returning nothing useful." If anyone later adds
data to one of these pages without remembering to add a route guard, it leaks
silently. A real guard (route-level redirect OR a consistent AccessGate on every
gated route) catches that class of mistake at the door.
/adminspecifically: "System Alerts: all healthy" is itself a (very small) leak— it confirms the alerts subsystem exists and is healthy to anyone who can type the
URL. Low impact in isolation; bad pattern if it becomes the rule.
Update (2026-05-27):
/roadmapcontent reviewSkimmed the rendered roadmap as anon. The page shows 80+ GitHub items grouped under
Features / Bugs / Tasks. Most of it is in the "internal sprint planning that some
teams expose on purpose" category — but a small subset is the kind of content
typically kept behind a login:
Items that explicitly advertise security gaps:
concern. Publishable Supabase keys are designed to be public, so the bug may be a
misnomer, but the framing tells a reader "the team treats this as unresolved
security debt."
"no DDoS protection in place."
"not SOC2-compliant."
telegraphs "not yet audited / hardened / backed up."
These don't leak data, but together they advertise a list of soft spots to anyone
who reads the page. That's the textbook reason "open security backlog" is normally
gated.
Internal planning visible to anon (lower concern, but unusual to expose):
is normal for public issue trackers, but exposing the curated list on the app
itself is a different posture.
Fix options for
/roadmapspecificallyEither of these closes the exposure; the team picks based on transparency stance:
(e.g.
internal,security) and have thegithub-roadmapedge function excludeitems with those labels before returning to the client. Cheap once the convention
exists; preserves a public-facing roadmap of features without the security/
planning bycatch.
Option 2 is good if "we want a public roadmap" is the actual product intent. Option 1
is the right default if not.
Suggested fix (one of)
/authondirect visit (matches the checklist wording) or render the same AccessGate panel
/profilealready uses, on all four routes. Pick one and apply uniformly.model, the checklist should say so — and the sidebar shouldn't pretend otherwise.
Acceptance criteria
QA_CHECKLIST.md§13.1 / §14.1 / §17.1 / §18.1 wording to match.§13/§14/§17/§18Playwright tests (currently written against theobserved behavior with
test.fail()notes referencing this bug) flip toexpect(page).toHaveURL(/\/auth/)if redirect is chosen, orexpect(<gate>) .toBeVisible()if AccessGate is chosen. Remove thetest.fail()s./adminSystem Alerts banner does not render to anon under whichevergate is chosen.
Notes
only). Future route additions should verify both entry points.
ci-authhelper bug (which blocks all signed-in tests).The fix here doesn't require login fixtures to work — it's about what anon sees.