Repository navigation
Description
Deep Security Audit — Data Leak Analysis (2026-05-13)
Full audit of every outbound HTTP call, the sharing pipeline, LLM headers, remote config, MCP tools, and bundled web UI. Each critical claim was verified against source code.
⛔ Conditional Data Leaks (opt-in, but scope is significant)
1. Share system uploads prompts, responses, tool outputs, and code diffs — MEDIUM (opt-in)
Files: packages/opencode/src/share/share-next.ts, packages/opencode/src/share/session.ts
When a session is shared (manually or via share: "auto"), the following is uploaded in real-time to https://opncd.ai:
| Data | Content |
|---|---|
| Session metadata | title, model name, timestamps |
| User messages | full user prompts |
| Assistant messages | full assistant responses |
| Tool parts | tool inputs and outputs (file reads, shell commands, etc.) |
| File diffs | SnapshotFileDiff: filename + unified diff patch (changed lines only) |
The SnapshotFileDiff type (packages/opencode/src/snapshot/index.ts:22–31) carries a unified diff patch string — changed lines, not full file contents. An earlier draft of this report incorrectly stated full file contents were sent; this has been corrected.
Auto-share triggers only if:
share: "auto"in config, ORautoshare: true(deprecated), OROPENCODE_AUTO_SHARE=1env var
Default: OFF. There is no path where sharing happens without explicit opt-in.
2. opencode provider routes requests through opncd.ai — MEDIUM (opt-in)
File: packages/opencode/src/session/llm.ts:374–380
When the user configures the opencode provider (requires explicit apiKey from opencode.ai), all LLM requests are routed through opencode.ai servers with these extra headers:
x-opencode-project: <first git root commit hash> ← pseudonymous project ID
x-opencode-session: <session UUID>
x-opencode-request: <message UUID>
x-opencode-client: cli|tui|desktop|...
User-Agent: opencode/<version>
This only affects users who explicitly configure provider.opencode.options.apiKey. Users running their own Anthropic/OpenAI/etc. keys are unaffected.
3. GitHub OIDC/PAT token forwarded to api.opencode.ai — MEDIUM (CI only)
File: packages/opencode/src/cli/cmd/github.ts:363,742
When running opencode github in GitHub Actions with use_github_token mode, a GitHub PAT or OIDC token is POSTed to https://api.opencode.ai/exchange_github_app_token to exchange for a GitHub App token. This is strictly a GitHub Actions CI workflow opt-in.
4. Web search queries sent to third-party services — LOW (tool invocation)
File: packages/opencode/src/tool/mcp-websearch.ts, packages/opencode/src/tool/websearch.ts
When the LLM invokes the web search tool (requires a permission prompt):
- Exa (
mcp.exa.ai): receives the search query - Parallel (
search.parallel.ai): receives the search query plus the local session UUID and model name
The ctx.ask permission prompt should name these third-party recipients explicitly.
5. Remote config token forwarding (enterprise wellknown) — HIGH (enterprise only)
File: packages/opencode/src/config/config.ts:538–561
When a wellknown auth entry is configured, opencode:
- Fetches
${url}/.well-known/opencode - Calls
substituteWellKnownRemoteConfig()— resolvesremote_config.urlandremote_config.headersfrom the wellknown response - Makes a second
fetch(remote.url, { headers: remote.headers })— forwarding any headers the wellknown endpoint specifies
If the wellknown endpoint is compromised or malicious, it can direct the client to an attacker-controlled URL and inject arbitrary headers (including auth tokens) into that second request. This is an SSRF / token-forwarding risk in enterprise deployments only.
⚠️ Background calls (no user data, but automatic)
6. models.dev refreshes every 60 minutes — LOW
File: packages/opencode/src/provider/models.ts:105,186
On startup and every 60 minutes (Schedule.spaced("60 minutes")), opencode fetches https://models.dev/api.json to update the available model list.
Data sent: Only User-Agent: opencode/{channel}/{version}/{client} — reveals version, release channel, and client type. No prompts, no code, no credentials. Pure GET.
Disable: Not directly configurable; this is the model registry update mechanism.
7. Auto-update check on TUI startup — LOW
File: packages/opencode/src/cli/upgrade.ts, packages/opencode/src/installation/index.ts
On every TUI launch, opencode checks for a newer version by hitting npm/brew/GitHub/Chocolatey registries (GET-only). Reveals version, channel, and client type in User-Agent. No user data.
Disable: autoupdate: false in config or OPENCODE_DISABLE_AUTOUPDATE=1.
✅ Confirmed Safe
| Path | Verdict |
|---|---|
| API keys in config | Only sent as Authorization headers to their respective providers — never serialized into shares or sent elsewhere |
| Sentry in web UI | Requires VITE_SENTRY_DSN at build time — not present in the distributed CLI binary |
| Honeycomb / PostHog | Only in cloud backend (packages/console) and maintainer scripts — never in CLI binary |
| LSP binary downloads | GET-only from GitHub releases, no user data |
| MCP server loading | Only loads URLs from user-configured opencode.json |
| Proxy server | Internal relay for workspace feature; does not silently intercept LLM traffic |
| OpenTelemetry | Disabled unless OTEL_EXPORTER_OTLP_ENDPOINT env var is set — fully opt-in |
| x-opencode-* headers | Project ID is first git root commit hash (pseudonymous), not a path or username; only sent to opencode provider |
| Share default | share: "auto" is NOT the default; no implicit sharing |
Recommendations
- Disable auto-update pings:
autoupdate: falsein config orOPENCODE_DISABLE_AUTOUPDATE=1 - Use your own provider API keys (Anthropic, OpenAI, etc.) — prompts go directly to the provider with no opencode.ai intermediary
- Treat
share: "auto"as high-risk — it uploads full prompts, responses, and code diffs in real-time to opncd.ai - Permission prompt for web search should name exa.ai / parallel.ai explicitly and note session ID is sent to Parallel
- Enterprise wellknown config: validate that
remote_config.urlis on an expected domain before following it
Audit performed 2026-05-13. Sources: direct code inspection + automated deep-dive with Claude Opus.
Activity
[-]Security Audit: No backdoors found — telemetry/egress inventory (2026-05-13)[/-][+]Deep Security Audit: Data Leak Analysis — Verified Findings (2026-05-13)[/+]OpenCodeEngineer commentedon May 14, 2026
Resuming work on the deep security audit with a remediation pass.
Current focus:
wellknownremote-config resolution against token/header forwarding to unexpected hosts.I’ll post a follow-up with concrete code-level changes.
OpenCodeEngineer commentedon May 14, 2026
Remediation update (implemented locally, 2026-05-14):
What was hardened
packages/opencode/src/config/config.tswellknown remote_config.urlnow must be same-origin with the wellknown base URLwellknown remote_config.urlnow rejects embedded URL credentials (username/password)Tests added/updated
packages/opencode/test/config/config.test.tswellknown remote_config supports templated env vars in headers(same-origin remote URL)wellknown remote_config rejects cross-origin URLwellknown remote_config rejects URL with embedded credentialsValidation
cd packages/opencode && bun test test/config/config.test.ts→ 83 pass / 0 failcd packages/opencode && bun typecheck→ passThis reduces the previously reported enterprise remote-config token/header-forwarding risk by blocking cross-origin remote config fetches.
fix(app): recover when session URL points to missing session (#211)
dzianisv commentedon Jun 21, 2026
Reviewed. All findings are opt-in behaviors with proper gates:
share: 'auto'or manual trigger. Default OFF.No unintended data leaks found. This audit serves as documentation. Keeping open as reference.
dzianisv commentedon Jun 22, 2026
This is a reference document, not an actionable fix. All findings are opt-in behaviors with proper gates. Keeping as documentation in issue history.
fix(app): recover when session URL points to missing session (#211)