Fix top 5 dangerous bugs (terminal raw-mode, credential overwrite, firmware cleanup, fd leak, substring check)#174
Open
davmonk wants to merge 1 commit into
Open
Conversation
…er, timeline
- countdown: join the input-reader child before close() and recover from
a race where close() raises ValueError, ensuring os_specifics.finalize()
always runs and the terminal is restored from raw mode.
- retroaccount: only persist save_user_json when the server returned a
valid non-empty tokens dict; previously an empty/non-dict response
could overwrite valid credentials and force an unnecessary logout.
- pocket firmware: continue iterating after finding the matching firmware
so older pocket_firmware*.bin files iterated after it are still removed.
- arcade organizer: use os.scandir() as a context manager in
_remove_broken_symlinks to release file descriptors on each recursion.
- timeline: replace `formatted_category in ('system')` (a substring check
due to the missing trailing comma) with `== 'system'`.
Co-Authored-By: Claude Opus 4.7 <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.
Summary
Five independent, high-confidence bug fixes found while auditing the codebase. Each is a real, demonstrable bug — no refactoring or speculative cleanup. All 164 unit + 85 integration tests still pass.
1.
countdown.py— terminal can be left in raw modeAfter
child_process.terminate(), the code immediately callschild_process.close().Process.close()raisesValueErrorif the process hasn't fully exited yet (the read-loop child blocks in_getch(), so it relies on terminate to die). When that races,close()raises and the subsequentos_specifics.finalize()— which restores the TTY — never runs, leaving the user's terminal stuck.Fix:
join(timeout=1.0)the child first, wrapclose()in atry/except ValueErrorwith akill()fallback, and ensurefinalize()always runs.2.
retroaccount.py— invalid server response can overwrite valid credentials_build_mister_sync_transitiondidnew_user_json = response.get('tokens', None)and then conditionally addeddevice_idonly when the value was a non-empty dict — but unconditionally passedsave_user_json=new_user_jsoninto the transition. An empty dict or non-dicttokenspayload would still get written over the validuser.json, and on the next sync the missingdevice_id/refresh_tokenwould triggercredentials_were_corruptedand force the user to re-login.Fix: only set
new_user_json(and thereforesave_user_json) whentokensis a non-empty dict.3.
pocket_firmware_update.py— stale firmware files left on the Pocket SDThe cleanup loop iterates
pocket_firmware*.binand removes old ones, butbreaks as soon as it finds the latest. Any stale firmware files that come after the latest one in glob order are never removed.Fix:
continueinstead ofbreakso the rest of the directory is still cleaned.4.
arcade_organizer.py—os.scandir()not closed in a recursive function_remove_broken_symlinkscalls itself for every subdirectory while holding an openos.scandir()iterator (no context manager). Each recursive call leaks a file descriptor until GC. On deep trees this can exhaust the per-process FD limit.Fix: wrap in
with os.scandir(directory) as entries:.5.
timeline.py— substring check instead of equalityif formatted_category in ('system'):is missing the trailing comma, so('system')is the string'system', not a 1-tuple. Python evaluates this as a substring check ('sys' in 'system'is True) rather than equality. Currently masked because no other categories happen to be substrings of'system', but the intent is clearly equality.Fix:
formatted_category == 'system'.Test plan
python3 -m unittest discover -s test/unit— 164 tests passpython3 -m unittest discover -s test/integration— 85 tests pass.binfiles)🤖 Generated with Claude Code