mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 21:56:18 -08:00
fix(logging): redact sensitive portal and Electron diagnostics (#1182)
* fix: redact sensitive log data * fix(ci): keep logging preload self-contained * fix(logging): preserve shared diagnostics * fix(logging): close trace redaction gaps * fix(logging): redact Xtream path credentials * fix(logging): close credential redaction gaps * fix(logging): suppress external player arguments * fix(logging): harden URL and date redaction * fix(logging): redact map keys and URL fragments * fix(logging): redact sensitive map values * fix(logging): redact credentials in diagnostic text * fix(logging): close remaining credential leaks
This commit is contained in:
1 parent
29e624b0dd
commit
5cae310430
29 files changed
+1203
-158
No files matched your search
@@ -16,6 +16,7 @@ describe('ElectronService', () => {
|
||||
const session = { id: 'session-1' };
|
||||
let electronBridge: {
|
||||
fetchPlaylistByUrl: jest.Mock;
|
||||
onPlayerError: jest.Mock;
|
||||
openInMpv: jest.Mock;
|
||||
openInVlc: jest.Mock;
|
||||
};
|
||||
@@ -24,9 +25,11 @@ describe('ElectronService', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(console, 'log').mockImplementation(() => undefined);
|
||||
jest.spyOn(console, 'error').mockImplementation(() => undefined);
|
||||
|
||||
electronBridge = {
|
||||
fetchPlaylistByUrl: jest.fn(),
|
||||
onPlayerError: jest.fn(),
|
||||
openInMpv: jest.fn().mockResolvedValue(session),
|
||||
openInVlc: jest.fn().mockResolvedValue(session),
|
||||
};
|
||||
@@ -99,6 +102,23 @@ describe('ElectronService', () => {
|
||||
expect(electronBridge.fetchPlaylistByUrl).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('redacts credentials from backend player errors before logging', () => {
|
||||
const secret = 'player-error-token-secret';
|
||||
const listener = electronBridge.onPlayerError.mock.calls[0][0];
|
||||
|
||||
listener({
|
||||
player: 'MPV',
|
||||
error: 'Playback failed',
|
||||
originalError: `Request failed: token=${secret}&channel=news`,
|
||||
});
|
||||
|
||||
const output = JSON.stringify(
|
||||
(console.error as jest.Mock).mock.calls
|
||||
);
|
||||
expect(output).not.toContain(secret);
|
||||
expect(output).toContain('channel=news');
|
||||
});
|
||||
|
||||
it('shows the trust-host action for Electron-wrapped security errors', async () => {
|
||||
const securityPayload = {
|
||||
code: ELECTRON_BRIDGE_SECURITY_ERROR_CODES.InvalidTlsCertificate,
|
||||
|
||||
@@ -7,7 +7,6 @@ import { DialogService } from '@iptvnator/ui/components';
|
||||
import { DataService, SettingsStore } from '@iptvnator/services';
|
||||
import {
|
||||
AUTO_UPDATE_PLAYLISTS,
|
||||
createDevLogger,
|
||||
ELECTRON_BRIDGE_SECURITY_ERROR_CODES,
|
||||
ERROR,
|
||||
normalizeHost,
|
||||
@@ -22,6 +21,7 @@ import {
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { AppConfig } from '../../environments/environment';
|
||||
import {
|
||||
createLogger,
|
||||
createPortalDebugRequestContext,
|
||||
logPortalDebugEvent,
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
@@ -54,7 +54,7 @@ export class ElectronService extends DataService {
|
||||
private readonly store = inject(Store);
|
||||
private readonly settingsStore = inject(SettingsStore);
|
||||
private readonly translateService = inject(TranslateService);
|
||||
private readonly debugLog = createDevLogger('ElectronService');
|
||||
private readonly logger = createLogger('ElectronService');
|
||||
private readonly silentXtreamActions = new Set<string>([
|
||||
XtreamCodeActions.GetAccountInfo,
|
||||
XtreamCodeActions.GetLiveCategories,
|
||||
@@ -67,7 +67,6 @@ export class ElectronService extends DataService {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.debugLog('Electron service initialized...');
|
||||
this.setupPlayerErrorListener();
|
||||
this.setupPortalDebugListener();
|
||||
}
|
||||
@@ -81,7 +80,10 @@ export class ElectronService extends DataService {
|
||||
error: string;
|
||||
originalError: string;
|
||||
}) => {
|
||||
console.error(`${data.player} Error:`, data.originalError);
|
||||
this.logger.error(
|
||||
`${data.player} Error:`,
|
||||
data.originalError
|
||||
);
|
||||
this.snackBar.open(
|
||||
`${data.player} Error: ${data.error}`,
|
||||
'Close',
|
||||
@@ -182,7 +184,7 @@ export class ElectronService extends DataService {
|
||||
duration: 5000,
|
||||
}
|
||||
);
|
||||
console.error('MPV launch error:', error);
|
||||
this.logger.error('MPV launch error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -211,7 +213,7 @@ export class ElectronService extends DataService {
|
||||
duration: 5000,
|
||||
}
|
||||
);
|
||||
console.error('VLC launch error:', error);
|
||||
this.logger.error('VLC launch error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -237,7 +239,7 @@ export class ElectronService extends DataService {
|
||||
return playlists as T;
|
||||
}
|
||||
|
||||
this.debugLog('Unknown IPC event type:', type);
|
||||
this.logger.debug('Unknown IPC event type:', type);
|
||||
return undefined as T;
|
||||
}
|
||||
|
||||
@@ -265,7 +267,7 @@ export class ElectronService extends DataService {
|
||||
return response;
|
||||
} catch (err: unknown) {
|
||||
const errorInfo = this.getErrorDetails(err);
|
||||
console.error('Stalker request error:', err);
|
||||
this.logger.error('Stalker request error:', err);
|
||||
this.snackBar.open(
|
||||
`Error: ${errorInfo?.message ?? ' Not found'}, status: ${errorInfo?.status ?? 404}`,
|
||||
'Close',
|
||||
@@ -362,7 +364,7 @@ export class ElectronService extends DataService {
|
||||
data.title
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
this.logger.error(
|
||||
'Either url or filePath must be provided, but not both.'
|
||||
);
|
||||
return;
|
||||
@@ -387,7 +389,7 @@ export class ElectronService extends DataService {
|
||||
{ duration: 2000 }
|
||||
);
|
||||
} catch (error: unknown) {
|
||||
console.error('Playlist refresh error:', error);
|
||||
this.logger.error('Playlist refresh error:', error);
|
||||
if (
|
||||
data.url &&
|
||||
this.handlePlaylistSecurityError(error, () => {
|
||||
@@ -590,12 +592,12 @@ export class ElectronService extends DataService {
|
||||
|
||||
// Log error to console
|
||||
if (isSilentAction) {
|
||||
this.debugLog(
|
||||
this.logger.debug(
|
||||
`Background Xtream action failed (${action ?? 'unknown'}):`,
|
||||
normalizedMessage
|
||||
);
|
||||
} else {
|
||||
console.error('Xtream request error:', normalizedMessage);
|
||||
this.logger.error('Xtream request error:', normalizedMessage);
|
||||
}
|
||||
|
||||
// Only show snackbar for user-triggered Xtream requests
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
} from 'rxjs';
|
||||
import { DataService } from '@iptvnator/services';
|
||||
import {
|
||||
createDevLogger,
|
||||
ERROR,
|
||||
Playlist,
|
||||
PLAYLIST_PARSE_BY_URL,
|
||||
@@ -32,6 +31,7 @@ import {
|
||||
createPortalDebugSuccessEvent,
|
||||
logPortalDebugEvent,
|
||||
logPortalDebugRequest,
|
||||
createLogger,
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { getRuntimeBackendUrl } from './runtime-config';
|
||||
|
||||
@@ -71,7 +71,7 @@ export class PwaService extends DataService {
|
||||
private readonly store = inject(Store);
|
||||
private readonly swUpdate = inject(SwUpdate);
|
||||
private readonly translateService = inject(TranslateService);
|
||||
private readonly debugLog = createDevLogger('PwaService');
|
||||
private readonly logger = createLogger('PwaService');
|
||||
private readonly providerTargetIds = new Map<string, Promise<string>>();
|
||||
private readonly silentXtreamActions = new Set<string>([
|
||||
XtreamCodeActions.GetAccountInfo,
|
||||
@@ -88,7 +88,6 @@ export class PwaService extends DataService {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.debugLog('PWA service initialized...');
|
||||
}
|
||||
|
||||
/** Uses service worker mechanism to check for available application updates */
|
||||
@@ -358,7 +357,7 @@ export class PwaService extends DataService {
|
||||
);
|
||||
|
||||
if (isSilentAction) {
|
||||
this.debugLog(
|
||||
this.logger.debug(
|
||||
`Background Xtream action failed (${action ?? 'unknown'}):`,
|
||||
normalizedMessage
|
||||
);
|
||||
@@ -398,7 +397,7 @@ export class PwaService extends DataService {
|
||||
|
||||
// Log error to console
|
||||
if (isSilentAction) {
|
||||
this.debugLog(
|
||||
this.logger.debug(
|
||||
`Background Xtream action failed (${action ?? 'unknown'}):`,
|
||||
normalizedMessage
|
||||
);
|
||||
@@ -409,7 +408,7 @@ export class PwaService extends DataService {
|
||||
};
|
||||
}
|
||||
|
||||
console.error('Xtream request error:', normalizedMessage);
|
||||
this.logger.error('Xtream request error:', normalizedMessage);
|
||||
this.snackBar.open(
|
||||
`Xtream request failed: ${normalizedMessage}`,
|
||||
'Close',
|
||||
@@ -533,7 +532,7 @@ export class PwaService extends DataService {
|
||||
} catch (err: unknown) {
|
||||
const errorInfo = this.getErrorDetails(err);
|
||||
logPortalDebugEvent(createPortalDebugErrorEvent(context, err));
|
||||
console.error('Stalker request error:', err);
|
||||
this.logger.error('Stalker request error:', err);
|
||||
|
||||
this.snackBar.open(
|
||||
`Error: ${errorInfo?.message ?? ' Not found'}, status: ${errorInfo?.status ?? 404}`,
|
||||
|
||||
Reference in new issue
Block a user