From 616ed7250396f037f3be969ef5572c650134569e Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Mon, 27 Apr 2026 12:10:42 +0200 Subject: [PATCH] fix: exclude mal/anilist/simkl session keys from settings export --- lib/services/settings_export_service.dart | 16 +++++-- .../settings_export_service_test.dart | 42 +++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/lib/services/settings_export_service.dart b/lib/services/settings_export_service.dart index 63c63b03..12ae6131 100644 --- a/lib/services/settings_export_service.dart +++ b/lib/services/settings_export_service.dart @@ -80,10 +80,18 @@ class SettingsExportService { }; /// Prefix denylist. A key is excluded if it starts with any of these. - /// `trakt_` covers OAuth session tokens and the runtime sync queue — the - /// `enable_trakt_*` feature toggles don't match this prefix and stay - /// exportable. - static const List _denyPrefixes = ['server_endpoint_', 'episode_count_', 'watched_threshold_', 'trakt_']; + /// The tracker prefixes (`trakt_`, `mal_`, `anilist_`, `simkl_`) cover + /// OAuth session tokens and runtime sync queues. The `enable_*` feature + /// toggles use a different prefix and stay exportable. + static const List _denyPrefixes = [ + 'server_endpoint_', + 'episode_count_', + 'watched_threshold_', + 'trakt_', + 'mal_', + 'anilist_', + 'simkl_', + ]; /// Literal prefix used by [StorageService._userPrefix] for any scoped key. static const String _userPrefixRoot = 'user_'; diff --git a/test/services/settings_export_service_test.dart b/test/services/settings_export_service_test.dart index b58480c8..1139c835 100644 --- a/test/services/settings_export_service_test.dart +++ b/test/services/settings_export_service_test.dart @@ -114,6 +114,48 @@ void main() { expect(p, contains('enable_trakt_scrobble')); }); + test('drops MAL / AniList / SIMKL session keys but keeps their feature toggles', () async { + final prefs = await BaseSharedPreferencesService.sharedCache(); + // Stripped tracker session tokens — these would carry access_token / + // refresh_token JSON if they leaked into the export. + await prefs.setString('mal_session', '{"access_token":"a","refresh_token":"r"}'); + await prefs.setString('anilist_session', '{"access_token":"a"}'); + await prefs.setString('simkl_session', '{"access_token":"a"}'); + // Feature toggles use the `enable_` prefix and SHOULD survive. + await prefs.setBool('enable_mal_scrobble', true); + await prefs.setBool('enable_anilist_scrobble', true); + await prefs.setBool('enable_simkl_scrobble', true); + + final out = SettingsExportService.buildExportMap(prefs); + final p = out['prefs'] as Map; + + expect(p, isNot(contains('mal_session'))); + expect(p, isNot(contains('anilist_session'))); + expect(p, isNot(contains('simkl_session'))); + expect(p, contains('enable_mal_scrobble')); + expect(p, contains('enable_anilist_scrobble')); + expect(p, contains('enable_simkl_scrobble')); + }); + + test('user-scoped tracker sessions are dropped after the user prefix is stripped', () async { + final prefs = await BaseSharedPreferencesService.sharedCache(); + // TrackerAccountStore writes under user_{uuid}_{baseKey}. After the + // active-user prefix is stripped on export, the key falls under the + // tracker prefix denylist. + await prefs.setString('user_alice_mal_session', '{"access_token":"a"}'); + await prefs.setString('user_alice_anilist_session', '{"access_token":"a"}'); + await prefs.setString('user_alice_simkl_session', '{"access_token":"a"}'); + await prefs.setString('user_alice_trakt_session', '{"access_token":"a"}'); + + final out = SettingsExportService.buildExportMap(prefs, currentUserUuid: 'alice'); + final p = out['prefs'] as Map; + + expect(p, isNot(contains('mal_session'))); + expect(p, isNot(contains('anilist_session'))); + expect(p, isNot(contains('simkl_session'))); + expect(p, isNot(contains('trakt_session'))); + }); + test('drops the internal migration flag', () async { final prefs = await BaseSharedPreferencesService.sharedCache(); await prefs.setBool('buffer_size_migrated_to_auto', true);