anomalyco / anomalyco/opencode
[SECURITY] "opencode upgrade" fetches a remote script and pipes it to bash with no integrity verification (curl|bash)
@jlongster is already working on this.
Since Aug 13, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
[SECURITY] opencode upgrade fetches a remote script and pipes it to bash with no integrity verification (curl|bash pattern)
Description
Description
- Severity: Medium (supply-chain / TOCTOU, full user privileges once action is confirmed)
- Affected: curl-based installs (
installation/index.ts:139-159, present atbaef5cd43band currentdev) - Related: #42022 (server-side request validation only; does not address the client fetch-and-exec)
Plugins
None required
OpenCode version
Observed in source at commits baef5cd43b and 743f6410f2, current dev branch (code-verified; see References below)
Steps to reproduce
- Run
opencode upgradeon a curl-based install (upgrade method resolves to `curl`). - Observe
GET https://opencode.ai/install→ response body piped tobashstdin,extendEnv: true, no checksum verification (packages/opencode/src/installation/index.ts:139-159). - Replace the endpoint content (local DNS/poisoned response) → script executes as the user with full privileges, no user review.
Screenshot and/or share link
N/A — verified against source (details and file:line references below).
Operating System
All (cross-platform; verified on macOS Darwin)
Details
The curl-install upgrade path does exactly the pattern the industry has been moving away from: GET https://opencode.ai/install and pipe the response body straight into a new bash process, with zero integrity verification.
const upgradeCurl = Effect.fnUntraced(function* (target: string) {
const response = yield* httpOk.execute(HttpClientRequest.get("https://opencode.ai/install"))
const body = yield* response.text
const bodyBytes = new TextEncoder().encode(body)
const proc = ChildProcess.make("bash", [], {
stdin: Stream.make(bodyBytes),
env: { VERSION: target },
extendEnv: true,
})
...
Impact
- TOCTOU: the script is fetched and executed as one opaque unit — the bytes the user might have seen in a previous run (or that were served when a previous install happened) are not the bytes that run now. No hash is pinned, computed, or checked.
- Full privileges: the script runs as the invoking user with
extendEnv: true, i.e. it inherits the shell environment (credentials,~/.config, SSH agent, etc.). - Single-gate security: the only control between "user said upgrade" and "arbitrary remote code executes" is the prompt. Any compromise of the install endpoint, a CDN/edge compromise, or a poisoned response is unbounded code execution with no user review of what actually runs.
VERSIONis passed as an env var but never constrains the executed script to that version — the script itself decides what to install.
Suggested fix
- Prefer installing from release artifacts (binary archives with published checksums) over executing a remote script.
- If the script must stay: pin its content hash in the client, verify
sha256of the fetched body before spawn, and refuse on mismatch. - At minimum, fetch → render the script for review / print its hash → require explicit confirmation before execution, and never
extendEnvsecrets into it.
Plugins
None required
OpenCode version
Observed in source at commits baef5cd43b and 743f6410f2, current dev branch (code-verified; file:line references in Details).
Screenshot and/or share link
N/A — verified against source (details and file:line references below).
Operating System
All (cross-platform; verified on macOS Darwin).
Steps to reproduce
- Run
opencode upgradeon a curl-based install (upgrade method resolves tocurl). - Observe
GET https://opencode.ai/install→ response body piped tobashstdin withextendEnv: trueand no checksum verification (packages/opencode/src/installation/index.ts:139-159). - Serve modified content for the endpoint (e.g. local DNS/poisoned response) → script executes as the user with full privileges; the user never reviewed the executed bytes.
Details
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.