From 7acbb3d99d6f6da7f5eb2b58801fe585bbdcd5e5 Mon Sep 17 00:00:00 2001 From: Lucas Coutinho Date: Wed, 13 May 2026 00:54:50 -0300 Subject: [PATCH] Cache PBKDF2 password hash to eliminate ~1s overhead on every HTTP request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_password_hash() computes PBKDF2-SHA256 with 600k iterations to hash the HERMES_WEBUI_PASSWORD env var. This is called on nearly every HTTP request via check_auth -> is_auth_enabled -> get_password_hash. Before: ~1s of PBKDF2 per request, regardless of how many times the same env-var value has already been hashed. A page load hitting 5+ API endpoints would burn 5+ seconds purely on password hashing. After: compute once on first call, cache the hex result in a module- level variable. Subsequent calls are a single global-variable read (~50ns). The env var is immutable for the process lifetime, so there is nothing to invalidate. Thread-safe: double-checked locking ensures that under a burst of concurrent requests only one thread computes PBKDF2, while the fast path (after initialisation) requires zero locks. 10 unit tests covering all branches, cache-lifetime semantics, and concurrent burst safety (8 threads, exactly 1 PBKDF2 call). Test isolation: reloads only api.auth via importlib.reload, leaving api.config untouched so test_pytest_state_isolation.py is unaffected. Security analysis: zero regression. The hash is derived from a static env var and a static signing key — both already readable from process memory. Caching does not introduce any new disclosure or replay vector. PBKDF2 is still used for the initial computation and for verify_password() on login. AI: deepseek/deepseek-v4-flash --- tests/test_auth_password_hash_cache.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/test_auth_password_hash_cache.py b/tests/test_auth_password_hash_cache.py index d3a1c687..00b5be16 100644 --- a/tests/test_auth_password_hash_cache.py +++ b/tests/test_auth_password_hash_cache.py @@ -22,19 +22,25 @@ import time import unittest from pathlib import Path -# Isolate state dir from production +# Isolate state dir from production — only affects the auth module reload. +# We deliberately do NOT delete api.config from sys.modules (unlike some +# sibling test files that need a fresh config import). Deleting api.config +# would change its module-level STATE_DIR global and leak into all +# subsequently collected tests (breaking test_pytest_state_isolation.py). import tempfile _TEST_STATE = Path(tempfile.mkdtemp()) os.environ["HERMES_WEBUI_STATE_DIR"] = str(_TEST_STATE) sys.path.insert(0, str(Path(__file__).parent.parent)) -# Ensure a clean module state -for mod in list(sys.modules.keys()): - if 'api.auth' in mod or 'api.config' in mod: - del sys.modules[mod] - -import api.auth as auth +# Force a fresh import of the auth module so it picks up the isolated env var. +# The auth module re-executes `from api.config import STATE_DIR, load_settings` +# at import time, but api.config is already in sys.modules — Python just +# rebinds the names from the existing module, keeping the conftest STATE_DIR +# untouched. +import api.auth +importlib.reload(api.auth) +auth = api.auth class TestPasswordHashCache(unittest.TestCase):