fix(bot): harden ws.ts integration seam

- maybeAbandon Promise no longer floats from setTimeout
- broadcastSinceLast loses dead extra parameter
- bot-slot token is randomized so a third party can't hijack the
  bot's color by guessing a fixed placeholder
This commit is contained in:
claude (blind_chess)
2026-04-28 14:17:46 -04:00
parent a9660c0694
commit 88bc23b0d0
2 changed files with 7 additions and 14 deletions
+4 -3
View File
@@ -93,10 +93,11 @@ function makeSlot(token: PlayerToken, now: number) {
}
function makeBotSlot(now: number) {
// Synthetic slot: occupies the player's color but never connects.
// Token is a 24-char placeholder; never matches a real client.
// Synthetic slot: occupies the player's color but never connects. The token
// is randomized (same shape as a real client token) so a third party can't
// hijack the bot's color by guessing a fixed placeholder.
return {
token: 'bot' + 'x'.repeat(21),
token: newPlayerToken(),
socket: null,
joinedAt: now,
rateBucket: { tokens: RATE_LIMIT.capacity, last: now },
+3 -11
View File
@@ -32,20 +32,12 @@ async function pokeBot(game: Game): Promise<void> {
}
}
function broadcastSinceLast(
game: Game,
extra?: { touchedPieceFor?: Color; touchedPiece?: import('@blind-chess/shared').Square },
): void {
function broadcastSinceLast(game: Game): void {
for (const c of ['w', 'b'] as const) {
const lastIdx = game.lastBroadcastIdx[c];
const all = game.announcements;
const slice = all.slice(lastIdx).filter((a) => a.audience === 'both' || a.audience === c);
sendUpdateTo(
game,
c,
slice,
extra?.touchedPieceFor === c ? { touchedPiece: extra.touchedPiece } : undefined,
);
sendUpdateTo(game, c, slice);
game.lastBroadcastIdx[c] = all.length;
}
}
@@ -249,7 +241,7 @@ function onClose(ctx: SocketCtx): void {
if (game.status === 'active') {
game.disconnectAt[color] = Date.now();
// Schedule grace timer.
setTimeout(() => maybeAbandon(game, color), GRACE_MS + 100);
setTimeout(() => { void maybeAbandon(game, color); }, GRACE_MS + 100);
}
notifyPeer(game, color, false, Date.now() + GRACE_MS);
}