123import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";45const mockSendMessage = vi.fn().mockResolvedValue({ ok: true });67vi.stubGlobal("chrome", {8 runtime: {9 id: "ext-id",10 sendMessage: mockSendMessage,11 },12});1314beforeAll(async () => {15 await import("./passkeys-firefox-relay");16});1718async function fire(payload: unknown, source: Window | null = window) {19 window.dispatchEvent(20 new MessageEvent("message", {21 data: { type: "PEACH_PASSKEY_REQUEST", id: "x", payload },22 source,23 })24 );25 await new Promise((resolve) => setTimeout(resolve, 10));26}2728describe("Firefox passkey relay allowlist", () => {29 beforeEach(() => {30 mockSendMessage.mockClear();31 });3233 const allowed = [34 "PASSKEY_CREATE",35 "PASSKEY_GET",36 "PASSKEY_HAS_CREDENTIALS",37 "INITIATE_FIREFOX_PASSKEY_CREATE",38 "PASSKEY_OFFER_DECLINE",39 "GET_PASSKEY_OFFER_DECLINED",40 "GET_PASSKEY_FOR_DOMAIN",41 ];4243 const blocked = [44 45 "COMPLETE_FIREFOX_PASSKEY_CREATE",46 47 48 "PASSKEY_AUTH_RESPONSE",49 "GET_ENTRIES",50 "GET_RECOVERY_WORDS",51 "DESTROY_VAULT",52 "GET_DERIVED_KEY",53 "ADD_ENTRY",54 "UPDATE_ENTRY",55 "DELETE_ENTRY",56 "CLEAR_VAULT_ENTRIES",57 "RESTORE_FROM_CODEX",58 "CHANGE_PASSWORD",59 "RECOVER_WITH_WORDS",60 "SETUP_DURESS",61 "PAIRING_COMPLETED",62 "ACTIVATE_LICENSE",63 "TRIGGER_SYNC",64 "GET_IDENTITIES",65 "SEARCH_VAULT",66 "GET_ENTRY_FOR_FILL",67 "LOCAL_WEBSOCKET_PROBE",68 "SUBNET_DETECT_PROBE",69 "",70 "TOTALLY_MADE_UP",71 ];7273 for (const type of allowed) {74 it(`forwards allowed message: ${type}`, async () => {75 await fire({ type });76 expect(mockSendMessage).toHaveBeenCalledWith({ type });77 });78 }7980 for (const type of blocked) {81 it(`blocks message: ${type || "(empty)"}`, async () => {82 await fire({ type });83 expect(mockSendMessage).not.toHaveBeenCalled();84 });85 }8687 it("blocks null payload", async () => {88 await fire(null);89 expect(mockSendMessage).not.toHaveBeenCalled();90 });9192 it("blocks empty payload object", async () => {93 await fire({});94 expect(mockSendMessage).not.toHaveBeenCalled();95 });9697 it("blocks cross-frame messages", async () => {98 await fire({ type: "PASSKEY_GET" }, null);99 expect(mockSendMessage).not.toHaveBeenCalled();100 });101102 it("does not post a response for blocked messages", async () => {103 const spy = vi.spyOn(window, "postMessage");104 await fire({ type: "GET_ENTRIES" });105 expect(spy).not.toHaveBeenCalled();106 spy.mockRestore();107 });108});