Chess on the ATmosphere checkmate.blue

Reject opponent records that do not continue our game #47

Merged checkmate.blue
targeting main
Merged opened by and assigned to checkmate.blue
targeting main

Three tickets, one change: HAD-146, HAD-157, HAD-158.

An opponent's record is untrusted input, but verifyOpponentResult loaded their claimed PGN into a fresh board and accepted any game-over position it found. A fabricated two-move checkmate passed, and the client then wrote the resulting loss into its own repo: a forged, permanent, public result signed by the victim.

  • verifyPgnExtends (game-logic.ts) requires a PGN to continue the game we already have. Same [FEN] starting header, and no divergence in move history up to the shorter length, so same-length rewrites are caught too.
  • verifyOpponentResult moves to src/lib/game-verify.ts and takes the current board, so no result is derived from a PGN that fails that check.
  • reconcileCompletion only accepts an agreed draw when our own record shows we offered one. The old comment claimed this check; the code never did it.
  • applyOpponentMove returns applied | stale | invalid | mismatch and sets integrityError, which shows a banner, locks the board, and blocks every write.

Verified against a copy of the pre-fix code: the fabricated-checkmate test plus four store tests fail without this, and pass with it. 159 tests green.

This pull request has no versions submitted yet.