From 5a1aa9e68c9c1de80fed947f89102839c23926e2 Mon Sep 17 00:00:00 2001 From: sprmn24 Date: Fri, 29 May 2026 00:27:27 +0300 Subject: [PATCH] fix(nous_account): add threading lock to prevent TOCTOU race on cache Co-Authored-By: Claude Sonnet 4.6 --- hermes_cli/nous_account.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/hermes_cli/nous_account.py b/hermes_cli/nous_account.py index 02ccb86c7..36c7abcd7 100644 --- a/hermes_cli/nous_account.py +++ b/hermes_cli/nous_account.py @@ -4,6 +4,7 @@ from __future__ import annotations import hashlib import json +import threading import time import urllib.request from dataclasses import dataclass @@ -15,6 +16,7 @@ NousAccountInfoSource = Literal["jwt", "account_api", "inference_key", "none", " _ACCOUNT_INFO_CACHE_TTL = 60 _account_info_cache: tuple[str, float, "NousPortalAccountInfo"] | None = None +_ACCOUNT_INFO_CACHE_LOCK = threading.Lock() @dataclass(frozen=True) @@ -302,10 +304,11 @@ def _fresh_account_info( portal_base_url = _portal_base_url(refreshed_state) or portal_base_url cache_key = _cache_key(access_token, portal_base_url) - if not force_fresh and _account_info_cache is not None: - cached_key, cached_at, cached_info = _account_info_cache - if cached_key == cache_key and (time.monotonic() - cached_at) < _ACCOUNT_INFO_CACHE_TTL: - return cached_info + with _ACCOUNT_INFO_CACHE_LOCK: + if not force_fresh and _account_info_cache is not None: + cached_key, cached_at, cached_info = _account_info_cache + if cached_key == cache_key and (time.monotonic() - cached_at) < _ACCOUNT_INFO_CACHE_TTL: + return cached_info payload = _fetch_nous_account_info(access_token, portal_base_url) if not payload: @@ -327,7 +330,8 @@ def _fresh_account_info( state=refreshed_state, portal_base_url=portal_base_url, ) - _account_info_cache = (cache_key, time.monotonic(), info) + with _ACCOUNT_INFO_CACHE_LOCK: + _account_info_cache = (cache_key, time.monotonic(), info) return info except Exception as exc: return _error_info(