[object Object]

← back to Ticket System

harden lockout prune: keep active in-window fail-records, prune only expired/stale (code-review W2, session close)

e7768c09527d6728e0e964ad981b94faea8e60b4 · 2026-08-25 14:45:22 -0700 · Steve

Files touched

Diff

commit e7768c09527d6728e0e964ad981b94faea8e60b4
Author: Steve <steve@designerwallcoverings.com>
Date:   Tue Aug 25 14:45:22 2026 -0700

    harden lockout prune: keep active in-window fail-records, prune only expired/stale (code-review W2, session close)
---
 server.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/server.js b/server.js
index a6c5ae9a..c4544e46 100644
--- a/server.js
+++ b/server.js
@@ -91,7 +91,9 @@ function noteAuth(req, ok) {
   rec.count++;
   if (rec.count >= FAIL_MAX) rec.until = now + LOCK_MS;
   authFails.set(ip, rec);
-  if (authFails.size > 5000) for (const [k, v] of authFails) if (!v.until || now > v.until) authFails.delete(k); // prune
+  // prune only truly-inactive records: expired locks OR window-stale (count would reset anyway).
+  // NOT active in-window records (until==0, count<MAX) — deleting those resets a live attacker's count.
+  if (authFails.size > 5000) for (const [k, v] of authFails) if ((v.until && now > v.until) || (now - v.first) > FAIL_WINDOW_MS) authFails.delete(k);
 }
 // Claude-spawning endpoints (/api/run, /api/dtd) must run from a LOCAL operator only.
 // Over the public `tickets` tunnel these would let a remote actor (past the shared cred)

← 4f405809 Loopback-gate /api/run + /api/dtd: block Claude-spawning end  ·  back to Ticket System  ·  auto-data-snapshot: 2026-08-25T18:53:12 (1 data files) — tmp aa4e0d5c →