fix(sources): name check hides values under short account keys

The name check used the log redactor's key list, which leaves out the
short keys `pass`, `pas`, `user` and `usr` that the URL stripper drops.
Both now share `isDisplaySensitiveKey`, so a name holding `?pass=…`
collapses to the host; `user`/`usr` are account names like `username`
and stay shown at the top level, as by design.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-10-13 23:54:51 +02:00
1 parent e4d22cbac8
commit 94faaa3fc4
3 files changed
+27 -4

No files matched your search

@@ -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', () => {
@@ -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);
}
@@ -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);
}