@openparachute/hub 0.7.4-rc.13 → 0.7.4-rc.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -527,7 +527,13 @@ describe("admin-lock management API", () => {
|
|
|
527
527
|
const r2 = await handleAdminLock(lockReq("/heartbeat", cookie, {}), "/heartbeat", {
|
|
528
528
|
db: harness.db,
|
|
529
529
|
});
|
|
530
|
-
|
|
530
|
+
const body2 = (await r2.json()) as { locked: boolean; idle_seconds?: number };
|
|
531
|
+
expect(body2.locked).toBe(false);
|
|
532
|
+
// The heartbeat MUST carry idle_seconds — the client re-anchors its local
|
|
533
|
+
// idle timer from it on every heartbeat. Omitting it poisoned the timer
|
|
534
|
+
// (undefined → NaN → instant re-lock). Regression guard for the PIN
|
|
535
|
+
// re-prompt loop.
|
|
536
|
+
expect(body2.idle_seconds).toBe(getIdleSeconds(harness.db));
|
|
531
537
|
});
|
|
532
538
|
|
|
533
539
|
test("unlock brute-force limiter: 6th attempt is 429", async () => {
|
package/src/api-admin-lock.ts
CHANGED
|
@@ -159,8 +159,15 @@ export async function handleAdminLock(
|
|
|
159
159
|
case "/heartbeat":
|
|
160
160
|
// Slide the idle window forward if (and only if) currently unlocked.
|
|
161
161
|
refreshActivity(gate.sessionId, getIdleSeconds(db), now().getTime());
|
|
162
|
+
// `idle_seconds` is part of the response so the heartbeat fulfills the
|
|
163
|
+
// same `AdminLockStatus` shape as GET status — the client re-anchors its
|
|
164
|
+
// local idle timer from it on every heartbeat, so it MUST be present.
|
|
165
|
+
// Omitting it poisoned the client timer with `undefined` (→ NaN → instant
|
|
166
|
+
// re-lock), the bug this fixes. It also lets a live session pick up an
|
|
167
|
+
// idle-window change the operator made in Settings mid-session.
|
|
162
168
|
return json(200, {
|
|
163
169
|
locked: isLockConfigured(db) && !isSessionUnlocked(gate.sessionId, now().getTime()),
|
|
170
|
+
idle_seconds: getIdleSeconds(db),
|
|
164
171
|
unlock_seconds_remaining: unlockSecondsRemaining(gate.sessionId, now().getTime()),
|
|
165
172
|
});
|
|
166
173
|
default:
|