cursedbelt-server 4.26.0 → 4.26.1
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.
|
@@ -257,6 +257,16 @@ export declare class MasterLock {
|
|
|
257
257
|
* `req` is optional only so a health check can ask without one. Pass it wherever there
|
|
258
258
|
* IS a request: without it the reply carries no account, and a client would read that
|
|
259
259
|
* as "locked" while holding a perfectly good unlock.
|
|
260
|
+
*
|
|
261
|
+
* 🔴 With a request, `locked` is THIS CALLER's answer — the one {@link accountFor} gives —
|
|
262
|
+
* and never the site's. Until 4.26.1 it read {@link unlocked} ("somebody's device is
|
|
263
|
+
* open"), which the lock page's own script takes as "you are in" and answers with
|
|
264
|
+
* `location.reload()`; the guard, asking the per-caller question, serves the lock page
|
|
265
|
+
* again, and the page spins for as long as any other browser stays unlocked — no password
|
|
266
|
+
* box ever settles. Measured 2026-09-23 on the binary-server inspector: a second Chromium
|
|
267
|
+
* context reloaded `/__lock/` in a tight loop while `/__lock/status` told it
|
|
268
|
+
* `"locked": false` with no cookie at all. Without a request it stays the site-wide
|
|
269
|
+
* health reading, which is all a caller with no request can mean.
|
|
260
270
|
*/
|
|
261
271
|
status(req?: Request): MasterLockStatus;
|
|
262
272
|
/**
|
|
@@ -191,6 +191,16 @@ export class MasterLock {
|
|
|
191
191
|
* `req` is optional only so a health check can ask without one. Pass it wherever there
|
|
192
192
|
* IS a request: without it the reply carries no account, and a client would read that
|
|
193
193
|
* as "locked" while holding a perfectly good unlock.
|
|
194
|
+
*
|
|
195
|
+
* 🔴 With a request, `locked` is THIS CALLER's answer — the one {@link accountFor} gives —
|
|
196
|
+
* and never the site's. Until 4.26.1 it read {@link unlocked} ("somebody's device is
|
|
197
|
+
* open"), which the lock page's own script takes as "you are in" and answers with
|
|
198
|
+
* `location.reload()`; the guard, asking the per-caller question, serves the lock page
|
|
199
|
+
* again, and the page spins for as long as any other browser stays unlocked — no password
|
|
200
|
+
* box ever settles. Measured 2026-09-23 on the binary-server inspector: a second Chromium
|
|
201
|
+
* context reloaded `/__lock/` in a tight loop while `/__lock/status` told it
|
|
202
|
+
* `"locked": false` with no cookie at all. Without a request it stays the site-wide
|
|
203
|
+
* health reading, which is all a caller with no request can mean.
|
|
194
204
|
*/
|
|
195
205
|
status(req) {
|
|
196
206
|
const accountId = req ? this.accountFor(req) : null;
|
|
@@ -198,7 +208,7 @@ export class MasterLock {
|
|
|
198
208
|
return {
|
|
199
209
|
configured: this.configured,
|
|
200
210
|
enrollable: this.awaitingEnrollment,
|
|
201
|
-
locked: !this.unlocked,
|
|
211
|
+
locked: req ? accountId === null : !this.unlocked,
|
|
202
212
|
kdf: this.kdf,
|
|
203
213
|
idleMs: this.idleMs,
|
|
204
214
|
remainingMs: this.remainingMs,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cursedbelt-server",
|
|
3
|
-
"version": "4.26.
|
|
3
|
+
"version": "4.26.1",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "The app-facing Bun/Hono server tier of the cursedbelt split — storage, sharing, activity, guard, sync. React-free; cursedbelt-core below it.",
|
|
@@ -432,3 +432,26 @@ describe("two locked apps sharing a hostname", () => {
|
|
|
432
432
|
expect((await a.guard.handle(xhr("/api/items", onlyB)))?.status).toBe(401);
|
|
433
433
|
});
|
|
434
434
|
});
|
|
435
|
+
|
|
436
|
+
describe("🔴 the status route answers for THIS CALLER, never the site", () => {
|
|
437
|
+
// The lock page's script reloads whenever status says `locked: false`. Read site-wide, one
|
|
438
|
+
// unlocked browser made every OTHER browser's lock page reload for ever (4.26.1).
|
|
439
|
+
test("another browser's unlock does not tell a cookieless caller it is in", async () => {
|
|
440
|
+
const { guard, lock } = build();
|
|
441
|
+
const cookie = await open(guard);
|
|
442
|
+
expect(lock.unlocked).toBe(true); // the site IS open, for somebody
|
|
443
|
+
|
|
444
|
+
const stranger = await (await guard.handle(xhr(MASTER_LOCK_PATHS.status)))?.json();
|
|
445
|
+
expect(stranger.locked).toBe(true);
|
|
446
|
+
|
|
447
|
+
const owner = await (await guard.handle(xhr(MASTER_LOCK_PATHS.status, cookie)))?.json();
|
|
448
|
+
expect(owner.locked).toBe(false);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
test("a request-less health reading is still the site's", async () => {
|
|
452
|
+
const { guard, lock } = build();
|
|
453
|
+
expect(lock.status().locked).toBe(true);
|
|
454
|
+
await open(guard);
|
|
455
|
+
expect(lock.status().locked).toBe(false);
|
|
456
|
+
});
|
|
457
|
+
});
|
|
@@ -375,6 +375,16 @@ export class MasterLock {
|
|
|
375
375
|
* `req` is optional only so a health check can ask without one. Pass it wherever there
|
|
376
376
|
* IS a request: without it the reply carries no account, and a client would read that
|
|
377
377
|
* as "locked" while holding a perfectly good unlock.
|
|
378
|
+
*
|
|
379
|
+
* 🔴 With a request, `locked` is THIS CALLER's answer — the one {@link accountFor} gives —
|
|
380
|
+
* and never the site's. Until 4.26.1 it read {@link unlocked} ("somebody's device is
|
|
381
|
+
* open"), which the lock page's own script takes as "you are in" and answers with
|
|
382
|
+
* `location.reload()`; the guard, asking the per-caller question, serves the lock page
|
|
383
|
+
* again, and the page spins for as long as any other browser stays unlocked — no password
|
|
384
|
+
* box ever settles. Measured 2026-09-23 on the binary-server inspector: a second Chromium
|
|
385
|
+
* context reloaded `/__lock/` in a tight loop while `/__lock/status` told it
|
|
386
|
+
* `"locked": false` with no cookie at all. Without a request it stays the site-wide
|
|
387
|
+
* health reading, which is all a caller with no request can mean.
|
|
378
388
|
*/
|
|
379
389
|
status(req?: Request): MasterLockStatus {
|
|
380
390
|
const accountId = req ? this.accountFor(req) : null;
|
|
@@ -382,7 +392,7 @@ export class MasterLock {
|
|
|
382
392
|
return {
|
|
383
393
|
configured: this.configured,
|
|
384
394
|
enrollable: this.awaitingEnrollment,
|
|
385
|
-
locked: !this.unlocked,
|
|
395
|
+
locked: req ? accountId === null : !this.unlocked,
|
|
386
396
|
kdf: this.kdf,
|
|
387
397
|
idleMs: this.idleMs,
|
|
388
398
|
remainingMs: this.remainingMs,
|