diff --git a/lib/utils/log_redaction_manager.dart b/lib/utils/log_redaction_manager.dart index 8072e297..6fb31ce6 100644 --- a/lib/utils/log_redaction_manager.dart +++ b/lib/utils/log_redaction_manager.dart @@ -24,6 +24,11 @@ class LogRedactionManager { /// Pattern-based catch-all for Jellyfin Quick Connect auth handles. static final RegExp _jellyfinQuickConnectSecretQueryParam = RegExp(r'secret=[^&#\s]+', caseSensitive: false); + /// Pattern-based catch-all for the Plex Home PIN sent as a `pin=` query + /// param by `/home/users/{uuid}/switch`. The `\b` keeps compound params + /// like `checkPin=` intact. + static final RegExp _pinQueryParam = RegExp(r'\bpin=[^&#\s]+', caseSensitive: false); + /// Pattern-based catch-all for the legacy Emby/Jellyfin header form. static final RegExp _embyTokenHeader = RegExp(r'X-Emby-Token[:=]\s*[^,;&#\s"]+', caseSensitive: false); @@ -120,6 +125,7 @@ class LogRedactionManager { redacted = redacted.replaceAll(_jellyfinApiKeyQueryParam, 'api_key=[REDACTED]'); redacted = redacted.replaceAll(_jellyfinQuickConnectSecretQueryParam, 'secret=[REDACTED]'); + redacted = redacted.replaceAll(_pinQueryParam, 'pin=[REDACTED]'); redacted = redacted.replaceAllMapped(_embyTokenHeader, (m) { final value = m.group(0)!; final separator = value.contains(':') ? ':' : '='; diff --git a/test/utils/log_redaction_manager_test.dart b/test/utils/log_redaction_manager_test.dart index c934a8a4..0a2d54e8 100644 --- a/test/utils/log_redaction_manager_test.dart +++ b/test/utils/log_redaction_manager_test.dart @@ -60,6 +60,21 @@ void main() { expect(result.contains('Authenticated=false'), isTrue); }); + test('redacts Plex Home pin query parameter without registration', () { + final input = 'POST https://clients.plex.tv/api/v2/home/users/uuid/switch?X-Plex-Token=tok&pin=1234 → 201'; + final result = LogRedactionManager.redact(input); + expect(result.contains('pin=1234'), isFalse); + expect(result.contains('pin=[REDACTED]'), isTrue); + }); + + test('pin redaction is case-insensitive and leaves compound params intact', () { + final result = LogRedactionManager.redact('PIN=0000&checkPin=abc&next=1'); + expect(result.contains('PIN=0000'), isFalse); + expect(result.contains('pin=[REDACTED]'), isTrue); + expect(result.contains('checkPin=abc'), isTrue); + expect(result.contains('next=1'), isTrue); + }); + test('redacts X-Emby-Token header form', () { final result = LogRedactionManager.redact('X-Emby-Token: emby-secret'); expect(result.contains('emby-secret'), isFalse);