mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-18 06:41:27 +02:00
- register_client now validates the client-supplied clientId (type, length, charset) before it enters clientSocketMap as both key and value, closing a map-poisoning / mapping-impersonation vector. - mempoolNotified is now a Map with real TTL eviction owned by PaymentHandlers (the actual dedup set). The dead duplicate Set in SocketHandlers and its random-10%-per-cycle eviction (which could drop live entries) are removed; addresses are also evicted promptly when monitoring stops. - The SessionManager `sessions` cache is evicted on disconnect (removeSocket, called after the suspend logic reads it) so it no longer grows unbounded with every socket ever seen. - sessionManager.dispose(), suspendedGameManager.cleanup() and paymentHandlers.dispose() are wired into SocketHandlers.shutdown() so their timers/maps don't leak on shutdown. - checkGamesTimeout snapshots activeGames before iterating and awaits each game-over sequentially (was mutating the Map during forEach and firing async game-overs concurrently, which could skip games / double-process). Adds inputValidation.test.js. Full suite: 19 suites / 149 tests.
56 lines
1.8 KiB
JavaScript
56 lines
1.8 KiB
JavaScript
/**
|
|
* Input validation / hardening tests (Phase 2.3).
|
|
*
|
|
* register_client must validate the client-supplied id before it goes into clientSocketMap
|
|
* (as both key and value), or a client could poison the map / impersonate mappings.
|
|
*/
|
|
|
|
const ConnectionHandler = require('../src/network/connectionHandler');
|
|
|
|
function buildHandler() {
|
|
const ch = new ConnectionHandler({
|
|
io: { sockets: { sockets: new Map() } },
|
|
broadcastManager: { broadcastUserCount: () => {} },
|
|
debugManager: { CONSOLE_LOGGING: false },
|
|
sessionManager: null,
|
|
rateLimiter: null
|
|
});
|
|
return ch;
|
|
}
|
|
|
|
function fakeSocket(id) {
|
|
const emitted = [];
|
|
return { id, emit: (event, data) => emitted.push({ event, data }), _emitted: emitted };
|
|
}
|
|
|
|
describe('handleRegisterClient validation', () => {
|
|
let ch;
|
|
afterEach(() => ch && ch.shutdown && ch.shutdown());
|
|
|
|
test('accepts a valid clientId and records the mapping', () => {
|
|
ch = buildHandler();
|
|
const sock = fakeSocket('sock1');
|
|
ch.handleRegisterClient(sock, { clientId: 'client-abc_123' });
|
|
|
|
expect(ch.clientSocketMap.get('client-abc_123')).toBe('sock1');
|
|
expect(ch.clientSocketMap.get('sock1')).toBe('client-abc_123');
|
|
expect(sock._emitted.find(e => e.event === 'socket_registered').data.success).toBe(true);
|
|
});
|
|
|
|
test.each([
|
|
['missing', undefined],
|
|
['empty', ''],
|
|
['too long', 'x'.repeat(65)],
|
|
['bad chars', 'evil<script>'],
|
|
['non-string', 12345]
|
|
])('rejects %s clientId and does not poison the map', (_label, badId) => {
|
|
ch = buildHandler();
|
|
const sock = fakeSocket('sock2');
|
|
ch.handleRegisterClient(sock, { clientId: badId });
|
|
|
|
expect(ch.clientSocketMap.size).toBe(0);
|
|
const reg = sock._emitted.find(e => e.event === 'socket_registered');
|
|
expect(reg.data.success).toBe(false);
|
|
});
|
|
});
|