Skip to content

geoword: security + bug fixes - #624

Merged
geoffrey-wu merged 10 commits into
qbreader:mainfrom
dn285:geoword-play-progress-gate
Oct 21, 2026
Merged

geoffrey-wu merged 10 commits into
qbreader:mainfrom
dn285:geoword-play-progress-gate

Conversation

@dn285

@dn285 dn285 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

The paid play routes only checked payment, so a paid player could:

  • fetch any question's audio, instructions, or answer before reaching it (check-answer?questionNumber=10 returned question 10's answer to a player who hadn't heard question 1)
  • record the same question's buzz repeatedly to unlock results and inflate leaderboard points
  • request another division's questions

Changes

  • require-current-question.js: question.mp3, get-instructions, check-answer, and record-buzz only 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.
  • The game enables Next only after the buzz is saved, so a quick Next can't run ahead of the gate.
  • New play-logs collection: each audio fetch and answer check is logged, so peeking, a faked isCorrect, scripted buzzes, and inflated celerity can be audited afterwards. Logging doesn't prevent these.
  • recordBuzz now 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

@dn285 dn285 changed the title Geoword play progress gate Security and bug fixes Oct 21, 2026
dn285 and others added 6 commits October 20, 2026 23:47
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>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@geoffrey-wu geoffrey-wu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

@geoffrey-wu geoffrey-wu changed the title Security and bug fixes geoword: security + bug fixes Oct 21, 2026
geoffrey-wu and others added 3 commits October 20, 2026 23:58
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>
@geoffrey-wu
geoffrey-wu merged commit a51af31 into qbreader:main Oct 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants