diff --git a/libs/playlist/shared/util/src/lib/source-display/source-display.spec.ts b/libs/playlist/shared/util/src/lib/source-display/source-display.spec.ts index b7777f214..f5dae0e39 100644 --- a/libs/playlist/shared/util/src/lib/source-display/source-display.spec.ts +++ b/libs/playlist/shared/util/src/lib/source-display/source-display.spec.ts @@ -74,6 +74,24 @@ describe('sourceDisplayName', () => { it('keeps an account name in the name: it is shown by design', () => { expect(sourceDisplayName({ title: 'joe', url: M3U_URL })).toBe('joe'); + expect( + sourceDisplayName({ + title: 'joe', + url: 'https://host.test/list?usr=joe&pass=hunter2', + }) + ).toBe('joe'); + }); + + it('drops a name holding a password under a short account key', () => { + for (const key of ['pass', 'pas', 'PASS']) { + const url = `https://host.test/list?user=joe&${key}=hunter2`; + expect(sourceDisplayName({ title: 'hunter2', url })).toBe( + 'host.test' + ); + expect(sourceDisplayName({ title: 'Mine hunter2', url })).toBe( + 'host.test' + ); + } }); it('shows the host for a name an import took from a URL fragment', () => { diff --git a/libs/playlist/shared/util/src/lib/source-display/source-display.ts b/libs/playlist/shared/util/src/lib/source-display/source-display.ts index 716381b5e..16c2db0ee 100644 --- a/libs/playlist/shared/util/src/lib/source-display/source-display.ts +++ b/libs/playlist/shared/util/src/lib/source-display/source-display.ts @@ -1,4 +1,3 @@ -import { isSensitiveKey } from '@iptvnator/shared/logging'; import { PlaylistMeta, playlistDisplayLabel, @@ -6,6 +5,7 @@ import { resolvePlaylistSourceKind, } from '@iptvnator/shared/interfaces'; import { + isDisplaySensitiveKey, isXtreamAccountPath, stripUrlCredentials, unwrapNestedValue, @@ -26,7 +26,7 @@ export type SourceDisplayFields = Partial< >; /** The account name stays on screen by design; every other secret goes. */ -const SHOWN_ACCOUNT_KEYS = new Set(['username', 'login']); +const SHOWN_ACCOUNT_KEYS = new Set(['username', 'login', 'user', 'usr']); /** Shorter values match ordinary words, not secrets. */ const MIN_SECRET_LENGTH = 3; const ABSOLUTE_URL = /^[a-z][a-z0-9+.-]*:\/\//i; @@ -110,7 +110,7 @@ function collectUrlSecrets( const visit = (params: URLSearchParams): void => params.forEach((value, key) => { const normalized = key.toLowerCase().replace(/[^a-z0-9]/g, ''); - if (isSensitiveKey(key)) { + if (isDisplaySensitiveKey(key)) { if (depth > 0 || !SHOWN_ACCOUNT_KEYS.has(normalized)) { secrets.push(value); } diff --git a/libs/playlist/shared/util/src/lib/source-display/strip-url-credentials.ts b/libs/playlist/shared/util/src/lib/source-display/strip-url-credentials.ts index 9ab029646..f2306f370 100644 --- a/libs/playlist/shared/util/src/lib/source-display/strip-url-credentials.ts +++ b/libs/playlist/shared/util/src/lib/source-display/strip-url-credentials.ts @@ -70,7 +70,12 @@ export function isXtreamAccountPath( ); } -function isDisplaySensitiveKey(key: string): boolean { +/** + * A parameter whose value must not be on screen: every key the log redactor + * treats as sensitive, plus the short account keys (`pass`, `usr`, …). + * The name check uses the same predicate, so it hides what the URL hides. + */ +export function isDisplaySensitiveKey(key: string): boolean { return SHORT_ACCOUNT_KEYS.has(key.toLowerCase()) || isSensitiveKey(key); }