mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-08 03:01:47 +00:00
PairingStore.approve_code() didn't consult _is_locked_out(), so after MAX_FAILED_ATTEMPTS bad approvals the lockout flag was set but a valid code still got accepted — any pending code (legitimately issued or attacker-obtained) could be approved during the 1-hour lockout window, nullifying the brute-force protection. - gateway/pairing.py: lockout check runs in approve_code() right after _cleanup_expired, before the pending lookup. Returns None on lockout. - tests/gateway/test_pairing.py: test_lockout_blocks_code_approval pins the regression — reporter's exact reproducer (generate valid code, exhaust attempts with WRONGCODE, try to approve valid code) must return None and leave is_approved == False. Also pins recovery: once lockout expires, the still-pending code approves normally. - hermes_cli/pairing.py: _cmd_approve distinguishes the two None cases. On lockout, prints 'Platform locked out... clears in N minutes. To reset sooner, delete the _lockout:<platform> entry from _rate_limits.json' instead of the misleading 'Code not found or expired' message. 29/29 pairing tests pass; E2E-verified with reporter's exact Python reproducer.
This commit is contained in:
parent
1baab8771a
commit
145e8ec237
3 changed files with 66 additions and 1 deletions
|
|
@ -195,12 +195,23 @@ class PairingStore:
|
|||
"""
|
||||
Approve a pairing code. Adds the user to the approved list.
|
||||
|
||||
Returns {user_id, user_name} on success, None if code is invalid/expired.
|
||||
Returns {user_id, user_name} on success, None if code is
|
||||
invalid/expired OR the platform is currently locked out after
|
||||
``MAX_FAILED_ATTEMPTS`` failed approvals (#10195). Callers can
|
||||
disambiguate with ``_is_locked_out(platform)``.
|
||||
"""
|
||||
with self._lock:
|
||||
self._cleanup_expired(platform)
|
||||
code = code.upper().strip()
|
||||
|
||||
# Lockout check — must run before the pending lookup so a
|
||||
# valid code (e.g. one already sitting in pending) cannot be
|
||||
# accepted once the lockout fires. Without this, the lockout
|
||||
# only blocks `generate_code`, not `approve_code` — nullifying
|
||||
# the brute-force protection for any code already issued.
|
||||
if self._is_locked_out(platform):
|
||||
return None
|
||||
|
||||
pending = self._load_json(self._pending_path(platform))
|
||||
if code not in pending:
|
||||
self._record_failed_attempt(platform)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue