Fix critical bugs in ttyd_proxy.py - #4
Conversation
- RateLimiter: add periodic purge of stale keys to prevent OOM under sustained unique-IP traffic - Login handler: catch UnicodeDecodeError on POST body decode, return 400 - TTYD proxy: remove internal error details from 502 responses, log to stderr instead Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
@claude review. Focus on critical issues: bugs, security vulnerabilities, logical errors, data loss risks, performance problems. Do NOT nitpick style, naming conventions, minor formatting, or subjective preferences — only flag issues that could break functionality or cause real harm in production. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
@claude review this PR. Focus only on critical issues: bugs, security vulnerabilities, logical errors, data loss risks, performance problems. |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
@claude review. Focus on critical issues: bugs, security vulnerabilities, logical errors, data loss risks, performance problems. Do NOT nitpick style, naming conventions, minor formatting, or subjective preferences — only flag issues that could break functionality or cause real harm in production. |
|
Claude finished @axisrow's task in 1m 49s —— View job PR Review
SummaryAll three fixes are correct. No critical issues introduced by this PR. Fix 1: RateLimiter memory leak (
|
|
@claude review this PR. Focus only on critical issues: bugs, security vulnerabilities, logical errors, data loss risks, performance problems. |
|
Claude finished @axisrow's task in 1m 15s —— View job PR Review
SummaryAll three fixes are correct. No critical issues introduced by this PR. Fix 1: RateLimiter memory leak (
|
…ину слёта auth #69 (#86) * test(entrypoint): pin Claude Code auth root cause #69 (mount /home/hapi, not /home) Root cause established by reproducing the "auth keeps resetting" report in Docker: the ONLY trigger is the persistent volume mounted at the parent `/home` instead of `/home/hapi`. A Docker volume is seeded from the image only while empty, so once a platform's persistent storage (Dokku/Railway) survives the first deploy and stays non-empty, a `/home` mount is no longer re-seeded — the entrypoint recreates `/home/hapi/.claude` empty on top of the volume and the saved login is gone. A `/home/hapi` mount keeps the home dir itself as the persisted unit and survives recreate (verified both ways). The three rival hypotheses from #69 were disproven by code: - entrypoint chown -R on ~/.claude is correct (restores ownership), non-destructive - cleanup_runner_state is scoped to ~/.hapi/*.json, never touches .claude - bin/glm exports ANTHROPIC_* only inside the wrapper and exec's claude — no leak Changes: - README.md + CLAUDE.md: explicit warning to mount /home/hapi, NOT /home, with the volume-seeding mechanism that makes the wrong mount lose creds. - tests/unit/test_shell_scripts.py: new TestClaudeAuthRootCause69 pinning the three artifacts that keep the bug fixed — CLAUDE_CONFIG_DIR path, the docs warning (both files), and the glm exec-wrapper contract (hypothesis #4). pytest tests/ -> 344 passed, 194 subtests. Refs #69 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vecu5xo9D9Cs2vmeohKVKv * feat(entrypoint): warn on console when volume mounted at /home not /home/hapi (#69) Make the #69 footgun self-announcing instead of docs-only. The wrong mount is a runtime condition, so the entrypoint now detects it at startup and prints a loud WARNING to stderr (visible in `docker logs` / Dokku deploy logs): WARNING: a volume is mounted at /home, NOT at /home/hapi. Detection reads /proc/mounts and keys off exact mountpoints: a separate mount AT /home with no separate mount at /home/hapi means the volume is one level too high. Warn-only, never abort — running without a volume (no mount at either) is valid, and a usable container beats a refused start. Verified in Docker across all three cases: warns on `-v ...:/home`, silent on `-v ...:/home/hapi`, silent with no volume. - entrypoint.sh: warn_if_volume_mounted_at_parent_home(), called at startup. - README.md: note the runtime warning alongside the existing mount guidance. - tests: TestClaudeAuthRootCause69.test_entrypoint_warns_on_parent_home_mount. pytest tests/ -> 345 passed, 194 subtests. Refs #69 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vecu5xo9D9Cs2vmeohKVKv * fix(entrypoint): warn about volume migration footgun + behavioral detector test (#69) Cycle-review (PR #86) — Codex flagged a critical practical hole: the warning told operators to "mount at /home/hapi", but for an EXISTING /home volume that data lives under the volume's hapi/ subdir. Retargeting the same volume to /home/hapi buries it at /home/hapi/hapi/ — invisible to the app — so the redeploy meant to fix persistence looks like it lost the creds. Confirmed against the volume layout observed in earlier Docker repro. - entrypoint.sh: warning now spells out the migration step (move the volume's hapi/ contents to its root, or use a fresh /home/hapi volume). - README.md + CLAUDE.md: same migration caveat alongside the mount guidance. - tests: test_warning_explains_volume_migration pins the migration text in all three places; test_detector_warns_only_on_parent_home_mount is BEHAVIORAL — it extracts the real detector and runs the awk logic against five synthetic /proc/mounts tables (closes the static-grep coverage gap noted by the Claude reviewer). Verified in Docker: the migration warning prints on a /home mount. pytest tests/ -> 347 passed, 201 subtests. Refs #69 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Vecu5xo9D9Cs2vmeohKVKv --------- Co-authored-by: axisrow <axisrow@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
) Six P1 correctness/reliability fixes from the #101 code review. #3 (proxy.py): enable SO_KEEPALIVE on both tunnel sockets so a client that vanishes without a FIN (laptop sleep, NAT timeout) is detected by the OS and the idle tunnel thread + sockets are freed instead of leaking forever. #4 (ratelimit.py): use time.monotonic() instead of wall-clock time.time(), so a backward clock jump (NTP correction, suspend/resume, VM migration) cannot freeze the window and lock out legitimate users past 60s/300s. #5 (app.py): _check_auth now honours redirect=True in the removed-user branch — a browser navigation with a valid cookie whose account was deleted/renamed lands on /login (302) instead of a raw 403 JSON blob. #6 (proxy.py): add "cookie" to HOP_BY_HOP_HEADERS so the client's signed ttyd_session + csrf_token is not forwarded to the internal ttyd (defense depth). #10 (bin/clihost-sync.sh): the remote symlink guards were byte-identical triplicates (local pair + one copy in each of two `bash -s` heredocs), so hardening one silently bypassed the others — the class of bug caught 3× before (#88/#90/#95). Define them once in a shared emit_remote_prelude injected into both remote payloads. #11 (.env.example): document SESSION_TIMEOUT, CLEANUP_ROOT, ROOT_PASSWORD, HAPI_USER, HERMES_AUTO_UPDATE (read by code + CLAUDE.md but missing from the example), plus the REQUEST_TIMEOUT slowloris knob for completeness. New/changed env vars documented in .env.example: SESSION_TIMEOUT, CLEANUP_ROOT, ROOT_PASSWORD, HAPI_USER, HERMES_AUTO_UPDATE, REQUEST_TIMEOUT. No port or volume changes. Closes #101 findings #3, #4, #5, #6, #10, #11. Claude-Session: https://claude.ai/code/session_01KgvJhzd8gEhXVE4MVu18nn Co-authored-by: axisrow <axisrow@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
attemptsdict keys (IP addresses) were never purged, causing unbounded memory growth. Added periodic cleanup of stale keys every 100 calls.str(exc)leaked internal details (IPs, ports, OS errors) to clients. Removeddetailfield, errors logged to stderr instead.Test plan
/login, verify 400 responsedetailfield🤖 Generated with Claude Code