Repository navigation
geoword: security + bug fixes - #624
Merged
Merged
Conversation
The game sent record-buzz without waiting and enabled Next right away. The server is about to serve only the question after the last saved buzz, so a quick Next could ask for a question it won't serve yet. Next now enables once the buzz is saved; if saving fails, the page says so and asks the player to reload, which resumes at the unsaved question. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The play routes only checked payment, so a paid player could fetch any question's audio, instructions, or answer before reaching it, e.g. check-answer?questionNumber=10 returned question 10's answer to a player who hadn't heard question 1. A new middleware validates questionNumber and lets the request through only when it is the question after the player's last recorded buzz. It covers question.mp3, get-instructions, and check-answer; sample.mp3 and the results relisten route are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
record-buzz took any question number any number of times. Recording question 1 ten times made the buzz count match the packet length, which unlocked the results pages and the progress gate, and each copy counted on the leaderboards. Putting record-buzz behind the same middleware accepts one buzz per question, in order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
question.mp3, get-instructions, and check-answer read the division from the query string, so a player could fetch another division's questions. A middleware now replaces it with the division the player chose, and returns 400 if they haven't chosen one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
recordBuzz looked up the buzz's tossup by packet and question number only. Each division has its own question 1, 2, ..., so in a multi-division packet a buzz could point at another division's tossup, and the results, category stats, team, and admin pages (which join on tossup_id since 6fef7cc) would show that tossup's question, answer, and category. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
geoffrey-wu
force-pushed
the
geoword-play-progress-gate
branch
from
October 21, 2026 03:47
3a9e9fe to
9e16799
Compare
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Adds tools/geoword/add-buzz-unique-index.js, which creates a unique index on buzzes so a double-sent buzz can't be saved twice; record-buzz answers 409 when that index rejects one. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The paid play routes only checked payment, so a paid player could:
check-answer?questionNumber=10returned question 10's answer to a player who hadn't heard question 1)Changes
require-current-question.js:question.mp3,get-instructions,check-answer, andrecord-buzzonly accept the question after the player's last buzz.use-division-choice.js: the server uses the player's chosen division instead of the query string.play-logscollection: each audio fetch and answer check is logged, so peeking, a fakedisCorrect, scripted buzzes, and inflated celerity can be audited afterwards. Logging doesn't prevent these.recordBuzznow matches the tossup in the player's division. Since 6fef7cc it ignored division, so multi-division packets could link buzzes to the wrong tossup.Testing
Locally: full playthroughs in headless Chrome, including resuming after a reload and a failed save. Out-of-order requests and repeated buzzes return 403. An audit over the logs flagged each simulated cheat and nothing from honest play.
🤖 Generated with Claude Code