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
+15
View File
@@ -48,6 +48,20 @@ afterEach(() => {
});
describe('RCON status transport', () => {
it('keeps reply bodies out of public failure summaries and redacts UDP diagnostics', async () => {
const request = rconStatusResult(server.name);
await vi.advanceTimersByTimeAsync(0);
sockets[0].emit('message', packet('print\nplayer 192.0.2.123:28960 unexpected response'));
const result = await request;
expect(result).toMatchObject({ ok: false, reason: 'bad-reply' });
if (result.ok) throw new Error('Expected an unrecognized reply');
expect(result.detail).not.toContain('player');
expect(result.detail).not.toContain('192.0.2');
const hex = result.rawReply!.split('\n').map(line => line.slice(6, 53).trim()).join(' ').replaceAll(' ', '');
const decoded = Buffer.from(hex, 'hex').toString();
expect(decoded).toContain('[redacted]');
expect(decoded).not.toContain('192.0.2');
});
it.each([
['timeout', 'timeout', 'Agent received no reply'],
['unreachable', 'send-error', 'Agent could not reach'],
@@ -79,6 +93,7 @@ describe('RCON status transport', () => {
expect(sockets[0].send.mock.calls[0][0].toString()).toContain('rcon test status\n');
reply();
expect(await health).toMatchObject({ ok: true, status: { players: 1, map: 'mp_kino' } });
expect(await health).not.toHaveProperty('rawReply');
expect(await occupancy).toBe(1);
expect(await duplicate).toEqual(await health);
expect(sockets[0].close).toHaveBeenCalledOnce();