From 56e1da93c1f47f8134909d6763818731845a5fc5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 12 Apr 2026 15:40:25 +0000 Subject: [PATCH] Address code review feedback: improve session secret generation, document rate-limit design Agent-Logs-Url: https://github.com/naturallaw777/staging_alpha/sessions/afb996f6-f6f5-4d4a-9f99-e46e3f89b4d7 Co-authored-by: naturallaw777 <99053422+naturallaw777@users.noreply.github.com> --- app/sovran_systemsos_web/server.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py index 7723d1f..af4f9e9 100644 --- a/app/sovran_systemsos_web/server.py +++ b/app/sovran_systemsos_web/server.py @@ -499,7 +499,11 @@ class NoCacheMiddleware(BaseHTTPMiddleware): # ── Session / authentication helpers ───────────────────────────── def _get_or_create_session_secret() -> bytes: - """Return the Hub session secret, generating it on first boot.""" + """Return the Hub session secret, generating it on first boot. + + The file is stored in /var/lib/secrets/ (mode 0600) so it is wiped + automatically during a security reset, which forces re-login after reset. + """ try: with open(HUB_SESSION_SECRET_FILE, "rb") as f: data = f.read().strip() @@ -507,15 +511,17 @@ def _get_or_create_session_secret() -> bytes: return data except FileNotFoundError: pass - secret = secrets.token_hex(32).encode() + # Generate 32 random bytes and hex-encode for human readability + token_bytes = secrets.token_bytes(32) + token_hex = token_bytes.hex().encode() try: os.makedirs(os.path.dirname(HUB_SESSION_SECRET_FILE), exist_ok=True) with open(HUB_SESSION_SECRET_FILE, "wb") as f: - f.write(secret) + f.write(token_hex) os.chmod(HUB_SESSION_SECRET_FILE, 0o600) except OSError: pass - return secret + return token_hex def _create_session() -> str: @@ -571,13 +577,17 @@ def _check_password(submitted: str) -> bool: def _record_failure(client_ip: str) -> None: - """Record a failed login attempt and apply a rate-limit delay.""" + """Record a failed login attempt and apply a rate-limit delay. + + Must always be called via loop.run_in_executor() so that the blocking + time.sleep() does not stall the asyncio event loop. + """ now = time.time() failures = _login_failures.setdefault(client_ip, []) # Prune old entries outside the window _login_failures[client_ip] = [t for t in failures if now - t < LOGIN_FAIL_WINDOW] _login_failures[client_ip].append(now) - # Always sleep a fixed delay to slow brute force + # Sleep in the thread-pool thread to slow brute-force without blocking the loop time.sleep(LOGIN_FAIL_DELAY)