[PR #269] fix: harden desktop shell against untrusted webviews and supply chain #237

Open
opened 2026-10-18 14:07:14 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/open-webui/desktop/pull/269
Author: @ggrace519
Created: 9/6/2026
Status: 🔄 Open

Base: main ← Head: feat/security-hardening


📝 Commits (3)

  • f91d995 fix: harden desktop shell against untrusted webviews and supply chain
  • bf094d4 fix(pty): ignore resize/write after the process has exited
  • 8018ba3 test(pty): run the live resize-after-exit case on Windows

📊 Changes

42 files changed (+1944 additions, -535 deletions)

View changed files

➕ .github/workflows/ci.yml (+22 -0)
📝 .github/workflows/release.yml (+5 -24)
📝 CHANGELOG.md (+31 -0)
📝 electron-builder.yml (+10 -0)
📝 eslint.config.mjs (+19 -1)
📝 package-lock.json (+6 -33)
📝 package.json (+3 -2)
➕ src/main/app-protocol.ts (+82 -0)
➕ src/main/guest-webview.ts (+49 -0)
📝 src/main/index.ts (+300 -163)
➕ src/main/linux-sandbox.ts (+17 -0)
➕ src/main/safe-open.ts (+39 -0)
➕ src/main/tls.ts (+58 -0)
➕ src/main/utils/artifact-integrity.ts (+160 -0)
➕ src/main/utils/child-env.ts (+64 -0)
➕ src/main/utils/error-message.ts (+4 -0)
➕ src/main/utils/hf-paths.ts (+63 -0)
📝 src/main/utils/huggingface.ts (+91 -83)
📝 src/main/utils/index.ts (+73 -66)
➕ src/main/utils/llama-release.ts (+35 -0)

...and 22 more files

📄 Description

Description

Guest Open WebUI pages (including remote connections) could invoke the full privileged desktop API via window.electronAPI[requestData.type]. Remote webviews also received Open Terminal API keys. TLS verification was disabled on every Chromium session. Python, llama.cpp, and Hugging Face GGUF downloads were not checksummed. Linux always launched with --no-sandbox. Packaged Electron fuses were left at insecure defaults. macOS/Windows releases published unsigned fallbacks on codesign failure.

This PR allowlists guest IPC, scopes TLS exceptions to user-added connection origins, verifies downloaded artifacts, sandboxes shell windows, pins Electron to 39.8.10 (GHSA-h7rp-cf8h-j98x), keeps Cloudflare Access / SSO login inside the app, and fails closed on unsigned releases.

Production shell windows load app://renderer/… instead of file://. Dev (npm run dev) is unchanged.

Overlaps (maintainers may want to close or rebase):

  • #253 (webview message handling) — this allowlists guest send() and does not index electronAPI with guest strings
  • #218 / #175 (OAuth popups in Electron) — Access/OIDC popups share the guest session; chat links still open externally (#165)
  • #217 (dedicated auth window)

Fork-only docs (CLAUDE.md, DECISIONS.md, develop-branch model) are not in this PR.

Security

  • Guest send() allowlist: token:update, app:info, app:data, window:isFocused
  • will-attach-webview forces sandbox / contextIsolation / no Node
  • Terminal/OpenAI events only to the local-connection webview
  • TLS default-verify; exceptions only for configured connection origins + localhost
  • openExternal is http:/https:/mailto: only
  • HF paths confined; GGUF SHA-256 from Hub LFS (?blobs=true)
  • Python / llama.cpp downloads hashed; llama.cpp “latest” picks a release that actually has binaries
  • Child env blocklist; llama --host/--port/--models-dir always from the desktop
  • Linux --no-sandbox only for AppImage/snap/Flatpak/unpackaged/ELECTRON_DISABLE_SANDBOX=1
  • Electron fuses at pack time; shell sandbox: true; drop @electron-toolkit/preload window.electron

Fixes

  • Access/SSO stays in the webview (session cookies land; UI loads after callback)
  • Tray Quit actually exits
  • Guest no longer crashes on app:data (null instead of {} — Open WebUI +layout.svelte does not import appData)
  • Quit waits for child processes

Infrastructure

  • npm test (23 node:test cases on the security helpers)
  • CI: typecheck, tests, eslint on src/main + preload

Related to #175, #165. Does not close those if #218 is the intended path for OAuth host allowlisting.

Verification

npm test          # 23 pass
npm run lint:main # exit 0
npx tsc --noEmit -p tsconfig.node.json --composite false

Contributor License Agreement


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/open-webui/desktop/pull/269 **Author:** [@ggrace519](https://github.com/ggrace519) **Created:** 9/6/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feat/security-hardening` --- ### 📝 Commits (3) - [`f91d995`](https://github.com/open-webui/desktop/commit/f91d9958c507c9771574524652f781f881c6aebb) fix: harden desktop shell against untrusted webviews and supply chain - [`bf094d4`](https://github.com/open-webui/desktop/commit/bf094d4d29facba518d60ab341dc7d4b7778be9f) fix(pty): ignore resize/write after the process has exited - [`8018ba3`](https://github.com/open-webui/desktop/commit/8018ba3fe61fe869c2a40ded9dd84718d120f619) test(pty): run the live resize-after-exit case on Windows ### 📊 Changes **42 files changed** (+1944 additions, -535 deletions) <details> <summary>View changed files</summary> ➕ `.github/workflows/ci.yml` (+22 -0) 📝 `.github/workflows/release.yml` (+5 -24) 📝 `CHANGELOG.md` (+31 -0) 📝 `electron-builder.yml` (+10 -0) 📝 `eslint.config.mjs` (+19 -1) 📝 `package-lock.json` (+6 -33) 📝 `package.json` (+3 -2) ➕ `src/main/app-protocol.ts` (+82 -0) ➕ `src/main/guest-webview.ts` (+49 -0) 📝 `src/main/index.ts` (+300 -163) ➕ `src/main/linux-sandbox.ts` (+17 -0) ➕ `src/main/safe-open.ts` (+39 -0) ➕ `src/main/tls.ts` (+58 -0) ➕ `src/main/utils/artifact-integrity.ts` (+160 -0) ➕ `src/main/utils/child-env.ts` (+64 -0) ➕ `src/main/utils/error-message.ts` (+4 -0) ➕ `src/main/utils/hf-paths.ts` (+63 -0) 📝 `src/main/utils/huggingface.ts` (+91 -83) 📝 `src/main/utils/index.ts` (+73 -66) ➕ `src/main/utils/llama-release.ts` (+35 -0) _...and 22 more files_ </details> ### 📄 Description ## Description Guest Open WebUI pages (including remote connections) could invoke the full privileged desktop API via `window.electronAPI[requestData.type]`. Remote webviews also received Open Terminal API keys. TLS verification was disabled on every Chromium session. Python, llama.cpp, and Hugging Face GGUF downloads were not checksummed. Linux always launched with `--no-sandbox`. Packaged Electron fuses were left at insecure defaults. macOS/Windows releases published unsigned fallbacks on codesign failure. This PR allowlists guest IPC, scopes TLS exceptions to user-added connection origins, verifies downloaded artifacts, sandboxes shell windows, pins Electron to 39.8.10 (GHSA-h7rp-cf8h-j98x), keeps Cloudflare Access / SSO login inside the app, and fails closed on unsigned releases. Production shell windows load `app://renderer/…` instead of `file://`. Dev (`npm run dev`) is unchanged. **Overlaps (maintainers may want to close or rebase):** - #253 (webview message handling) — this allowlists guest `send()` and does not index `electronAPI` with guest strings - #218 / #175 (OAuth popups in Electron) — Access/OIDC popups share the guest session; chat links still open externally (#165) - #217 (dedicated auth window) Fork-only docs (`CLAUDE.md`, `DECISIONS.md`, develop-branch model) are **not** in this PR. ### Security - Guest `send()` allowlist: `token:update`, `app:info`, `app:data`, `window:isFocused` - `will-attach-webview` forces `sandbox` / `contextIsolation` / no Node - Terminal/OpenAI events only to the local-connection webview - TLS default-verify; exceptions only for configured connection origins + localhost - `openExternal` is `http:`/`https:`/`mailto:` only - HF paths confined; GGUF SHA-256 from Hub LFS (`?blobs=true`) - Python / llama.cpp downloads hashed; llama.cpp “latest” picks a release that actually has binaries - Child env blocklist; llama `--host`/`--port`/`--models-dir` always from the desktop - Linux `--no-sandbox` only for AppImage/snap/Flatpak/unpackaged/`ELECTRON_DISABLE_SANDBOX=1` - Electron fuses at pack time; shell `sandbox: true`; drop `@electron-toolkit/preload` `window.electron` ### Fixes - Access/SSO stays in the webview (session cookies land; UI loads after callback) - Tray Quit actually exits - Guest no longer crashes on `app:data` (`null` instead of `{}` — Open WebUI `+layout.svelte` does not import `appData`) - Quit waits for child processes ### Infrastructure - `npm test` (23 `node:test` cases on the security helpers) - CI: typecheck, tests, eslint on `src/main` + preload ## Related Issues Related to #175, #165. Does not close those if #218 is the intended path for OAuth host allowlisting. ## Verification ``` npm test # 23 pass npm run lint:main # exit 0 npx tsc --noEmit -p tsconfig.node.json --composite false ``` --- ## Contributor License Agreement - [x] By submitting this pull request, I confirm that I have read and fully agree to the [Contributor License Agreement (CLA)](https://github.com/open-webui/desktop/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT), and I am providing my contributions under its terms. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
open-webui/desktop#237
No description provided.