Skip to content

[QA] §13/§14/§17/§18 — anon access to "auth-only" routes doesn't match the checklist (no leak, but inconsistent gating) #189

Description

@bleonar5

[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

  1. 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.
  2. Spec drift: the checklist promises a redirect; the app doesn't deliver it. Either
    the spec or the app should change so they agree.
  3. 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.
  4. /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:

  1. Gate the page (same fix as the other three routes — redirect or AccessGate).
  2. 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)

  1. 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.
  2. 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

  • Pick the gating model (redirect or AccessGate) and apply it to all four routes.
  • Update QA_CHECKLIST.md §13.1 / §14.1 / §17.1 / §18.1 wording to match.
  • The §13/§14/§17/§18 Playwright tests (currently written against the
    observed behavior with test.fail() notes referencing this bug) flip to
    expect(page).toHaveURL(/\/auth/) if redirect is chosen, or expect(<gate>) .toBeVisible() if AccessGate is chosen. Remove the test.fail()s.
  • Confirm /admin System Alerts banner does not render to anon under whichever
    gate is chosen.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions