37b22166bab5

scope auto-lock refresh to extension pages and secret-touching fill

time
2026-09-03 22:14 UTC · 1 month ago
machine
koh2
agent
claude-sonnet-4-6
session
PB-26-304 passive auto-lock refresh
parent
af7feb6fddaf
6 files manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/background/message-policy.ts+1 −1src/background/messages.autolock-refresh.test.ts+26 −5src/background/messages.ts+7 −2
manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22   "manifest_version": 3,33   "name": "Peach Password Manager",4-  "version": "5.0.71",4+  "version": "5.0.72",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.0.71",5+  "version": "5.0.72",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.0.71",3+  "version": "5.0.72",44   "description": "Zero-knowledge password manager",55   "license": "GPL-3.0-only",66   "type": "module",

view file

src/background/message-policy.ts+1 −1
@@ -118,7 +118,7 @@118118   ML_PREDICT_BATCH: "content-script-allowed",119119   OPEN_PRO_GATE: "content-script-allowed",120120   OPEN_POPUP: "content-script-allowed",121-  TOUCH_AUTO_LOCK: "content-script-allowed",121+  TOUCH_AUTO_LOCK: "extension-page-only",122122   MARK_PASSKEY_BANNER_SHOWN: "content-script-allowed",123123 124124   // These seven are the explicit Firefox MAIN-world passkey relay surface.

view file

src/background/messages.autolock-refresh.test.ts+26 −5
@@ -68,6 +68,14 @@6868     return (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mock.calls.length;6969   }7070 71+  it("content-script GET_SETTINGS does not reschedule the lock alarm", async () => {72+    (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();73+74+    await messages.handleMessage({ type: "GET_SETTINGS" }, pageSender);75+76+    expect(alarmCreations()).toBe(0);77+  });78+7179   it("repeated AUTOFILL_QUERY does not reschedule the lock alarm", async () => {7280     (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();7381 @@ -94,13 +102,23 @@94102     expect(alarmCreations()).toBe(0);95103   });96104 97-  // The control: this is not a blanket stop-refreshing. A user-initiated98-  // message still pushes the deadline out.99-  it("a user-initiated message still reschedules the lock alarm", async () => {105+  it("page TOUCH_AUTO_LOCK is rejected without rescheduling the lock alarm", async () => {100106     (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();101107 102-    await messages.handleMessage({ type: "TOUCH_AUTO_LOCK", payload: {} }, pageSender);108+    await expect(109+      messages.handleMessage({ type: "TOUCH_AUTO_LOCK", payload: {} }, pageSender),110+    ).resolves.toMatchObject({ ok: false, error: "invalid_sender" });111+    expect(alarmCreations()).toBe(0);112+  });103113 114+  it("an extension-page TOUCH_AUTO_LOCK reschedules the lock alarm", async () => {115+    (chrome.alarms.create as unknown as ReturnType<typeof vi.fn>).mockClear();116+117+    await messages.handleMessage(118+      { type: "TOUCH_AUTO_LOCK", payload: {} },119+      { url: "chrome-extension://test-extension-id/popup.html" },120+    );121+104122     expect(alarmCreations()).toBe(1);105123   });106124 @@ -115,7 +133,10 @@115133         pageSender,116134       );117135     }118-    await messages.handleMessage({ type: "TOUCH_AUTO_LOCK", payload: {} }, pageSender);136+    await messages.handleMessage(137+      { type: "TOUCH_AUTO_LOCK", payload: {} },138+      { url: "chrome-extension://test-extension-id/popup.html" },139+    );119140     for (let i = 0; i < 20; i += 1) {120141       await messages.handleMessage(121142         { type: "AUTOFILL_QUERY", payload: { domain: "example.com", kind: "login" } },

view file

src/background/messages.ts+7 −2
@@ -380,7 +380,7 @@380380       return { ok: false, error: "invalid_sender" };381381     }382382 383-    if (shouldRefreshAutoLock(msg.type) && vault.isUnlocked()) {383+    if (shouldRefreshAutoLock(msg.type, sender) && vault.isUnlocked()) {384384       touchAutoLock();385385     }386386 @@ -954,6 +954,7 @@954954             totpCode = undefined;955955           }956956         }957+        if (isPageSender(sender) && vault.isUnlocked()) touchAutoLock();957958         return { ok: true, data: { ...safeEntry, totpCode, totpRemaining } };958959       }959960       // Trusted full-entry fetch for extension-owned edit surfaces. Content@@ -1818,7 +1819,11 @@18181819   });18191820 }18201821 1821-function shouldRefreshAutoLock(type: MessageType): boolean {1822+function shouldRefreshAutoLock(1823+  type: MessageType,1824+  sender: Partial<chrome.runtime.MessageSender>,1825+): boolean {1826+  if (!isExtensionPageSender(sender)) return false;18221827   return (18231828     type !== "HAS_VAULT" &&18241829     type !== "IS_UNLOCKED" &&

view file