a7728642a731

complete locked-vault Sync reset flow (v5.2.48)

time
2026-09-11 17:21 UTC · 1 month ago
machine
koh2
agent
camel-stream/auto
session
fix secure reset completion
parent
9aec7adfb69c
6 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/background/messages.test.ts+27 −0src/background/messages.ts+15 −3src/shared/release-notes.ts+3 −3
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach Password Manager",4-  "version": "5.2.47",4+  "version": "5.2.48",55   "description": "Zero-knowledge password manager",66   "browser_specific_settings": {77     "gecko": {

view file

manifest.json+1 −1
@@ -2,7 +2,7 @@22   "manifest_version": 3,33   "minimum_chrome_version": "116",44   "name": "Peach Password Manager",5-  "version": "5.2.47",5+  "version": "5.2.48",66   "description": "Zero-knowledge password manager",77   "action": {88     "default_popup": "src/popup/index.html",

view file

package.json+1 −1
@@ -1,6 +1,6 @@11 {22   "name": "peach",3-  "version": "5.2.47",3+  "version": "5.2.48",44   "description": "Zero-knowledge password manager",55   "license": "GPL-3.0-only",66   "type": "module",

view file

src/background/messages.test.ts+27 −0
@@ -1831,6 +1831,33 @@18311831     expect(response).toMatchObject({ ok: true, data: { state: "V2_ANCHOR" } });18321832     expect(await identity.getOrCreateDeviceId()).not.toBe(previousDeviceId);18331833   });1834+  it("SECURE_RESET unlocks a locked real vault before rotating identity", async () => {1835+    const vault = await import("./vault");1836+    await vault.createVault("correct-pass-12345");1837+    const identity = await import("./device-identity");1838+    const previousDeviceId = await identity.getOrCreateDeviceId();1839+    const { upsertPairedDevice } = await import("./paired-devices");1840+    await upsertPairedDevice({1841+      deviceId: "phone-1",1842+      deviceName: "June's Phone",1843+      deviceType: "android",1844+      longTermPublicKeyHex: "a".repeat(64),1845+      signingPublicKeyHex: "b".repeat(64),1846+      pairedAt: 1_700_000_000_000,1847+      trustSource: "direct",1848+    });1849+    await vault.lock();1850+    expect(vault.isUnlocked()).toBe(false);1851+1852+    const response = await dispatch({1853+      type: "SECURE_RESET",1854+      payload: { password: "correct-pass-12345" },1855+    });1856+1857+    expect(response).toMatchObject({ ok: true, data: { state: "V2_ANCHOR" } });1858+    expect(vault.isUnlocked()).toBe(true);1859+    expect(await identity.getOrCreateDeviceId()).not.toBe(previousDeviceId);1860+  });18341861 18351862   // The old "fresh direct re-pair clears the old revocation" test dispatched18361863   // COMPLETE_PAIRING, a handler deleted on 2026-08-04 (no legitimate senders;

view file

src/background/messages.ts+15 −3
@@ -1808,11 +1808,23 @@18081808         if (typeof payload.password !== "string" || payload.password.length === 0) {18091809           return { ok: false, error: "Enter your master password" };18101810         }1811-        if (!(await vault.verifyMainPassword(payload.password))) {1811+        const wasLocked = !vault.isUnlocked();1812+        const passwordValid = wasLocked1813+          ? await vault.unlock(payload.password)1814+          : await vault.verifyMainPassword(payload.password);1815+        if (!passwordValid) {18121816           return { ok: false, error: "Incorrect master password" };18131817         }1814-        // Enter the blocking state first: a crash between these two calls then1815-        // leaves the device refusing sync rather than mid-reset over live1818+        if (wasLocked) {1819+          await setupAutoLock();1820+          broadcastToContentScripts({ type: "VAULT_UNLOCKED" });1821+          onVaultUnlocked();1822+        }1823+        // Identity rotation needs the live vault root key. A password check is1824+        // deliberately verification-only, so establish the real session first1825+        // when Settings is opened after the vault session expired.1826+        // Enter the blocking state before the reset steps: a crash after this1827+        // point leaves the device refusing sync rather than mid-reset over live18161828         // trust. Both are idempotent, so a retry is safe.18171829         await enterResetRequired(browserGenerationIo);18181830         const result = await confirmSecureReset(browserGenerationIo, {

view file

src/shared/release-notes.ts+3 −3
@@ -21,11 +21,11 @@2121 2222 export const RELEASES: PeachRelease[] = [2323   {24-    version: "5.2.47",24+    version: "5.2.48",2525     releasedAt: "2026-09-11",2626     notes: [{27-      heading: "Repair Sync reset and peer removal",28-      body: "Sync reset now creates the new device identity, and removing a peer still works when the old identity needs migration.",27+      heading: "Complete locked-vault Sync resets",28+      body: "Sync reset now unlocks the real vault before rotating its local identity when Settings is opened after the vault session expired.",2929     }],3030   },3131   {

view file