[Hardening] Fix /record start guard leak that permanently locks out recording for a guild #83
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
/record startinbot/questboard_bot/cogs/recording.pyuses a per-guild reservation setself._starting(initialized at line 189) to stop a second concurrent start from racing through the async setup. The guard at line 212 rejects any start whileguild.idis inself._activeorself._starting.Current behavior
The slash-command path adds the guild to the reservation set at line 222 — but the
try:whosefinallydiscards it (lines 317–318) only begins at line 246. Two early returns sit in the gap:returnat line 233returnat line 242Either return leaves
guild.idstranded inself._starting, so every subsequent/record startin that guild fails the line-212 guard with "A recording is already in progress for this server" until the bot process restarts. One typo'd UUID permanently locks out recording for the guild.The API-triggered path is already correct:
start_from_apiadds at line 342 immediately followed bytry:at 343 withfinally: self._starting.discard(...)at 416–417, and no early exits in between.Fix / Spec
record_start, moveself._starting.add(guild.id)from line 222 down to immediately before the existingtry:at line 246 (mirroring thestart_from_apipattern). This is safe: the code between the guard (line 212) and line 246 —uuid.UUID(...)parse,isinstance/member.voicechecks — is fully synchronous, so no other coroutine can interleave between guard check and reservation.finally: self._starting.discard(guild.id)at 317–318 unchanged.bot/tests/test_recording.py: invokerecord_startwith an invalid session ID (and separately with the member not in voice), assert the guild is not left incog._startingafterward / that a second start attempt is not rejected by the guard.Acceptance criteria
/record startwith an invalid UUID, a subsequent valid/record startfor the same guild proceeds past the guard.awaiton the success path).bot/tests/test_recording.py.References
bot/questboard_bot/cogs/recording.py:189(_startingset),:212(guard),:222(leaked add),:233/:242(early returns),:246(try:),:317-318(finallydiscard)bot/questboard_bot/cogs/recording.py:339-343,:416-417(start_from_api)Filed from the July 2026 full-project review.
Picking this up as part of a v3.3.0 push. Landing on branch
hardening/bottogether with #85, #94, and #111 (grouped by component to keep the diffs reviewable).Fixed on
main(commit42664a3, merged viacd315d6).self._starting.add(guild.id)moved down to immediately before thetry:inrecord_start, past the synchronous UUID-parse and voice-channel checks, mirroring the already-correctstart_from_apipattern. Regression tests inbot/tests/test_recording.pycover both early-return paths (invalid UUID; user not in voice) — the guild is no longer stranded in_starting, and a subsequent valid start proceeds. Bot suite green (158 passed).