Hash and verify passwords off the event loop (B-55)
Argon2 is deliberately expensive — tens of milliseconds of CPU per call. Called inline from the async handlers for register, login, change-password and the admin reset, that cost froze the entire process for its duration: every other request, plus all six background tasks (scheduler, confirmation poller, RBF bumper, listener, both reconcilers). A burst of unauthenticated login attempts was therefore not just slow logins, it delayed draws and confirmations. hash_password_async/verify_password_async wrap the existing pair in run_in_threadpool, and every async caller now uses them. The synchronous functions stay: they're what the wrappers call, and what tests and scripts (no running loop) use directly. The regression test runs a heartbeat task alongside the hashing and counts how often the loop got to run it — 1 tick with the old inline call, many with the threadpooled one. Also drops the running "already fixed and removed" list from BUGS.md: the file tracks open findings, and `git log --all --grep 'B-nn'` is the record of how a closed one was closed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,14 +5,10 @@ Third full-codebase audit, opened after the 2026-07-26 (B-01 … B-24) and
|
||||
fixed finding, B-51.
|
||||
|
||||
The list opened at B-52 … B-72 and holds only what is still **open**: a finding is
|
||||
removed from this file once it is fixed. Per CLAUDE.md's convention each entry gets
|
||||
its own commit with its own regression test, and the `B-nn` marker goes in a comment
|
||||
next to the fix so `git log --all --grep 'B-nn'` finds it later.
|
||||
|
||||
Already fixed and removed: B-52 (a round past ~50 participants deadlocked the
|
||||
payout — `025754c`), B-53 (a bet could pay into the pool of a round it was left
|
||||
out of — `64f6229`), B-54 (`X-Forwarded-For` was read from the front, so every
|
||||
IP-keyed control was bypassable — `907e32e`).
|
||||
removed from this file once it is fixed, and is not listed here afterwards. Per
|
||||
CLAUDE.md's convention each entry gets its own commit with its own regression test,
|
||||
and the `B-nn` marker goes in a comment next to the fix, so
|
||||
`git log --all --grep 'B-nn'` is the record of how any closed finding was closed.
|
||||
|
||||
State of the tree at audit time: 264 unit tests, all passing; `tests/integration/`
|
||||
still empty; withdrawal and the RBF bump path still never live-broadcast.
|
||||
@@ -44,20 +40,6 @@ remains the last prerequisite for running unattended.
|
||||
|
||||
## High — security
|
||||
|
||||
### B-55 — Argon2 hashing runs on the event loop
|
||||
|
||||
`app/auth/security.py:20-39`, called from `app/auth/routes.py` and
|
||||
`app/api/routes/users.py`.
|
||||
|
||||
`hash_password`/`verify_password` are synchronous and cost tens of milliseconds
|
||||
each, so every login, registration and password change blocks the whole process —
|
||||
including all six background tasks (scheduler, confirmation poller, RBF bumper,
|
||||
listener, both reconcilers). A burst of unauthenticated login attempts (which,
|
||||
per B-54, is not effectively throttled) is a cheap denial of service that also
|
||||
delays draws and confirmations.
|
||||
|
||||
Fix: run both through `starlette.concurrency.run_in_threadpool`.
|
||||
|
||||
### B-56 — `RateLimiter._buckets` is never pruned
|
||||
|
||||
`app/auth/rate_limit.py:37`.
|
||||
|
||||
Reference in New Issue
Block a user