mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
fix(logging): close remaining credential leaks
This commit is contained in:
1 parent
7d2f525c9c
commit
7f4b5be848
4 files changed
+47
-15
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,
|
||||
|
||||
@@ -80,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',
|
||||
@@ -181,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;
|
||||
}
|
||||
}
|
||||
@@ -210,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;
|
||||
}
|
||||
}
|
||||
@@ -361,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;
|
||||
@@ -386,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, () => {
|
||||
|
||||
@@ -51,10 +51,19 @@ describe('redactSensitiveData', () => {
|
||||
it('redacts Stalker identity credentials while retaining request diagnostics', () => {
|
||||
const identitySecrets = {
|
||||
sn: 'stalker-sn-secret',
|
||||
serialNumber: 'stalker-serial-number-secret',
|
||||
device_id: 'stalker-device-id-secret',
|
||||
deviceId1: 'stalker-device-id1-secret',
|
||||
device_id2: 'stalker-device-id2-secret',
|
||||
signature: 'stalker-signature-secret',
|
||||
signature1: 'stalker-signature1-secret',
|
||||
signature2: 'stalker-signature2-secret',
|
||||
stalkerSerialNumber: 'playlist-stalker-serial-number-secret',
|
||||
stalkerDeviceId1: 'playlist-stalker-device-id1-secret',
|
||||
stalkerDeviceId2: 'playlist-stalker-device-id2-secret',
|
||||
stalkerSignature1: 'playlist-stalker-signature1-secret',
|
||||
stalkerSignature2: 'playlist-stalker-signature2-secret',
|
||||
prehash: 'stalker-prehash-secret',
|
||||
};
|
||||
|
||||
const result = redactSensitiveData({
|
||||
@@ -141,10 +150,12 @@ describe('redactSensitiveData', () => {
|
||||
const authorization = 'diagnostic-authorization-secret';
|
||||
const authorizationAssignment =
|
||||
'diagnostic-authorization-assignment-secret';
|
||||
const stalkerDeviceId = 'diagnostic-stalker-device-id-secret';
|
||||
const diagnostic = [
|
||||
`Request failed: token=${token}&action=get_profile`,
|
||||
`Upstream response Authorization: Bearer ${authorization}; status=401`,
|
||||
`Retrying with authorization=Bearer ${authorizationAssignment}, attempt=2`,
|
||||
`Identity rejected: stalkerDeviceId1: ${stalkerDeviceId}; action=handshake`,
|
||||
];
|
||||
|
||||
const output = serialized(redactSensitiveData(diagnostic));
|
||||
@@ -152,9 +163,11 @@ describe('redactSensitiveData', () => {
|
||||
expect(output).not.toContain(token);
|
||||
expect(output).not.toContain(authorization);
|
||||
expect(output).not.toContain(authorizationAssignment);
|
||||
expect(output).not.toContain(stalkerDeviceId);
|
||||
expect(output).toContain('get_profile');
|
||||
expect(output).toContain('status=401');
|
||||
expect(output).toContain('attempt=2');
|
||||
expect(output).toContain('action=handshake');
|
||||
});
|
||||
|
||||
it('does not repeat a redacted Error message secret in its stack', () => {
|
||||
|
||||
@@ -33,15 +33,11 @@ const SENSITIVE_KEY_NAMES = new Set([
|
||||
]);
|
||||
|
||||
const SENSITIVE_KEY_SUFFIXES = [
|
||||
'apikey',
|
||||
'authorization',
|
||||
'cookie',
|
||||
'macaddress',
|
||||
'passwd',
|
||||
'password',
|
||||
'secret',
|
||||
'token',
|
||||
'username',
|
||||
'apikey', 'authorization', 'cookie',
|
||||
'deviceid', 'deviceid1', 'deviceid2',
|
||||
'macaddress', 'passwd', 'password', 'prehash',
|
||||
'serialnumber', 'signature', 'signature1', 'signature2',
|
||||
'secret', 'token', 'username',
|
||||
];
|
||||
|
||||
const XTREAM_CREDENTIAL_PATH_SEGMENTS = new Set([
|
||||
@@ -185,7 +181,7 @@ function redactEmbeddedSensitivePairs(value: string): string {
|
||||
: match
|
||||
);
|
||||
return redactedAssignments.replace(
|
||||
/\b([a-z0-9_.-]*(?:api[-_.]?key|auth(?:orization)?|cookie|credentials|device[-_.]?id2?|login|mac(?:[-_.]?address)?|passwd|password|pwd|secret|set[-_.]?cookie|signature2?|sn|token|username))(\s*:\s*)(?:Bearer\s+)?[^;,\r\n]+/giu,
|
||||
/\b([a-z0-9_.-]*(?:api[-_.]?key|auth(?:orization)?|cookie|credentials|device[-_.]?id[12]?|login|mac(?:[-_.]?address)?|passwd|password|prehash|pwd|secret|serial[-_.]?number|set[-_.]?cookie|signature[12]?|sn|token|username))(\s*:\s*)(?:Bearer\s+)?[^;,\r\n]+/giu,
|
||||
(_match, key: string, separator: string) =>
|
||||
`${key}${separator}${REDACTED_VALUE}`
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user