mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-20 22:01:45 +00:00
fix(security): stop cross-user offline data leak on shared devices (#1176)
Closes BLOCKER B4 — three reinforcing paths could serve one account's cached data to the next user on a shared device: - The Workbox 'api-data' cache keyed trip/user-scoped GETs by URL only (cookie-blind). Changed to NetworkOnly; offline reads come from the per-user IndexedDB cache via the repo layer instead. - IndexedDB had no per-user scoping. The Dexie connection is now scoped per user (trek-offline-u<id>) behind a Proxy so the ~19 importers keep a stable binding; login opens the user DB, logout deletes it and returns to the anonymous DB. - logout() was fire-and-forget and racy: background flush/syncAll could re-seed the DB after the wipe. It is now async and ordered — close an auth gate, unregister sync triggers, disconnect, clear caches, delete the user DB — and flush()/syncAll() bail when the gate is closed.
This commit is contained in:
@@ -23,6 +23,9 @@ import {
|
||||
upsertReservations,
|
||||
upsertTripFiles,
|
||||
upsertSyncMeta,
|
||||
reopenForUser,
|
||||
reopenAnonymous,
|
||||
deleteCurrentUserDb,
|
||||
type QueuedMutation,
|
||||
type SyncMeta,
|
||||
type BlobCacheEntry,
|
||||
@@ -271,3 +274,37 @@ describe('offlineDb — clearAll', () => {
|
||||
expect(await offlineDb.places.count()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('offlineDb — per-user scoping (B4)', () => {
|
||||
afterEach(async () => {
|
||||
// Leave the suite on the anonymous DB so other tests are unaffected.
|
||||
await reopenAnonymous();
|
||||
});
|
||||
|
||||
it('isolates one user\'s cached data from another', async () => {
|
||||
await reopenForUser(1);
|
||||
await upsertPlaces([makePlace(10, 1)]);
|
||||
expect(await offlineDb.places.count()).toBe(1);
|
||||
|
||||
// Switching users must not expose user 1's rows.
|
||||
await reopenForUser(2);
|
||||
expect(await offlineDb.places.count()).toBe(0);
|
||||
|
||||
// Switching back restores user 1's data (different physical DB).
|
||||
await reopenForUser(1);
|
||||
expect(await offlineDb.places.get(10)).toBeDefined();
|
||||
});
|
||||
|
||||
it('deleteCurrentUserDb wipes the user DB and returns to anonymous', async () => {
|
||||
await reopenForUser(5);
|
||||
await upsertPlaces([makePlace(20, 1)]);
|
||||
|
||||
await deleteCurrentUserDb();
|
||||
// Now on the anonymous DB — no user data.
|
||||
expect(await offlineDb.places.count()).toBe(0);
|
||||
|
||||
// Re-opening user 5 starts empty (DB was deleted, not just detached).
|
||||
await reopenForUser(5);
|
||||
expect(await offlineDb.places.count()).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user