Selaa lähdekoodia

fix(desktop): restrict external links

Brendan Allan 2 viikkoa sitten
vanhempi
sitoutus
c552cee37e

+ 19 - 0
packages/desktop/src/main/external-link.test.ts

@@ -0,0 +1,19 @@
+import { describe, expect, test } from "bun:test"
+import { safeExternalUrl } from "./external-link"
+
+describe("safeExternalUrl", () => {
+  test("allows web links", () => {
+    expect(safeExternalUrl("https://opencode.ai/docs")).toBe("https://opencode.ai/docs")
+    expect(safeExternalUrl("http://localhost:3000/callback")).toBe("http://localhost:3000/callback")
+  })
+
+  test("rejects executable and application protocols", () => {
+    expect(safeExternalUrl("file:///tmp/script.sh")).toBeUndefined()
+    expect(safeExternalUrl("ssh://attacker.example")).toBeUndefined()
+    expect(safeExternalUrl("custom-handler:payload")).toBeUndefined()
+  })
+
+  test("rejects malformed URLs", () => {
+    expect(safeExternalUrl("not a url")).toBeUndefined()
+  })
+})

+ 8 - 0
packages/desktop/src/main/external-link.ts

@@ -0,0 +1,8 @@
+const allowedProtocols = new Set(["https:", "http:"])
+
+export function safeExternalUrl(value: string) {
+  if (!URL.canParse(value)) return
+  const url = new URL(value)
+  if (!allowedProtocols.has(url.protocol)) return
+  return url.toString()
+}

+ 4 - 1
packages/desktop/src/main/ipc.ts

@@ -8,6 +8,7 @@ import type { DesktopMenuAction } from "@opencode-ai/app/desktop-menu"
 import type { FatalRendererError, ServerReadyData, TitlebarTheme } from "../preload/types"
 import { runDesktopMenuAction } from "./desktop-menu-actions"
 import { setForceFocus } from "./debug"
+import { safeExternalUrl } from "./external-link"
 import { assertAttachmentBudget, createPickedFileAuthorizations } from "./attachment-picker"
 import { getStore, removeStoreFileIfEmpty } from "./store"
 import { getPinchZoomEnabled, getWindowID, setPinchZoomEnabled, setTitlebar, updateTitlebar } from "./windows"
@@ -178,7 +179,9 @@ export function registerIpcHandlers(deps: Deps) {
   )
 
   ipcMain.on("open-link", (_event: IpcMainEvent, url: string) => {
-    void shell.openExternal(url)
+    const externalUrl = safeExternalUrl(url)
+    if (!externalUrl) return
+    void shell.openExternal(externalUrl)
   })
 
   ipcMain.handle("open-path", async (_event: IpcMainInvokeEvent, path: string, app?: string) => {