518931773452
fix: recovery words hide on window blur in both the recovery display and Codex generation views — words are cleared on visibilitychange-hidden (Codex requires the password step again) so an OS screenshot of a backgrounded popup never captures them; bump 5.0.14→5.0.15
- time
- 2026-08-30 00:47 UTC · 1 month ago
- machine
- koh2
- agent
- glm-5.3-flash
- session
- pb-26 audit fixes
- parent
- 7c55294ece28
↓ peach-browser-51893177.tar.gzatlas at this save
files changed5 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/popup/views/CodexGenerateView.tsx+15 −0src/popup/views/RecoveryWordDisplay.tsx+29 −11manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach Password Manager",4- "version": "5.0.14",4+ "version": "5.0.15",55 "description": "Zero-knowledge password manager",66 "browser_specific_settings": {77 "gecko": {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.0.14",5+ "version": "5.0.15",66 "description": "Zero-knowledge password manager",77 "action": {88 "default_popup": "src/popup/index.html",package.json+1 −1
@@ -1,6 +1,6 @@11 {22 "name": "peach",3- "version": "5.0.14",3+ "version": "5.0.15",44 "description": "Zero-knowledge password manager",55 "license": "GPL-3.0-only",66 "type": "module",src/popup/views/CodexGenerateView.tsx+15 −0
@@ -94,6 +94,21 @@9494 await loadSnapshot();9595 });9696 97+ // Screenshot hygiene: drop the displayed words when the window is98+ // backgrounded so an OS screenshot of the hidden popup never captures99+ // them; re-showing requires the master-password step again.100+ const handleVisibilityChange = () => {101+ const snap = snapshot();102+ if (document.visibilityState === "hidden" && snap?.recoveryWords) {103+ setSnapshot({ ...snap, recoveryWords: null });104+ setNeedsPassword(true);105+ }106+ };107+ document.addEventListener("visibilitychange", handleVisibilityChange);108+ onCleanup(() =>109+ document.removeEventListener("visibilitychange", handleVisibilityChange),110+ );111+97112 async function loadSnapshot(password?: string): Promise<void> {98113 const res = (await sendMessage({99114 type: "GET_CODEX_VAULT_DATA",src/popup/views/RecoveryWordDisplay.tsx+29 −11
@@ -36,18 +36,8 @@3636 let countdownTimer: ReturnType<typeof setInterval> | null = null;3737 3838 onMount(async () => {39- // Fetch the recovery words from the background (vault must be unlocked).40- const res = (await sendMessage({41- type: "GET_RECOVERY_WORDS",42- })) as { ok: boolean; data?: string[]; error?: string };39+ await refreshWords();4340 44- if (res.ok && res.data) {45- setWords(res.data);46- } else {47- setError("could not retrieve recovery words");48- }49- setLoading(false);50-5141 // Start 5-second countdown5242 let remaining = 5;5343 countdownTimer = setInterval(() => {@@ -60,8 +50,36 @@6050 }, 1000);6151 });6252 53+ async function refreshWords(): Promise<void> {54+ // Fetch the recovery words from the background (vault must be unlocked).55+ const res = (await sendMessage({56+ type: "GET_RECOVERY_WORDS",57+ })) as { ok: boolean; data?: string[]; error?: string };58+59+ if (res.ok && res.data) {60+ setWords(res.data);61+ } else {62+ setError("could not retrieve recovery words");63+ }64+ setLoading(false);65+ }66+67+ // Screenshot hygiene: blank the words whenever the window is backgrounded,68+ // so an OS screenshot of the hidden popup never captures them. They are69+ // re-fetched when the user returns (the recovery-pending window still70+ // exempts the fetch from password re-auth).71+ const handleVisibilityChange = () => {72+ if (document.visibilityState === "hidden") {73+ setWords([]);74+ } else if (!confirmed()) {75+ void refreshWords();76+ }77+ };78+ document.addEventListener("visibilitychange", handleVisibilityChange);79+6380 onCleanup(() => {6481 if (countdownTimer) clearInterval(countdownTimer);82+ document.removeEventListener("visibilitychange", handleVisibilityChange);6583 });6684 6785 async function handleConfirm() {