GitHub Security Advisory: GHSA-gv8f-wpm2-m5wr
Package: @siteboon/claude-code-ui (NPM)
Severity: HIGH (CVSS 0.0)
CWE: CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection'), CWE-287: Improper Authentication, CWE-1188: Initialization of a Resource with an Insecure Default
Vulnerable versions: <= 1.24.0
Patched in: 1.25.0

Summary: @siteboon/claude-code-ui Vulnerable to Unauthenticated RCE via WebSocket Shell Injection

Details:
# Security Advisory: Insecure Default JWT Secret + WebSocket Auth Bypass Enables Unauthenticated RCE via Shell Injection
Download: [cve_claudecodeui_submission_v2.zip](https://github.com/user-attachments/files/25686652/cve_claudecodeui_submission_v2.zip)

##  Submission Info

| Field | Value |
|-------|-------|
| **Package** | `@siteboon/claude-code-ui` |
| **Ecosystem** | npm |
| **Affected versions** | `<= 1.24.0` (latest) |
| **Severity** | Critical |
| **CVSS Score** | 9.8 |
| **CVSS Vector** | `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` |
| **CWE** | CWE-1188, CWE-287, CWE-78 |
| **Reported** | 2026-03-02 |
| **Researcher** | Ethan-Yang (OPCIA) |

---

## Summary

Three chained vulnerabilities allow **unauthenticated remote code execution** on any
claudecodeui instance running with default configuration. No account, credentials, or
prior access is required.

The root cause of RCE is **OS command injection (CWE-78)** in the WebSocket shell
handler. Authentication is bypassed by combining an insecure default JWT secret
**(CWE-1188)** with a WebSocket authentication function that skips database user
validation **(CWE-287)**.

---

## Vulnerability Details

### 1. Insecure Default JWT Secret — `CWE-1188`

**File**: `server/middleware/auth.js`, line 6

```javascript
const JWT_SECRET = process.env.JWT_SECRET || 'claude-ui-dev-secret-change-in-production';
```

The server uses an environment variable for `JWT_SECRET`, but falls back to a
well-known default value when the variable is not set. Critically, `JWT_SECRET` is
**not included in `.env.example`**, so the majority of users deploy without setting it,
leaving the fallback value in effect.

Since this default string is published verbatim in the public source code, any attacker
can use it to sign arbitrary JWT tokens.

---

### 2. WebSocket Authentication Skips Database Validation — `CWE-287`

**File**: `server/middleware/auth.js`, lines 82–108

`authenticateWebSocket()` only verifies the JWT **signature**. It does **not** check
whether the `userId` in the payload actually exists in the database — unlike
`authenticateToken()` which is used for REST endpoints and does perform this check:

```javascript
// authenticateWebSocket() — VULNERABLE
const decoded = jwt.verify(token, JWT_SECRET);
return decoded;  // ← userId never verified against DB

// authenticateToken() — CORRECT (REST endpoints)
const decoded = jwt.verify(token, JWT_SECRET);
const user = userDb.getUserById(decoded.userId);  // ← DB check present
if (!user) return res.status(401)...
```

A forged token with a non-existent `userId` passes WebSocket authentication,
bypassing access control entirely.

---

### 3. OS Command Injection via WebSocket Shell — `CWE-78`

**File**: `server/index.js`, line 1179

```javascript

shellCommand = `cd "${projectPath}" && ${initialCommand}`;
```

Both `projectPath` and `initialCommand` are taken directly from the WebSocket message
payload and interpolated into a bash command string without any sanit