fix(logging): redact Xtream path credentials

This commit is contained in:
4gray committed 2026-07-18 19:02:53 +02:00
1 parent 8a34bad28a
commit 002f559b9d
2 files changed
+50

No files matched your search

@@ -105,6 +105,40 @@ describe('redactSensitiveData', () => {
expect(output).toContain('profile');
});
it('redacts Xtream credentials in playback URL paths', () => {
const username = 'xtream-path-user-secret';
const password = 'xtream-path-password-secret';
const urls = [
new URL(
`https://example.com/base/live/${username}/${password}/101.ts`
),
new URL(
`https://example.com/movie/${username}/${password}/202.mkv`
),
new URL(
`https://example.com/series/${username}/${password}/303.mp4`
),
new URL(
`https://example.com/timeshift/${username}/${password}/60/2026-07-18:12-00/404.ts`
),
];
const embedded = `Playback failed for https://example.com/live/${username}/${password}/505.m3u8`;
const ordinaryLiveUrl = 'https://example.com/live/channel.m3u8';
const output = serialized(
redactSensitiveData({ urls, embedded, ordinaryLiveUrl })
);
expect(output).not.toContain(username);
expect(output).not.toContain(password);
expect(output).toContain('101.ts');
expect(output).toContain('202.mkv');
expect(output).toContain('303.mp4');
expect(output).toContain('404.ts');
expect(output).toContain('505.m3u8');
expect(output).toContain(ordinaryLiveUrl);
});
it('does not mutate input and safely bounds cycles, depth, arrays, objects, and strings', () => {
const input: Record<string, unknown> = {
status: 'ok',
@@ -37,6 +37,13 @@ const SENSITIVE_KEY_SUFFIXES = [
'username',
];
const XTREAM_CREDENTIAL_PATH_SEGMENTS = new Set([
'live',
'movie',
'series',
'timeshift',
]);
export interface RedactionOptions {
maxDepth?: number;
maxArrayItems?: number;
@@ -119,6 +126,15 @@ function redactUrl(
redacted.password = REDACTED_VALUE;
}
const pathSegments = redacted.pathname.split('/');
for (let index = 0; index < pathSegments.length - 3; index += 1) {
if (XTREAM_CREDENTIAL_PATH_SEGMENTS.has(pathSegments[index])) {
pathSegments[index + 1] = REDACTED_VALUE;
pathSegments[index + 2] = REDACTED_VALUE;
}
}
redacted.pathname = pathSegments.join('/');
const search = redactSearchParams(
redacted.searchParams,
sanitizeValue