From 6366ca8094a087d9a081f76834be8b82e72fe58d Mon Sep 17 00:00:00 2001 From: matcha Date: Sun, 4 Oct 2026 02:46:45 +0000 Subject: [PATCH] Consolidate Persimmons source --- extension/tests/worker.test.mjs | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 extension/tests/worker.test.mjs diff --git a/extension/tests/worker.test.mjs b/extension/tests/worker.test.mjs new file mode 100644 index 0000000..3449f77 --- /dev/null +++ b/extension/tests/worker.test.mjs @@ -0,0 +1,60 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import {AUTO_LOCK} from '../vault-session.mjs'; + +test('worker keys stay private; trusted popup only; close, timer, auth loss and lock races fail closed', async () => { + let connect, removed, valid = true, grant = true; + const stored = {}, timers = []; + globalThis.setInterval = () => 1; globalThis.clearInterval = () => {}; + globalThis.setTimeout = callback => { timers.push(callback); return timers.length; }; + globalThis.clearTimeout = () => {}; + globalThis.chrome = { + runtime: {id:'fixture', getURL:path=>'chrome-extension://fixture/'+path, + getPlatformInfo:async()=>({}), onConnect:{addListener:fn=>connect=fn}}, + permissions:{contains:async()=>grant,onRemoved:{addListener:fn=>removed=fn}}, + storage:{local:{get:async key=>({[key]:stored[key]}),set:async data=>Object.assign(stored,data)}} + }; + globalThis.fetch = async()=>new Response(JSON.stringify({owner:valid?'owner':'other',items:[]}),{headers:{'content-type':'application/json'}}); + const {vaultUnlocked} = await import('../vault-worker.mjs'); + function popup(url='chrome-extension://fixture/popup.html') { + let listener, close, disconnected = false, sequence = 0; + const replies = new Map(); + connect({name:'human-vault',sender:{id:'fixture',url},disconnect:()=>disconnected=true, + onMessage:{addListener:fn=>listener=fn},onDisconnect:{addListener:fn=>close=fn}, + postMessage:message=>{replies.get(message.id)?.(message);replies.delete(message.id)}}); + return {get disconnected(){return disconnected}, close:()=>close(), + send:(op,data={})=>new Promise(resolve=>{const id=++sequence;replies.set(id,resolve);listener({id,op,owner:'owner',...data})})}; + } + assert.equal(popup('https://persimmons.app/').disconnected,true); + assert.equal(popup('chrome-extension://fixture/other.html').disconnected,true); + let port = popup(); + const unlock = () => port.send('unlock',{passphrase:'synthetic test passphrase'}); + const firstUnlock = unlock(); + await new Promise(resolve=>setImmediate(resolve)); + assert.equal(vaultUnlocked('owner'),false,'status during unlock does not cancel it'); + assert.equal((await firstUnlock).result,true); + assert.equal(vaultUnlocked('owner'),true); + assert.equal(vaultUnlocked('other'),false); + assert.equal(vaultUnlocked('owner'),true,'another owner status cannot lock the session'); + const record=stored['human-vault:https://persimmons.app:owner']; + assert.ok(record.wrappedKey); assert.equal(Object.values(stored).some(x=>x instanceof CryptoKey),false); + assert.doesNotMatch(JSON.stringify(stored),/synthetic test passphrase/); + port.close(); port=popup();assert.equal((await port.send('status')).result,false); + stored[AUTO_LOCK]='5';assert.equal((await unlock()).result,true); + port.close();port=popup();assert.equal((await port.send('status')).result,true); + const encrypted=await port.send('encrypt',{origin:'https://wp.phly.cloud',account:{username:'fixture',password:'synthetic'}}); + assert.equal(encrypted.ok,true);assert.ok(encrypted.result.ciphertext); + assert.deepEqual((await port.send('decrypt',{item:encrypted.result})).result,{username:'fixture',password:'synthetic'}); + timers.at(-1)();assert.equal((await port.send('status')).result,false); + assert.equal((await unlock()).result,true); + valid=false;assert.equal((await port.send('status')).ok,false);valid=true; + assert.equal((await port.send('status')).result,false); + assert.equal((await unlock()).result,true);removed();assert.equal((await port.send('status')).result,false); + grant=false;assert.equal((await unlock()).ok,false);grant=true; + assert.equal((await port.send('unlock',{passphrase:'wrong'})).ok,false); + const pending=unlock();await port.send('lock');assert.equal((await pending).ok,false); + // Close invalidates any pending unlock and never installs its key. + unlock();port.close();port=popup(); + await new Promise(resolve=>setImmediate(resolve)); + assert.equal((await port.send('status')).result,false); +});