{"uuid": "466a667f-7570-4927-9ec1-fd60a9992b7f", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "GHSA-jhw3-9q9r-gc5w", "type": "seen", "source": "https://gist.github.com/Mundi-Xu/99af1b08275fd437cfb79bfe481e68b7", "content": "# Cherry Studio SearchService RCE via Node.js Integration in Remote Content\n\n| Field | Value |\n|---|---|\n| Product | Cherry Studio |\n| Identifier | GHSA-jhw3-9q9r-gc5w |\n| Weakness | Remote content executed with Node.js privileges |\n| Severity | Critical |\n| CVSS 3.1 | 9.6 (`CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H`) |\n| Affected versions | `&gt;= 1.2.2, &lt;= 1.9.12` |\n| Patched versions | None |\n\n## Summary\n\nCherry Studio's web-search implementation creates an Electron `BrowserWindow` with `nodeIntegration: true` and `contextIsolation: false`, then loads remote web content into it. JavaScript delivered by the page can use Node.js APIs such as `fs`, `child_process`, `os`, and `process.env`.\n\nThe local search-provider path loads a configured search-engine URL into this privileged window for every search. Additional paths load individual result pages when `usingBrowser` is enabled and open local-provider websites in the same window from the settings page.\n\nRemote JavaScript therefore runs with the operating-system privileges of the Cherry Studio process. It can read or modify files, execute commands, and access credentials available to the user.\n\n## Affected Code\n\nThe privileged search window is created in [`src/main/services/SearchService.ts`](https://github.com/CherryHQ/cherry-studio/blob/da0a11fdc43ab91e1d486f57663c61f6c747e69d/src/main/services/SearchService.ts#L17-L27):\n\n```typescript\nconst newWindow = new BrowserWindow({\n  width: 1280,\n  height: 768,\n  show,\n  webPreferences: {\n    nodeIntegration: true,\n    contextIsolation: false,\n    devTools: is.dev\n  }\n})\n```\n\nThe same service loads the supplied URL and executes the page before extracting its HTML in [`SearchService.ts`](https://github.com/CherryHQ/cherry-studio/blob/da0a11fdc43ab91e1d486f57663c61f6c747e69d/src/main/services/SearchService.ts#L57-L79):\n\n```typescript\nif (window) {\n  await window.loadURL(url)\n} else {\n  window = await this.createNewSearchWindow(uid)\n  await window.loadURL(url)\n}\n\nreturn await window.webContents.executeJavaScript('document.documentElement.outerHTML')\n```\n\nNo navigation restriction or permission handler is registered on this search window. Redirected content retains the same Node.js privileges.\n\n## Reachable Paths\n\n### 1. Local search providers\n\n[`LocalSearchProvider.ts`](https://github.com/CherryHQ/cherry-studio/blob/da0a11fdc43ab91e1d486f57663c61f6c747e69d/src/renderer/src/providers/WebSearchProvider/LocalSearchProvider.ts#L42-L51) unconditionally loads the configured search URL into the privileged window:\n\n```typescript\nconst url = this.provider.url.replace('%s', encodeURIComponent(queryWithLanguage))\nconst promisesToRace: [Promise] = [window.api.searchService.openUrlInSearchWindow(uid, url)]\ncontent = await Promise.race(promisesToRace)\n```\n\nThe page itself and any scripts it includes execute with Node.js access.\n\n### 2. Search-result pages\n\n[`fetch.ts`](https://github.com/CherryHQ/cherry-studio/blob/da0a11fdc43ab91e1d486f57663c61f6c747e69d/src/renderer/src/utils/fetch.ts#L55-L75) loads an individual result URL into the same window when `usingBrowser` is enabled:\n\n```typescript\nif (usingBrowser) {\n  const windowApiPromise = window.api.searchService.openUrlInSearchWindow(`search-window-${nanoid()}`, url)\n  html = await Promise.race(promisesToRace)\n}\n```\n\nSearXNG and local providers pass their `usingBrowser` setting to this function when retrieving result content. This setting is disabled by default, so this path is opt-in.\n\n### 3. Provider settings\n\nThe local-provider settings action opens the provider website in the privileged window in [`WebSearchProviderSetting.tsx`](https://github.com/CherryHQ/cherry-studio/blob/da0a11fdc43ab91e1d486f57663c61f6c747e69d/src/renderer/src/pages/settings/WebSearchSettings/WebSearchProviderSetting.tsx#L170-L176):\n\n```typescript\nawait window.api.searchService.openSearchWindow(provider.id, true)\nawait window.api.searchService.openUrlInSearchWindow(provider.id, officialWebsite)\n```\n\n## Attack Conditions\n\nThe three paths have different prerequisites:\n\n- The local search-provider page is always loaded into the privileged window when that provider is used. Exploitation requires attacker-controlled JavaScript to run in the search page, for example through a compromised page or third-party script.\n- Loading an individual search result requires `usingBrowser` to be enabled. Once enabled, an attacker-controlled result URL is loaded directly into the privileged window.\n- The settings path requires attacker-controlled JavaScript on the provider website loaded by the application.\n\nIn every case, the user must initiate the search or open the provider website. The remote page itself receives Node.js access without a separate permission prompt.\n\n## Minimal Proof of Concept\n\nThe following page demonstrates Node.js access by creating a harmless marker file in the operating system's temporary directory:\n\n```html\n\n\n  \n    \nCherry Studio Node.js integration test\n    \n      const fs = require('fs')\n      const os = require('os')\n      const path = require('path')\n\n      const markerPath = path.join(os.tmpdir(), 'cherry-studio-nodeintegration-poc.txt')\n      fs.writeFileSync(markerPath, `Node.js access confirmed: ${process.version}\\n`)\n      document.body.insertAdjacentHTML('beforeend', `\nMarker created at: ${markerPath}`)\n    \n  \n\n```\n\nOne reproduction route in a test profile is:\n\n1. Host the page at `https://TEST_HOST/poc.html`.\n2. Configure a Local or SearXNG provider with `usingBrowser` enabled.\n3. Make the test search provider return `https://TEST_HOST/poc.html` as a result and perform a search through Cherry Studio.\n4. Confirm that `cherry-studio-nodeintegration-poc.txt` was created in the system temporary directory.\n\nThe same primitive provides access to other Node.js modules, including `child_process`, under the user's account.\n\n## Impact\n\nRemote page JavaScript runs with the privileges of the desktop application rather than those of a normal sandboxed browser page. A successful attack can:\n\n- read or modify files accessible to the user;\n- access application data, API keys, SSH keys, and cloud credentials;\n- execute local programs or shell commands;\n- modify startup files or other persistence locations;\n- terminate processes or delete user data.\n\nUser interaction is required to initiate the affected search or open the provider-settings page. No attacker-controlled account or application privilege is required.\n\n## Disclosure Timeline\n\n- **April 10, 2025:** The affected `SearchService` implementation was introduced in public commit [`f9c6bddae5b91cc50872c76e791105fa219d0d34`](https://github.com/CherryHQ/cherry-studio/commit/f9c6bddae5b91cc50872c76e791105fa219d0d34).\n- **April 11, 2025:** Cherry Studio `1.2.2`, the first affected release, was published.\n- **April 8, 2026:** Report submitted through GitHub Private Vulnerability Reporting as GHSA-jhw3-9q9r-gc5w.\n- **July 3, 2026:** The legacy `SearchService` path was removed from `main` in public commit `151853035e8e417a51559ebfc243eda98361a882`.\n- **July 5, 2026:** A stable release was published and still contained the affected code.\n- **July 16, 2026:** Technical details publicly disclosed.\n\n## References\n\n- GitHub Security Advisory: [GHSA-jhw3-9q9r-gc5w](https://github.com/CherryHQ/cherry-studio/security/advisories/GHSA-jhw3-9q9r-gc5w)\n- Introduction commit: [`f9c6bddae5b91cc50872c76e791105fa219d0d34`](https://github.com/CherryHQ/cherry-studio/commit/f9c6bddae5b91cc50872c76e791105fa219d0d34)\n- Main-branch removal commit: [`151853035e8e417a51559ebfc243eda98361a882`](https://github.com/CherryHQ/cherry-studio/commit/151853035e8e417a51559ebfc243eda98361a882)\n- Electron security guidance: [Security checklist](https://www.electronjs.org/docs/latest/tutorial/security)\n", "creation_timestamp": "2026-07-15T17:21:51.300649Z"}