a22c047efc1a
preserve Firefox conditional passkey request timing (v5.3.5)
- time
- 2026-10-08 15:51 UTC · 2 weeks ago
- machine
- Junes-MacBook-Pro.local
- agent
- gpt-6
- session
- repair firefox passkey sign-in
- parent
- 69251a167183
↓ peach-browser-a22c047e.tar.gzatlas at this save
files changed5 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/content/passkeys-firefox.test.ts+15 −1src/content/passkeys-firefox.ts+2 −1manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach Password Manager",4- "version": "5.3.4",4+ "version": "5.3.5",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.3.4",5+ "version": "5.3.5",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.3.4",3+ "version": "5.3.5",44 "description": "Zero-knowledge password manager",55 "license": "GPL-3.0-only",66 "type": "module",src/content/passkeys-firefox.test.ts+15 −1
@@ -134,6 +134,7 @@134134 // ── tests ──────────────────────────────────────────────────────────────────135135 136136 describe("PB-26-209: exportFunction privileged bridge", () => {137+ let nativeGet: ReturnType<typeof vi.fn>;137138 beforeEach(async () => {138139 vi.resetModules();139140 setupChromeMock();@@ -150,7 +151,7 @@150151 // jsdom doesn't have navigator.credentials by default — install a151152 // native stub that the bridge will replace via exportFunction.152153 const nativeCreate = vi.fn(async () => null);153- const nativeGet = vi.fn(async () => null);154+ nativeGet = vi.fn(async () => null);154155 Object.defineProperty(navigator, "credentials", {155156 value: { create: nativeCreate, get: nativeGet },156157 configurable: true,@@ -328,6 +329,19 @@328329 });329330 330331 describe("valid responses produce credentials", () => {332+ it("get: leaves conditional requests and their cancellation signal with Firefox", async () => {333+ const controller = new AbortController();334+ const options: CredentialRequestOptions = {335+ ...makeGetOptions(), mediation: "conditional", signal: controller.signal,336+ };337+ mockSendMessage338+ .mockResolvedValueOnce({ ok: true, hasCredentials: true })339+ .mockResolvedValueOnce({ ok: true, credential: validGetCredential() });340+ await expect(navigator.credentials.get(options)).resolves.toBeNull();341+ expect(nativeGet).toHaveBeenCalledWith(options);342+ expect(mockSendMessage).not.toHaveBeenCalled();343+ });344+331345 it.each([332346 "new Uint8Array([1, 2, 3, 4]).buffer",333347 "new Uint8Array(new Uint8Array([9, 1, 2, 3, 4, 9]).buffer, 1, 4)",src/content/passkeys-firefox.ts+2 −1
@@ -162,7 +162,8 @@162162 exportFunction(163163 function patchedGet(options?: CredentialRequestOptions): Promise<Credential | null> {164164 return pagePromise(async () => {165- if (!options?.publicKey) return originalGet(options);165+ // Conditional autofill must remain pending under Firefox's UI and cancellation rules.166+ if (!options?.publicKey || options.mediation === "conditional") return originalGet(options);166167 167168 try {168169 const publicKey = options.publicKey;