66b318659107
fix: DESTROY_VAULT preserves peach_license and peach_pro_celebrated — Pro survives vault reset; drop unused beforeEach import unblocking typecheck (v3.6.110)
- time
- 2026-05-28 20:05 UTC · 5 months ago
- machine
- koh2
- parent
- 49d919333ee0
↓ peach-browser-66b31865.tar.gzatlas at this save
files changed6 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/background/messages.test.ts+34 −0src/background/messages.ts+15 −0src/shared/browser.test.ts+1 −1manifest.firefox.json+1 −1
@@ -2,7 +2,7 @@22 "_comment_audio_capture": "Firefox does not honor the Chrome 'audioCapture' optional_permission — getUserMedia in WebExtensions is gated solely by the runtime permission prompt. Microphone access for chirp pairing works from the popup under the existing '<all_urls>' host permission; no manifest entry is required or accepted here.",33 "manifest_version": 3,44 "name": "Peach Password Manager",5- "version": "3.6.109",5+ "version": "3.6.110",66 "description": "Zero-knowledge password manager",77 "browser_specific_settings": {88 "gecko": {manifest.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach Password Manager",4- "version": "3.6.109",4+ "version": "3.6.110",55 "description": "Zero-knowledge password manager",66 "action": {77 "default_popup": "src/popup/index.html",package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "peach",3- "version": "3.6.109",3+ "version": "3.6.110",44 "description": "Zero-knowledge password manager",55 "type": "module",66 "scripts": {src/background/messages.test.ts+34 −0
@@ -75,6 +75,9 @@7575 delete storage[key];7676 }7777 }),78+ clear: vi.fn(async () => {79+ for (const key of Object.keys(storage)) delete storage[key];80+ }),7881 },7982 session: {8083 get: vi.fn(async (key?: string | string[]) => {@@ -766,5 +769,36 @@766769 payload: { category: "Bug report", message: "hi", version: "2.1.0", browser: "Chrome" },767770 });768771 expect(alarmsCreate).not.toHaveBeenCalled();772+ });773+774+ it("DESTROY_VAULT preserves the Pro license and celebration flag", async () => {775+ const vault = await import("./vault");776+ await vault.createVault("correct-pass-12345");777+ await vault.addEntry({ title: "Gmail", username: "june", password: "s", url: "https://mail.google.com" });778+779+ // Simulate an activated Pro license + already-celebrated marker.780+ storage.peach_license = "PEACH-PRO-TEST-KEY";781+ storage.peach_pro_celebrated = true;782+783+ const res = await dispatch({ type: "DESTROY_VAULT" });784+ expect(res).toMatchObject({ ok: true });785+786+ // Vault data is gone…787+ expect(storage.peach_vault_a).toBeUndefined();788+ // …but the paid license and celebration flag survive the wipe.789+ expect(storage.peach_license).toBe("PEACH-PRO-TEST-KEY");790+ expect(storage.peach_pro_celebrated).toBe(true);791+ });792+793+ it("DESTROY_VAULT with no license clears storage without resurrecting keys", async () => {794+ const vault = await import("./vault");795+ await vault.createVault("correct-pass-12345");796+797+ const res = await dispatch({ type: "DESTROY_VAULT" });798+ expect(res).toMatchObject({ ok: true });799+800+ expect(storage.peach_vault_a).toBeUndefined();801+ expect(storage.peach_license).toBeUndefined();802+ expect(storage.peach_pro_celebrated).toBeUndefined();769803 });770804 });src/background/messages.ts+15 −0
@@ -950,9 +950,24 @@950950 // onboarded flag, history, sync state, declined cards, recall, etc.);951951 // session.clear() wipes pending passkey requests + pro-notice queues.952952 // Caller (Panic.tsx) handles UI navigation back to onboarding.953+ //954+ // The Pro license is the one thing that must survive: the user paid for955+ // it, and destroying a vault (panic wipe, fresh start) is not a refund.956+ // Capture it before the clear and restore it after. peach_pro_celebrated957+ // rides along so the Pro celebration doesn't replay on the next unlock.958+ const preserved = await chrome.storage.local.get([959+ "peach_license",960+ "peach_pro_celebrated",961+ ]);953962 vault.lock();954963 await storageClear();955964 await vault.clearVaultHistory();965+ const toRestore: Record<string, unknown> = {};966+ if (preserved.peach_license !== undefined) toRestore.peach_license = preserved.peach_license;967+ if (preserved.peach_pro_celebrated !== undefined) toRestore.peach_pro_celebrated = preserved.peach_pro_celebrated;968+ if (Object.keys(toRestore).length > 0) {969+ await chrome.storage.local.set(toRestore);970+ }956971 if (chrome.storage.session) {957972 await chrome.storage.session.clear();958973 }src/shared/browser.test.ts+1 −1
@@ -9,7 +9,7 @@99 * surfaces match the upstream browser.1010 */1111 12-import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";12+import { afterEach, describe, expect, it, vi } from "vitest";1313 1414 import { detectBrowser, firefoxVersion } from "./browser";1515