Protect RCON diagnostics and redact player addresses (Refs #61)

This commit is contained in:
2026-10-08 16:47:34 -04:00
parent 29d2d9634c
commit 0cc4498b53
36 changed files with 280 additions and 83 deletions
+33 -1
View File
@@ -4,8 +4,40 @@ import {
parsePlayerCount,
parsePlayerRoster,
parseStatusHeader,
formatRawReply
formatRawReply,
agentRconStatusResult
} from '../../src/lib/server/integrations/rcon.js';
import { redactRconAddresses } from '../../src/lib/server/integrations/rcon-diagnostics.js';
describe('private RCON diagnostics', () => {
const addresses = ['192.0.2.123:28960', '198.51.100.9', '[2001:db8::1]:28960',
'2001:db8::abcd', '[::ffff:192.0.2.4]:28960', '[fe80::1%eth0]:1234', '::1'];
it.each(addresses)('redacts %s from both plain text and decoded hex diagnostics', address => {
const text = `unrecognized status\nplayer address ${address} trailing text`;
expect(redactRconAddresses(text)).toBe('unrecognized status\nplayer address [redacted] trailing text');
const dump = formatRawReply(Buffer.from(text));
const hex = dump.split('\n').map(line => line.slice(6, 53).trim()).join(' ').replaceAll(' ', '');
const decoded = Buffer.from(hex, 'hex').toString();
expect(decoded).toBe(redactRconAddresses(text));
});
it('redacts complete addresses before truncating native and UDP replies', () => {
const native = agentRconStatusResult({ text: 'x'.repeat(3990) + ' 192.0.2.123:28960' }, { host: 'agent', port: 1 });
expect(native).toMatchObject({ ok: false });
expect(native.rawReply).not.toContain('192.0.2');
expect(native.rawReply!.length).toBeLessThanOrEqual(4000);
const dump = formatRawReply(Buffer.from('x'.repeat(500) + ' 192.0.2.123:28960'));
const hex = dump.split('\n').filter(line => /^[0-9a-f]{4} /.test(line)).map(line => line.slice(6, 53).trim()).join(' ').replaceAll(' ', '');
expect(Buffer.from(hex, 'hex').toString()).not.toContain('192.0.2');
});
it('omits native success replies while preserving the parsed status', () => {
const result = agentRconStatusResult({ text: 'print\nmap: mp_kino\nnum score ping guid name address\n--- ----- ---- ---- ---- -------\n' }, { host: 'agent', port: 1 });
expect(result).toMatchObject({ ok: true, status: { players: 0, map: 'mp_kino' } });
expect(result).not.toHaveProperty('rawReply');
});
});
describe('parsePlayerCount', () => {
it('counts rows after --- --- separator (status dialect)', () => {