From 9a4b8fb2990ae6a8ffc38476f4e002c8057ae161 Mon Sep 17 00:00:00 2001 From: Josue Zamudio Date: Wed, 16 Sep 2026 00:43:02 +0000 Subject: [PATCH] Docs: audit fix batch plan (sync login, scoped restart, per-IP budget, login CSRF, pinned deps) --- docs/CHANGES-2026-09.md | 44 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 docs/CHANGES-2026-09.md diff --git a/docs/CHANGES-2026-09.md b/docs/CHANGES-2026-09.md new file mode 100644 index 0000000..65d7baa --- /dev/null +++ b/docs/CHANGES-2026-09.md @@ -0,0 +1,44 @@ +# Code-audit fix batch (2026-09) + +Findings from the 2026-09-15 code audit, in shipping order. The systemd +cutover was explicitly deferred — the process stays on run.sh's nohup +model; only the restart mechanism's blast radius is fixed here. + +## 1. login handler blocks the event loop (HIGH) + +`login` is `async def`, so the anti-brute-force `time.sleep(0.5)` inside +the lockout check runs ON the event loop: every wrong password stalls +every in-flight request (all streams included) on the single worker. +Fix: make the handler a plain `def` (uvicorn runs it on the threadpool); +move the lockout logic into a side-effect-free `server/auth.py` so it is +unit-testable without booting the app. Regression test measures +`/api/cdn` latency while a wrong-password login is mid-sleep. + +## 2. run.sh global pkill (HIGH) + +`pkill -f "uvicorn .*app:app"` also matches the test server +(`uvicorn server.app:app --host 127.0.0.1 --port 8399`): an ani-web +restart kills running test sessions (and vice versa). Fix: PID file +(`server.pid`) becomes the primary kill path; any pattern fallback is +port-scoped to the real instance. + +## 3. lockout is per-username only (MEDIUM) + +Spreading failures across usernames bypasses the per-username lockout +entirely (and anyone can lock a known account for 15 min). Add a +per-IP failure budget (`ANIWEB_IP_FAILS` / window) on top of the +per-username lockout. Client IP = `request.client.host`; the reverse +proxy's `X-Forwarded-For` is consulted only when `ANIWEB_TRUST_XFF=1` +(set by run.sh), so a spoofed header can't widen or narrow the gate. + +## 4. login is exempt from the CSRF header check (MEDIUM) + +The custom-header check that makes cross-site form POSTs fail excludes +`/login`. The exemption is load-bearing only by accident (JSON body +content-type); login becomes covered by the same check as every other +mutation, and login.html sends the header. + +## 5. dependencies unpinned (MEDIUM) + +`curl_cffi` version churn = impersonation churn = silent upstream 403 +waves. requirements.txt becomes a full freeze; bump deliberately.