- _serve_spa built file paths directly from the URL with no containment
check, on a route reachable with zero authentication. Verified the exact
exploit (percent-encoded traversal) reproduces path escape, then fixed by
resolving the path and requiring it stay under WEB_DIR before ever
touching the filesystem.
- OAuth state was generated and handed to the client but never bound
server-side or checked on callback — classic login CSRF/session fixation.
Now bound to a short-lived httponly cookie set during login and verified
(constant-time) on callback.
- Default bind changed from 0.0.0.0 to 127.0.0.1 — loading this cog with
zero config shouldn't expose a web server on every interface before the
owner has even run setup. `[p]evedash host 0.0.0.0` still opts back in.
- _check_guild_access and the callback's guild-membership check relied
solely on the member cache, which can wrongly deny a real admin if the
Members intent hasn't warmed for that guild yet. Both now fall back to a
live fetch on cache miss (per-guild for the single-guild endpoints,
bot-wide only in the rare one-time login path — deliberately not done in
the bulk guild-list endpoint, where that fallback would mean an API call
per guild on every page load).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>