From 6f6664e744ae4e279bef576c59c8cc1bb476f048 Mon Sep 17 00:00:00 2001 From: slashdevcorpse <73707049+slashdevcorpse@users.noreply.github.com> Date: Fri, 19 Jun 2026 20:10:43 -0400 Subject: [PATCH] Validate shell openPath targets (#5830) Co-authored-by: brennanb2025 --- src/main/ipc/shell.test.ts | 39 ++++++++++++++++++++++++++++++++++++++ src/main/ipc/shell.ts | 6 ++++-- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/main/ipc/shell.test.ts b/src/main/ipc/shell.test.ts index a72074b043e..09e98c8e975 100644 --- a/src/main/ipc/shell.test.ts +++ b/src/main/ipc/shell.test.ts @@ -131,6 +131,45 @@ describe('registerShellHandlers', () => { await expect(handler({})).resolves.toBeNull() }) + describe('shell:openPath', () => { + it('ignores relative paths', async () => { + const handler = getHandler('shell:openPath') + + await expect(handler({}, 'relative/workspace')).resolves.toBeUndefined() + expect(statMock).not.toHaveBeenCalled() + expect(showItemInFolderMock).not.toHaveBeenCalled() + }) + + it('ignores missing paths', async () => { + statMock.mockRejectedValueOnce(new Error('missing')) + const workspacePath = resolve('missing-workspace') + const handler = getHandler('shell:openPath') + + await expect(handler({}, workspacePath)).resolves.toBeUndefined() + expect(statMock).toHaveBeenCalledWith(normalize(workspacePath)) + expect(showItemInFolderMock).not.toHaveBeenCalled() + }) + + it('reveals existing absolute paths', async () => { + const workspacePath = resolve('workspace') + const handler = getHandler('shell:openPath') + + await expect(handler({}, workspacePath)).resolves.toBeUndefined() + expect(showItemInFolderMock).toHaveBeenCalledWith(normalize(workspacePath)) + }) + + it('swallows launcher failures', async () => { + showItemInFolderMock.mockImplementationOnce(() => { + throw new Error('launcher unavailable') + }) + const workspacePath = resolve('workspace') + const handler = getHandler('shell:openPath') + + await expect(handler({}, workspacePath)).resolves.toBeUndefined() + expect(showItemInFolderMock).toHaveBeenCalledWith(normalize(workspacePath)) + }) + }) + describe('shell:openInFileManager', () => { it('rejects relative paths', async () => { const handler = getHandler('shell:openInFileManager') diff --git a/src/main/ipc/shell.ts b/src/main/ipc/shell.ts index 1b38ecb9b47..a2eac833ff4 100644 --- a/src/main/ipc/shell.ts +++ b/src/main/ipc/shell.ts @@ -126,8 +126,10 @@ async function openWithSystemDefault(pathValue: string): Promise { } export function registerShellHandlers(): void { - ipcMain.handle('shell:openPath', (_event, path: string) => { - shell.showItemInFolder(path) + ipcMain.handle('shell:openPath', async (_event, path: string): Promise => { + // Why: keep the legacy fire-and-forget renderer contract while reusing the + // same absolute/existing path validation as the explicit file-manager API. + void (await openInFileManager(path)) }) ipcMain.handle(