mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-28 16:32:28 +00:00
A per-file sweep (local suite runs were reading the PIPE's exit code, not npm's) surfaced four tests failing on HEAD: - hash-change-handler: logged-out default is deliberately TRENDING since 04621efc; test still expected most-recent-videos - session-actor / sessionActor / nsec-login: encoded the pre-fix "no-arg clearStoredSessionActor wipes EVERY account" behavior that 2026-07-02 deliberately removed (one account's logout must not destroy other saved accounts' keys). Replaced with stronger assertions: multi-account survival on no-arg clear, targeted forget, and the blocked-key security property asserted on the blocked account specifically. Integrity notes in each file; TODO #11b addendum documents the pipe lesson. Co-Authored-By: Claude Fable 5 <[email protected]>
118 lines
4.1 KiB
JavaScript
118 lines
4.1 KiB
JavaScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import * as sessionActor from "../../js/nostr/sessionActor.js";
|
|
import { indexedDB, IDBKeyRange } from "fake-indexeddb";
|
|
// Ensure localStorage is available
|
|
import "../test-helpers/setup-localstorage.mjs";
|
|
|
|
// Polyfill IndexedDB
|
|
globalThis.indexedDB = indexedDB;
|
|
globalThis.IDBKeyRange = IDBKeyRange;
|
|
|
|
test("SessionActor", async (t) => {
|
|
const passphrase = "correct-horse-battery-staple";
|
|
const privateKey = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
|
|
|
await t.test("encrypts and decrypts private key", async () => {
|
|
const encrypted = await sessionActor.encryptSessionPrivateKey(privateKey, passphrase);
|
|
|
|
assert.ok(encrypted.ciphertext);
|
|
assert.ok(encrypted.salt);
|
|
assert.ok(encrypted.iv);
|
|
assert.equal(encrypted.algorithm, "AES-GCM");
|
|
|
|
const payload = {
|
|
privateKeyEncrypted: encrypted.ciphertext,
|
|
encryption: {
|
|
salt: encrypted.salt,
|
|
iv: encrypted.iv,
|
|
iterations: encrypted.iterations,
|
|
hash: encrypted.hash,
|
|
version: encrypted.version,
|
|
algorithm: encrypted.algorithm,
|
|
}
|
|
};
|
|
|
|
const decrypted = await sessionActor.decryptSessionPrivateKey(payload, passphrase);
|
|
assert.equal(decrypted, privateKey);
|
|
});
|
|
|
|
await t.test("fails to decrypt with wrong passphrase", async () => {
|
|
const encrypted = await sessionActor.encryptSessionPrivateKey(privateKey, passphrase);
|
|
|
|
const payload = {
|
|
privateKeyEncrypted: encrypted.ciphertext,
|
|
encryption: {
|
|
salt: encrypted.salt,
|
|
iv: encrypted.iv,
|
|
}
|
|
};
|
|
|
|
try {
|
|
await sessionActor.decryptSessionPrivateKey(payload, "wrong-password");
|
|
assert.fail("Should have thrown error");
|
|
} catch (error) {
|
|
assert.match(error.message, /Failed to decrypt/);
|
|
}
|
|
});
|
|
|
|
await t.test("persists and reads session actor", async () => {
|
|
// Clean up first
|
|
localStorage.clear();
|
|
sessionActor.clearStoredSessionActor();
|
|
|
|
const actor = {
|
|
pubkey: "pubkey123",
|
|
privateKeyEncrypted: "encrypteddata",
|
|
encryption: {
|
|
salt: "salt",
|
|
iv: "iv",
|
|
iterations: 1000,
|
|
},
|
|
createdAt: Date.now(),
|
|
};
|
|
|
|
sessionActor.persistSessionActor(actor);
|
|
|
|
// Verify localStorage
|
|
const stored = JSON.parse(localStorage.getItem(sessionActor.SESSION_ACTOR_STORAGE_KEY));
|
|
assert.equal(stored.pubkey, actor.pubkey);
|
|
assert.equal(stored.privateKeyEncrypted, actor.privateKeyEncrypted);
|
|
|
|
// Verify read
|
|
const read = sessionActor.readStoredSessionActorEntry();
|
|
assert.ok(read);
|
|
assert.equal(read.pubkey, actor.pubkey);
|
|
assert.equal(read.privateKeyEncrypted, actor.privateKeyEncrypted);
|
|
assert.equal(read.encryption.salt, actor.encryption.salt);
|
|
});
|
|
|
|
// spec_correction: no-arg clearStoredSessionActor() deliberately no longer
|
|
// wipes the per-account map (that wiped EVERY saved account's key on one
|
|
// account's logout — fixed 2026-07-02, same class as the NIP-46 map wipe).
|
|
// Forgetting an account is the targeted clearStoredSessionActor(pubkey).
|
|
// See tests/nostr/sessionActor.test.mjs for the full integrity note and the
|
|
// multi-account survival scenario.
|
|
await t.test("clears stored session actor (targeted; no-arg drops only the default slot)", async () => {
|
|
const actor = {
|
|
pubkey: "pubkey123",
|
|
privateKeyEncrypted: "encrypteddata",
|
|
encryption: { salt: "salt", iv: "iv" }
|
|
};
|
|
sessionActor.persistSessionActor(actor);
|
|
|
|
// No-arg clear: default slot gone, but the account's key survives.
|
|
sessionActor.clearStoredSessionActor();
|
|
assert.equal(localStorage.getItem(sessionActor.SESSION_ACTOR_STORAGE_KEY), null);
|
|
assert.ok(
|
|
sessionActor.readStoredSessionActorEntry("pubkey123"),
|
|
"no-arg clear must not delete the account's stored key",
|
|
);
|
|
|
|
// Targeted clear actually forgets it.
|
|
sessionActor.clearStoredSessionActor("pubkey123");
|
|
assert.equal(sessionActor.readStoredSessionActorEntry("pubkey123"), null);
|
|
assert.equal(sessionActor.readStoredSessionActorEntry(), null);
|
|
});
|
|
});
|