diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx index 9e7ed5b6c49..22febf78e46 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/permission.tsx @@ -19,21 +19,10 @@ import { useDialog } from "../../ui/dialog" import { getScrollAcceleration } from "../../util/scroll" import { useTuiConfig } from "../../context/tui-config" import { ConfigProtection } from "@/kilocode/permission/config-paths" // kilocode_change +import { normalizeUrls } from "@/kilocode/util/url" // kilocode_change type PermissionStage = "permission" | "always" | "reject" -// Convert IDN/Unicode hostnames to punycode ASCII in any http/https URLs found -// in the given string, to prevent homograph attacks in permission dialogs. -function normalizeUrls(text: string) { - return text.replace(/https?:\/\/\S+/g, (match) => { - try { - return new URL(match).href - } catch { - return match - } - }) -} - function normalizePath(input?: string) { if (!input) return "" diff --git a/packages/opencode/src/kilocode/util/url.ts b/packages/opencode/src/kilocode/util/url.ts new file mode 100644 index 00000000000..c9e152c374e --- /dev/null +++ b/packages/opencode/src/kilocode/util/url.ts @@ -0,0 +1,18 @@ +/** + * Normalize any http/https URLs in a string so that IDN/Unicode hostnames are + * converted to their punycode ASCII form, preventing homograph attacks in + * permission dialogs where visually identical Unicode characters (e.g. Cyrillic + * 'а' U+0430) could impersonate trusted domains (e.g. 'apitest.com'). + * + * Example: "curl https://аpitest.com/status" (Cyrillic а) + * → "curl https://xn--pitest-2nf.com/status" + */ +export function normalizeUrls(text: string) { + return text.replace(/https?:\/\/\S+/g, (match) => { + try { + return new URL(match).href + } catch { + return match + } + }) +} diff --git a/packages/opencode/src/tool/bash.ts b/packages/opencode/src/tool/bash.ts index e8750312a5b..df171c24ad6 100644 --- a/packages/opencode/src/tool/bash.ts +++ b/packages/opencode/src/tool/bash.ts @@ -20,6 +20,7 @@ import { Shell } from "@/shell/shell" import { BashArity } from "@/permission/arity" import * as Truncate from "./truncate" import { Plugin } from "@/plugin" +import { normalizeUrls } from "@/kilocode/util/url" // kilocode_change import { Effect, Stream } from "effect" import { ChildProcess } from "effect/unstable/process" import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner" @@ -238,18 +239,6 @@ function preview(text: string) { return "...\n\n" + text.slice(-MAX_METADATA_LENGTH) } -// Normalize any http/https URLs in a command string so that IDN/Unicode hostnames -// are converted to their punycode ASCII form, preventing homograph attacks in -// permission dialogs where "аpitest.com" (Cyrillic) looks identical to "apitest.com". -function normalizeUrls(text: string) { - return text.replace(/https?:\/\/\S+/g, (match) => { - try { - return new URL(match).href - } catch { - return match - } - }) -} function tail(text: string, maxLines: number, maxBytes: number) { const lines = text.split("\n") diff --git a/packages/opencode/src/tool/webfetch.ts b/packages/opencode/src/tool/webfetch.ts index cde4bab8564..ec4172d8256 100644 --- a/packages/opencode/src/tool/webfetch.ts +++ b/packages/opencode/src/tool/webfetch.ts @@ -4,6 +4,7 @@ import * as Tool from "./tool" import TurndownService from "turndown" import DESCRIPTION from "./webfetch.txt" import { isImageAttachment } from "@/util/media" +import { normalizeUrls } from "@/kilocode/util/url" // kilocode_change const MAX_RESPONSE_SIZE = 5 * 1024 * 1024 // 5MB const DEFAULT_TIMEOUT = 30 * 1000 // 30 seconds @@ -34,16 +35,7 @@ export const WebFetchTool = Tool.define( throw new Error("URL must start with http:// or https://") } - // Normalize URL: convert IDN/Unicode hostnames to punycode ASCII to prevent - // homograph attacks where visually similar Unicode characters impersonate trusted domains. - // e.g. "аpitest.com" (Cyrillic а) → "xn--pitest-n5b.com" - const url = (() => { - try { - return new URL(params.url).href - } catch { - return params.url - } - })() + const url = normalizeUrls(params.url) // kilocode_change yield* ctx.ask({ permission: "webfetch",