Skip to content

Commit fef2323

Browse files
authored
fix(app,gui-plugins): port renderer UUID generation to HTTP-safe uuid() (#60462)
1 parent fdb588a commit fef2323

20 files changed

Lines changed: 61 additions & 21 deletions

File tree

‎packages/app/AGENTS.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515
- App (from `packages/app`): `bun dev -- --port 4444`
1616
- Open `http://localhost:4444` to verify UI changes (it targets the backend at `http://localhost:4096`).
1717

18+
## Insecure Origins (HTTP LAN)
19+
20+
The Web UI is served over plain HTTP on non-localhost LAN and Tailscale origins (`isSecureContext === false`). This rule applies across `app`, `ui`, `session-ui` and `gui-plugins` window code.
21+
22+
- Never call `crypto.randomUUID()` directly in renderer or browser code; it is `[SecureContext]`-only and throws `TypeError: crypto.randomUUID is not a function` on plain HTTP origins. Always use `uuid()` from `@opencode/util/uuid`.
23+
- Guard any other `[SecureContext]`-only Web API (`crypto.subtle`, `navigator.clipboard`, `navigator.serviceWorker`) with a non-secure fallback.
24+
1825
## SolidJS
1926

2027
- Always prefer `createStore` over multiple `createSignal` calls

‎packages/app/e2e/regression/review.spec.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,27 @@ test("a rejected Git init releases its session's button without showing an error
151151
await expect(init).toBeEnabled()
152152
})
153153

154+
test("opens file picker launchers when randomUUID is unavailable on insecure HTTP origins", async ({ page }) => {
155+
await page.addInitScript(() => {
156+
Object.defineProperty(globalThis, "isSecureContext", { configurable: true, value: false })
157+
Object.defineProperty(globalThis.crypto, "randomUUID", { configurable: true, value: undefined })
158+
})
159+
160+
await openSession(page, {
161+
name: "ReviewOpenFileInsecure",
162+
seed: { panes: { ses_reviewopenfileinsecure: { review: true } } },
163+
})
164+
165+
const panel = page.locator("#review-panel")
166+
const tab = panel.getByRole("tab", { name: "Open file", exact: true })
167+
await openFileLauncher(panel)
168+
await expect(tab).toHaveAttribute("aria-selected", "true")
169+
await expect(panel.getByRole("combobox", { name: "Filter files" })).toBeFocused()
170+
171+
await openFileLauncher(panel)
172+
await expect(tab).toHaveCount(2)
173+
})
174+
154175
test("open file tab browses, searches, and tracks missing files", async ({ page }) => {
155176
const searches: { query: string; dirs?: string; limit?: number; directory: string }[] = []
156177
const directory = "C:/OpenCode/ReviewOpenFile"

‎packages/app/src/composer/attachments/attachments.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { onCleanup, onMount } from "solid-js"
22
import { createStore } from "solid-js/store"
33
import { makeEventListener } from "@solid-primitives/event-listener"
44
import { createBlobReference } from "@/runtime/persistence/drafts"
5-
import { uuid } from "@/runtime/persistence/uuid"
5+
import { uuid } from "@opencode/util/uuid"
66
import type { ComposerPrompt } from "../types"
77
import type { ImageAttachmentPart, PathAttachmentPart } from "../state"
88
import type { AttachmentDestination } from "./destination"

‎packages/app/src/composer/attachments/destination.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Accessor } from "solid-js"
22
import { useServer } from "@/runtime/server/current"
33
import { useServerSDK } from "@/runtime/server/client"
44
import { authTokenFromCredentials } from "@/runtime/server/api"
5-
import { uuid } from "@/runtime/persistence/uuid"
5+
import { uuid } from "@opencode/util/uuid"
66
import { useWorkspaceLocation } from "@/workspaces/location"
77
import type { ComposerControls } from "../adapter"
88

‎packages/app/src/composer/comment-note.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { SessionMessageUser } from "@opencode/client/promise"
22
import { Option, Schema } from "effect"
33
import { Persistence } from "@/runtime/persistence/schema"
4-
import { uuid } from "@/runtime/persistence/uuid"
4+
import { uuid } from "@opencode/util/uuid"
55
import { FileSelection } from "@/workspaces/files/types"
66
import { durableNote, LegacyBrowserNote, NoteComment, QuoteComment, type ContextItem } from "./schema"
77

‎packages/app/src/composer/comments.tsx‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Persist, persisted } from "@/runtime/persistence/storage"
77
import { useServerSDK } from "@/runtime/server/client"
88
import type { ServerScope } from "@/runtime/server/scope"
99
import { createScopedCache } from "@/runtime/server/scoped-cache"
10-
import { uuid } from "@/runtime/persistence/uuid"
10+
import { uuid } from "@opencode/util/uuid"
1111
import type { SelectedLineRange } from "@/workspaces/files/model"
1212
import { useWorkspaceLocation } from "@/workspaces/location"
1313
import { CommentStore, type LineComment } from "./schema"
@@ -80,9 +80,12 @@ function group(comments: LineComment[]) {
8080
}
8181

8282
function createCommentSessionState(store: Store<CommentStore>, setStore: SetStoreFunction<CommentStore>) {
83-
const [state, setState] = createStore({
84-
focus: null as CommentFocus | null,
85-
active: null as CommentFocus | null,
83+
const [state, setState] = createStore<{
84+
focus: CommentFocus | null
85+
active: CommentFocus | null
86+
}>({
87+
focus: null,
88+
active: null,
8689
})
8790

8891
// Reuse the previous array when contents are unchanged so consumers keep a stable

‎packages/app/src/runtime/plugin/host-apis.tsx‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { useDialog } from "@opencode/ui/context/dialog"
1616
import { useTheme } from "@opencode/ui/theme/context"
1717
import { toaster } from "@opencode/ui/toast"
1818
import { base64Encode } from "@opencode/util/encode"
19+
import { uuid } from "@opencode/util/uuid"
1920
import {
2021
type App,
2122
type Layout,
@@ -1023,7 +1024,7 @@ export function createPluginAttachment(apis: HostApis) {
10231024
id: error ? settings.sounds.errors() : settings.sounds.agent(),
10241025
}))
10251026

1026-
const key = `plugin:${plugin}\0${options.id ?? crypto.randomUUID()}`
1027+
const key = `plugin:${plugin}\0${options.id ?? uuid()}`
10271028
const result = { notification: false, sound: false }
10281029
const session = options.session
10291030

‎packages/app/src/runtime/server/data.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Data } from "@opencode/client/solid"
22
import { isSessionNotFoundError, type OpenCodeEvent, type SessionInfo } from "@opencode/client/promise"
33
import { onCleanup } from "solid-js"
44
import { createStore } from "solid-js/store"
5-
import { uuid } from "@/runtime/persistence/uuid"
5+
import { uuid } from "@opencode/util/uuid"
66

77
type SessionMutation = { readonly id: string; readonly type: "remove"; readonly sessionID: string }
88

‎packages/app/src/session/composer/region.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { showToast } from "@/shell/notifications/toast"
2020
import { errorMessage } from "@/shell/layout/helpers"
2121
import { useLocal } from "@/providers/models/selection"
2222
import { usePlatform } from "@/runtime/platform/platform"
23-
import { uuid } from "@/runtime/persistence/uuid"
23+
import { uuid } from "@opencode/util/uuid"
2424
import { contextItemKey } from "@/composer/schema"
2525
import type { QuoteSource } from "@/composer/quote-source"
2626
import type { ComposerQuoteAnchor } from "@opencode/plugin/gui"

‎packages/app/src/shell/tabs/tabs.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { usePluginServers } from "@/runtime/plugin/servers"
88
import { createEffect, getOwner, onCleanup, startTransition } from "solid-js"
99
import { useLocation, useNavigate } from "@solidjs/router"
1010
import { usePlatform } from "@/runtime/platform/platform"
11-
import { uuid } from "@/runtime/persistence/uuid"
11+
import { uuid } from "@opencode/util/uuid"
1212
import { SessionTabsRemovedDetail } from "@/shell/titlebar/session-events"
1313
import { sessionHref } from "@/shell/routes/session"
1414
import { createTabMemory } from "./memory"

0 commit comments

Comments
 (0)