Skip to content

fix(security): scope dashboard cache per-user (cross-account metrics bleed)#9

Merged
ralyodio merged 1 commit into
masterfrom
fix/dashboard-localstorage-bleed
Jun 12, 2026
Merged

fix(security): scope dashboard cache per-user (cross-account metrics bleed)#9
ralyodio merged 1 commit into
masterfrom
fix/dashboard-localstorage-bleed

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

The bug you hit

/dashboard, /console, and /dashboard/projections showed another account's
metrics
after switching accounts in the same browser.

Root cause (client-side, not the DB)

Verified the backend is clean: RLS policies are correct (user_id = auth.uid()),
the source_state rows contain only each user's own {enabled, daemon} (no keys,
positions, or cross-account data), and the runtime cache is keyed per user.

The leak was in useSourceState(): it persisted the live source-state to a
single global localStorage key b1dz:source-state and hydrated from it on
load. Switching accounts on one browser showed the previous account's cached
metrics, and an empty account never overwrote them. Settings has no localStorage
— which is why settings looked clean but the dashboards didn't.

Fix

  • Scope the cache key per user: b1dz:source-state:<userId>.
  • Start empty (no synchronous hydrate from a global key) so first paint never
    shows another account's data; resolve the user, then hydrate that user's cache.
  • Purge the legacy global key on load to clear already-leaked blobs from browsers.

Part of the multi-tenant security pass (follow-up to #5).

🤖 Generated with Claude Code

… accounts)

useSourceState persisted the live source-state (arb/trade/pipeline/pumpfun
metrics) to a SINGLE global localStorage key 'b1dz:source-state' and hydrated
from it on load. On a shared browser, switching accounts showed the previous
account's metrics on /dashboard, /console, and /dashboard/projections — and an
empty account never overwrote them. (Settings has no localStorage, which is why
it looked clean.)

- Scope the cache key per user id: 'b1dz:source-state:<userId>'.
- Start empty (no synchronous hydrate from a global key) so first paint never
  shows another account's data; resolve the user, then hydrate that user's cache.
- Purge the legacy global key on load to clear already-leaked blobs from browsers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio ralyodio merged commit 6ca79b2 into master Jun 12, 2026
4 of 6 checks passed
@ralyodio ralyodio deleted the fix/dashboard-localstorage-bleed branch June 12, 2026 09:25
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.

1 participant