759ec348c2cb
test: align sender fixtures with fail-closed URL policy (PB-26-115, v5.0.35)
- time
- 2026-09-01 20:48 UTC · 1 month ago
- machine
- koh2
- agent
- gpt-5
- session
- 2026-09-01 security assessment remediation
- parent
- 74c52ffb68e6
↓ peach-browser-759ec348.tar.gzatlas at this save
files changed9 files
manifest.firefox.json+1 −1manifest.json+1 −1package.json+1 −1src/background/messages.adversarial.test.ts+10 −7src/background/messages.test.ts+19 −10src/background/notes.test.ts+5 −1src/background/recovery-words.test.ts+5 −1src/background/recovery.adversarial.test.ts+5 −1src/background/vault.adversarial.test.ts+5 −1manifest.firefox.json+1 −1
@@ -1,7 +1,7 @@11 {22 "manifest_version": 3,33 "name": "Peach Password Manager",4- "version": "5.0.34",4+ "version": "5.0.35",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.34",5+ "version": "5.0.35",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.34",3+ "version": "5.0.35",44 "description": "Zero-knowledge password manager",55 "license": "GPL-3.0-only",66 "type": "module",src/background/messages.adversarial.test.ts+10 −7
@@ -158,7 +158,10 @@158158 159159 async function dispatch(160160 message: Message,161- sender: chrome.runtime.MessageSender = { id: "peach-extension" } as chrome.runtime.MessageSender,161+ sender: chrome.runtime.MessageSender = {162+ id: "peach-extension",163+ url: "chrome-extension://peach-extension/popup.html",164+ } as chrome.runtime.MessageSender,162165 ): Promise<MessageResponse> {163166 if (!listener) throw new Error("listener not registered");164167 return new Promise<MessageResponse>((resolve) => {@@ -345,7 +348,7 @@345348 expect(r.error).toBe("invalid_sender");346349 });347350 348- it("sender with no id (extension UI) is accepted", async () => {351+ it("sender with no URL is rejected", async () => {349352 if (!listener) throw new Error("listener not registered");350353 await setupUnlockedVault();351354 const r = await new Promise<MessageResponse>((resolve) => {@@ -355,7 +358,7 @@355358 resolve356359 );357360 });358- expect(r.ok).toBe(true);361+ expect(r).toMatchObject({ ok: false, error: "invalid_sender" });359362 });360363 });361364 @@ -569,10 +572,10 @@569572 url: "https://github.com",570573 type: "login",571574 });572- const r = await dispatch({573- type: "GET_ENTRY_FOR_FILL",574- payload: { id: e.id },575- });575+ const r = await dispatch(576+ { type: "GET_ENTRY_FOR_FILL", payload: { id: e.id } },577+ { id: "peach-extension", url: "https://github.com/login" },578+ );576579 expect(r.ok).toBe(true);577580 const data = (r as { data: Record<string, unknown> }).data;578581 expect("passkey" in data).toBe(false);src/background/messages.test.ts+19 −10
@@ -135,7 +135,10 @@135135 136136 async function dispatch(137137 message: Message,138- sender: chrome.runtime.MessageSender = { id: "peach-extension" }138+ sender: chrome.runtime.MessageSender = {139+ id: "peach-extension",140+ url: "chrome-extension://peach-extension/popup.html",141+ }139142 ): Promise<MessageResponse> {140143 return await new Promise<MessageResponse>((resolve) => {141144 const keepChannelOpen = listener(message, sender, (response) => resolve(response));@@ -317,10 +320,10 @@317320 url: "https://github.com",318321 });319322 320- const response = await dispatch({321- type: "GET_ENTRY_FOR_FILL",322- payload: { id: entry.id },323- });323+ const response = await dispatch(324+ { type: "GET_ENTRY_FOR_FILL", payload: { id: entry.id } },325+ { id: "peach-extension", url: "https://github.com/login" },326+ );324327 325328 expect(response).toMatchObject({326329 ok: true,@@ -602,7 +605,7 @@602605 breachSpy.mockRestore();603606 });604607 605- it("messages from a different extension id are rejected while undefined sender.id is allowed", async () => {608+ it("messages from foreign or URL-less senders are rejected", async () => {606609 const vault = await import("./vault");607610 await vault.createVault("correct-pass-12345");608611 @@ -612,8 +615,8 @@612615 });613616 614617 await expect(dispatch({ type: "GET_ENTRIES" }, {})).resolves.toMatchObject({615- ok: true,616- data: [],618+ ok: false,619+ error: "invalid_sender",617620 });618621 });619622 @@ -887,7 +890,10 @@887890 totp: "JBSWY3DPEHPK3PXP",888891 });889892 890- const res = await dispatch({ type: "GET_ENTRY_FOR_FILL", payload: { id: e.id } });893+ const res = await dispatch(894+ { type: "GET_ENTRY_FOR_FILL", payload: { id: e.id } },895+ { id: "peach-extension", url: "https://mail.test/login" },896+ );891897 expect(res.ok).toBe(true);892898 const data = res.data as { totp?: string; totpCode?: string; totpRemaining?: number };893899 expect(data.totp).toBeUndefined();@@ -907,7 +913,10 @@907913 });908914 await vault.updateEntry(e.id, { password: "new-pw" });909915 910- const fill = await dispatch({ type: "GET_ENTRY_FOR_FILL", payload: { id: e.id } });916+ const fill = await dispatch(917+ { type: "GET_ENTRY_FOR_FILL", payload: { id: e.id } },918+ { id: "peach-extension", url: "https://bank.test/login" },919+ );911920 expect(fill.ok).toBe(true);912921 // Passive autofill never receives prior passwords (blast-radius containment).913922 expect((fill.data as Record<string, unknown>).passwordHistory).toBeUndefined();src/background/notes.test.ts+5 −1
@@ -27,6 +27,7 @@2727 const chromeMock = {2828 runtime: {2929 id: 'peach-extension',30+ getURL: (path = '') => `chrome-extension://peach-extension/${path}`,3031 onMessage: {3132 addListener: vi.fn((fn: MessageListener) => { listener = fn; }),3233 },@@ -87,7 +88,10 @@8788 8889 async function dispatch(message: Message): Promise<MessageResponse> {8990 return new Promise<MessageResponse>((resolve) => {90- const keepOpen = listener(message, { id: 'peach-extension' }, resolve);91+ const keepOpen = listener(message, {92+ id: 'peach-extension',93+ url: 'chrome-extension://peach-extension/popup.html',94+ }, resolve);9195 expect(keepOpen).toBe(true);9296 });9397 }src/background/recovery-words.test.ts+5 −1
@@ -35,6 +35,7 @@3535 const chromeMock = {3636 runtime: {3737 id: 'peach-extension',38+ getURL: (path = '') => `chrome-extension://peach-extension/${path}`,3839 onMessage: { addListener: vi.fn((fn: MessageListener) => { listener = fn; }) },3940 sendMessage: vi.fn(async () => ({ ok: true })),4041 },@@ -93,7 +94,10 @@9394 9495 async function dispatch(message: Message): Promise<MessageResponse> {9596 return new Promise<MessageResponse>((resolve) => {96- const keepOpen = listener(message, { id: 'peach-extension' }, resolve);97+ const keepOpen = listener(message, {98+ id: 'peach-extension',99+ url: 'chrome-extension://peach-extension/popup.html',100+ }, resolve);97101 expect(keepOpen).toBe(true);98102 });99103 }src/background/recovery.adversarial.test.ts+5 −1
@@ -39,6 +39,7 @@3939 const chromeMock = {4040 runtime: {4141 id: "peach-extension",42+ getURL: (path = "") => `chrome-extension://peach-extension/${path}`,4243 onMessage: {4344 addListener: vi.fn((fn: MessageListener) => {4445 listener = fn;@@ -120,7 +121,10 @@120121 return new Promise<MessageResponse>((resolve) => {121122 const keepOpen = listener!(122123 message,123- { id: "peach-extension" } as chrome.runtime.MessageSender,124+ {125+ id: "peach-extension",126+ url: "chrome-extension://peach-extension/popup.html",127+ } as chrome.runtime.MessageSender,124128 resolve125129 );126130 expect(keepOpen).toBe(true);src/background/vault.adversarial.test.ts+5 −1
@@ -56,6 +56,7 @@5656 return {5757 runtime: {5858 id: "peach-extension",59+ getURL: (path = "") => `chrome-extension://peach-extension/${path}`,5960 onMessage: {6061 addListener: vi.fn((fn: MessageListener) => {6162 listenerSink.listener = fn;@@ -160,7 +161,10 @@160161 return new Promise<MessageResponse>((resolve) => {161162 const keepOpen = sink.listener!(162163 message,163- { id: "peach-extension" } as chrome.runtime.MessageSender,164+ {165+ id: "peach-extension",166+ url: "chrome-extension://peach-extension/popup.html",167+ } as chrome.runtime.MessageSender,164168 resolve165169 );166170 expect(keepOpen).toBe(true);