Back to skill

Security audit

RTS Dashboard

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real OpenClaw dashboard, but it exposes private conversations and powerful Gateway controls with weak scoping and no visible authentication.

Review before installing. Run this only in a trusted local environment, assume anyone who can reach the local dashboard port may read OpenClaw conversation content or trigger Gateway actions, and prefer a version that adds authentication, strict origin checks, transcript opt-in/redaction, safer shutdown controls, input validation, and an updated ws dependency.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server.js:620
Finding
Unauthenticated disclosure of agent conversations and operational metadata<![CDATA[ ## Vulnerability Details **File Location**: `server.js:620-680`, `server.js:811-814` **Vulnerability Type**: Missing authentication, permissive CORS, and unrestricted WebSocket access **Risk Level**: High ### Vulnerable Code ```js if (req.url === '/api/state') { try { const data = await collectAllData(); res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); res.end(JSON.stringify(data)); } catch (e) { res.writeHead(500); res.end(JSON.stringify({ error: e.message })); } return; } if (req.url.startsWith('/api/session-history?')) { const params = new URL(req.url, 'http://localhost').searchParams; const agent = params.get('agent'); const sessionId = params.get('sessionId'); res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Access-Control-Allow-Origin': '*' }); if (!agent || !sessionId) { res.end(JSON.stringify({ error: 'Missing agent or sessionId' })); return; } try { const jsonlPath = path.join( OPENCLAW_HOME, 'agents', agent, 'sessions', sessionId + '.jsonl' ); const content = fs.readFileSync(jsonlPath, 'utf8'); const lines = content.trim().split('\n'); const messages = []; for (const line of lines) { try { const entry = JSON.parse(line); const role = entry.role || (entry.message && entry.message.role); const rawContent = entry.content || (entry.message && entry.message.content); if ((role === 'user' || role === 'assistant') && rawContent) { let text = ''; if (typeof rawContent === 'string') { text = rawContent; } else if (Array.isArray(rawContent)) { for (const part of rawContent) { if (part.type === 'text' && part.text) { text += part.text + '\n'; } } } if (text.trim()) { ...[truncated 2853 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate a cryptographically random dashboard token at startup and require it on every API and WebSocket request. 2. Replace wildcard CORS with an explicit allowlist, such as the exact dashboard origin. 3. Validate the `Origin` header during WebSocket upgrades and reject unknown or absent origins. 4. Apply per-resource authorization so a caller can access only explicitly permitted agents and sessions. 5. Do not expose complete transcripts by default. Require an explicit, authenticated user action. 6. Minimize `/api/state` output by removing unnecessary hostname, session, and conversation fields. 7. Add security headers, including a restrictive Content Security Policy. 8. Treat localhost services as potentially reachable by hostile browser content and local processes. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server.js:682
Finding
Unauthenticated Gateway stop and restart operations<![CDATA[ ## Vulnerability Details **File Location**: `server.js:682-701` **Vulnerability Type**: Missing authorization for administrative service-control operations **Risk Level**: High ### Vulnerable Code ```js if (req.url === '/api/gateway/restart' && req.method === 'POST') { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); try { const { execSync } = require('child_process'); execSync('openclaw gateway restart', { timeout: 15000, stdio: 'pipe' }); res.end(JSON.stringify({ ok: true, action: 'restart', message: '网关重启成功' })); } catch (e) { res.end(JSON.stringify({ ok: false, action: 'restart', message: e.message })); } return; } if (req.url === '/api/gateway/stop' && req.method === 'POST') { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); try { const { execSync } = require('child_process'); execSync('openclaw gateway stop', { timeout: 15000, stdio: 'pipe' }); res.end(JSON.stringify({ ok: true, action: 'stop', message: '网关已关闭' })); } catch (e) { res.end(JSON.stringify({ ok: false, action: 'stop', message: e.message })); } return; } ``` ### Technical Analysis The dashboard exposes administrative endpoints that execute fixed service-control commands under the OS identity of the server process. Neither endpoint authenticates the caller, verifies authorization, validates the request origin, or requires an anti-CSRF token. Although the commands are fixed and do not create a direct shell-injection vulnerability, exposing them through an unauthenticated HTTP interface creates an unauthorized privilege boundary crossing. A monitoring dashboard does not require unrestricted remote service-control privileges for its core visualization functionality. The wildcard CORS response and perm ...[truncated 1053 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove Gateway lifecycle controls unless they are essential to the declared dashboard functionality. 2. Require authenticated administrator authorization for every control operation. 3. Add CSRF protection and strict request-origin validation. 4. Require an explicit user confirmation immediately before stopping or restarting the Gateway. 5. Replace shell-based `execSync` with `execFile` or `spawn`, using a fixed executable and fixed argument array. 6. Rate-limit lifecycle actions and log the authenticated caller, timestamp, and result. 7. Return appropriate HTTP status codes instead of always returning status 200. 8. Run the dashboard under a dedicated, least-privileged OS account where practical. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
server.js:707
Finding
Unauthenticated agent-message injection through a privileged Gateway identity<![CDATA[ ## Vulnerability Details **File Location**: `server.js:707-794` **Vulnerability Type**: Confused-deputy privilege escalation and missing authorization **Risk Level**: Critical ### Vulnerable Code ```js if (req.url === '/api/chat' && req.method === 'POST') { let body = ''; req.on('data', c => body += c); req.on('end', () => { res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' }); try { const { agent, sessionId, message } = JSON.parse(body); if (!agent || !message) { res.end(JSON.stringify({ ok: false, error: '缺少 agent 或 message' })); return; } const WsClient = require('ws'); const gwWs = new WsClient( `ws://127.0.0.1:${GATEWAY_PORT}/webchat/ws`, { origin: 'http://127.0.0.1:4320' } ); let phase = 'connecting'; gwWs.on('message', data => { try { const msg = JSON.parse(data.toString()); if ( msg.type === 'event' && msg.event === 'connect.challenge' ) { phase = 'sending-connect'; const nonce = msg.payload?.nonce || ''; const connectParams = { minProtocol: 3, maxProtocol: 3, client: { id: 'openclaw-control-ui', version: '1.0', platform: os.platform(), mode: 'webchat' }, role: 'operator', scopes: [ 'operator.admin', 'operator.read', 'operator.write' ], caps: [] }; const token = getGatewayToken(); if (token) connectParams.auth = { token }; connectParams.device = signConnectChallenge(nonce, connectParams); gwWs.send(JSON.stringify({ type: 'req', id: 'c-' + Date ...[truncated 3237 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require strong authentication for `/api/chat`. 2. Authorize the caller for the selected agent and session before opening a Gateway connection. 3. Remove wildcard CORS and enforce an exact trusted origin. 4. Add CSRF tokens for browser-originated state-changing requests. 5. Request only the minimum Gateway scope required for `chat.send`; do not request administrator scope unless strictly necessary. 6. Require interactive confirmation showing the target agent, session, and message. 7. Validate agent and session identifiers against an authorized server-side list. 8. Enforce message length and HTTP body-size limits. 9. Add rate limiting and audit logging. 10. Isolate Gateway credentials from unauthenticated request handlers and avoid using a broadly privileged identity as a generic request proxy. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
public/index.html:1307
Finding
Stored DOM cross-site scripting through unescaped OpenClaw metadata<![CDATA[ ## Vulnerability Details **File Location**: `public/index.html:1307-1314`, `public/index.html:1354-1358`, `public/index.html:1402-1405`, `public/index.html:1522-1546` **Vulnerability Type**: Stored DOM cross-site scripting **Risk Level**: High ### Vulnerable Code ```js agentDiv.innerHTML = ` <div class="agent-status"> <span>${agent.name || agent.id}</span> <span class="${statusClass}">${statusText}</span> </div> ${isActive && session?.currentTask ? `<div class="agent-task">${truncateText(session.currentTask, 50)}</div>` : ''} ${isActive ? '<div class="pulse-bar"></div>' : ''} `; ``` ```js const chTag = sess.channel && sess.channel !== '--' ? `·${sess.channel}` : ''; div.innerHTML = ` <div class="status-dot"></div> <span class="icon">🖥️</span> <span class="label">${sess.agent}${chTag}·${timeText}</span> `; ``` ```js skillDiv.innerHTML = ` <span>${skill.name || skill.id}</span> <span class="skill-category ${categoryClass}"> ${categoryName} </span> `; ``` ```js const skillsList = session?.skills && session.skills.length > 0 ? session.skills .map(s => '<span class="skill-tag">' + s + '</span>') .join('') : '<span style="color:#667788;font-size:11px">无</span>'; infoContainer.innerHTML = ` <div class="detail-section"> <div class="detail-header">📋 基本信息</div> <div class="detail-grid"> <div class="detail-item"> <span class="detail-label">名称</span> <span class="detail-value">${agent.name || agent.id}</span> </div> <div class="detail-item"> <span class="detail-label">模型</span> <span class="detail-value">${agent.model || '--'}</span> </div> <div class="detail-item"> <span class="detail-label">渠道</span> <span class="detail-value">${session ? session.channel : '--'}</span> </div> </div> </div> <div class="detail-section"> <div class="detail-header">🔧 已部署技能</div> <div class= ...[truncated 2063 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace dynamic `innerHTML` construction with DOM creation APIs and assign untrusted values through `textContent`. 2. Where HTML templates are unavoidable, apply context-appropriate escaping to every dynamic value. 3. Escape agent IDs, names, models, channels, tasks, and skill names consistently. 4. Do not rely on truncation as sanitization; truncating a string does not neutralize HTML. 5. Add a restrictive Content Security Policy that blocks inline scripts and inline event handlers. 6. Validate metadata fields when they are loaded and reject unexpected control characters or markup. 7. Review every remaining `innerHTML` assignment and distinguish constant markup from untrusted content. 8. Add automated tests using payloads in every server-provided field. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
server.js:630
Finding
Path traversal in the session-history file lookup<![CDATA[ ## Vulnerability Details **File Location**: `server.js:630-646` **Vulnerability Type**: Path traversal and insufficient input validation **Risk Level**: Medium ### Vulnerable Code ```js if (req.url.startsWith('/api/session-history?')) { const params = new URL(req.url, 'http://localhost').searchParams; const agent = params.get('agent'); const sessionId = params.get('sessionId'); res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8', 'Access-Control-Allow-Origin': '*' }); if (!agent || !sessionId) { res.end(JSON.stringify({ error: 'Missing agent or sessionId' })); return; } try { const jsonlPath = path.join( OPENCLAW_HOME, 'agents', agent, 'sessions', sessionId + '.jsonl' ); if (!fs.existsSync(jsonlPath)) { res.end(JSON.stringify({ messages: [], error: 'Session file not found' })); return; } const content = fs.readFileSync(jsonlPath, 'utf8'); ``` ### Technical Analysis The `agent` and `sessionId` query parameters are used as path components without validating their syntax. `path.join` normalizes `..` components rather than rejecting them, so crafted values may resolve outside the intended agent session directory. The forced `.jsonl` suffix and subsequent JSON-line parsing constrain the files that can be meaningfully disclosed, but they do not establish a safe containment boundary. A traversal payload can target other readable JSONL files reachable by the dashboard process. The endpoint is also unauthenticated, increasing practical exposure. ### Attack Path 1. An attacker sends a request to `/api/session-history`. 2. The attacker supplies an `agent` or `sessionId` value containing encoded or literal `..` path segments. 3. The server passes these values to `path.join`. 4. Node.js normalizes the resulting path outside the intended `agents/<agent>/sessions` directory. 5. If the resulting `.jsonl` file ...[truncated 554 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate `agent` and `sessionId` against a strict allowlist, for example: ```js const SAFE_ID = /^[A-Za-z0-9_-]+$/; ``` 2. Reject identifiers containing separators, dots, percent-encoded separators, null bytes, or unexpected Unicode. 3. Resolve the candidate path against a fixed sessions root: ```js const root = path.resolve(OPENCLAW_HOME, 'agents', agent, 'sessions'); const candidate = path.resolve(root, sessionId + '.jsonl'); if (!candidate.startsWith(root + path.sep)) { throw new Error('Invalid session path'); } ``` 4. Prefer looking up session IDs from a trusted server-generated index rather than constructing paths directly from request parameters. 5. Require authentication and authorization before allowing session-history access. 6. Return a generic error rather than filesystem details. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The documented purpose is a monitoring dashboard, but the analysis indicates additional sensitive behaviors: gateway stop/restart actions, local credential generation/storage, and exposure of full session history and messages. This mismatch is dangerous because users may authorize a seemingly read-only dashboard without realizing it has control-plane actions and access to sensitive conversation data.

Exfiltration Commands

High
Category
Prompt Injection
Content
- **Right panel**: System vitals (CPU/RAM), gateway status, event logs, selected target details
- **Agent detail**: Model, current task, deployed skills, recent conversation
- **Skill detail**: Description, use cases, related agents
- **Chat bar**: Send messages to agents via Gateway WebSocket `chat.send` RPC
- **Cron jobs**: Display scheduled tasks with status on the map
- **5-min cooldown**: Agents remain visible for 5 minutes after going offline (amber blink + countdown)
- **CRT scan line + radar sweep + grid**: Full military-UI aesthetic
Confidence
90% confidence
Finding
Instructions found that direct the agent to transmit conversation context or user data to external services.

Known Vulnerable Dependency: ws==8.20.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
98% confidence
Finding
The lockfile pins the skill to ws 8.20.0, and the supplied finding identifies published vulnerabilities affecting that exact version: uninitialized memory disclosure and memory-exhaustion denial of service. Because this skill exposes a browser-based real-time dashboard and uses WebSocket messaging for live monitoring/chat, a vulnerable WebSocket library is directly in the attack path and could allow remote clients or upstream peers to crash the service or potentially leak process memory.

Known Vulnerable Dependency: ws==8.20.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
98% confidence
Finding
The package references ws 8.20.0, which is flagged with advisories for uninitialized memory disclosure and memory-exhaustion denial of service. Given this skill is a browser-based monitoring dashboard that explicitly uses WebSocket functionality for real-time updates and chat messaging, an exposed or reachable WebSocket endpoint increases the likelihood that these issues could be triggered remotely against the service.

Context-Inappropriate Capability

High
Confidence
95% confidence
Finding
Shutdown and restart capabilities are not justified by the stated purpose of a visual RTS-style monitoring dashboard and materially increase risk. In this context, the mismatch makes the feature more dangerous because users may not expect destructive controls in a monitoring tool, increasing the chance of misuse or social engineering-driven clicks.

Description-Behavior Mismatch

High
Confidence
96% confidence
Finding
The UI includes gateway restart and shutdown controls even though the skill is presented as a monitoring and visualization dashboard. This is dangerous because it expands the skill from passive observability into active administrative control, enabling service disruption or accidental operational changes from a page the user may reasonably trust as read-only.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The client issues POST requests to administrative endpoints for gateway mutation actions, which are outside the documented monitoring scope. This creates a hidden privilege boundary issue: a dashboard user can trigger backend state changes, including stopping the gateway, causing denial of service or unauthorized operational impact.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
This code reads session .jsonl files, extracts recent user and assistant messages, and includes them in the dashboard state returned over HTTP/WebSocket. That goes beyond operational monitoring metadata and exposes potentially sensitive conversation content from other sessions to anyone who can access the dashboard service.

Ssd 3

High
Confidence
98% confidence
Finding
The code collects recent user and assistant messages from session logs and emits them in plain text as part of the live session objects. This is sensitive data disclosure because the dashboard becomes a passive transcript viewer for potentially private prompts, outputs, and embedded secrets.

Missing User Warnings

High
Confidence
92% confidence
Finding
The service exposes sensitive session content through HTTP/WebSocket interfaces without any visible warning, consent flow, or disclosure that conversation data will be shown. In a monitoring tool, silent exposure of message content increases the likelihood of inadvertent privacy violations and unauthorized observation.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The /api/session-history endpoint returns full chat transcripts by reading raw session logs and serializing user/assistant content back to the client. This creates a direct data-exfiltration path for conversation history that is inconsistent with a dashboard described as a monitoring UI.

Ssd 3

High
Confidence
99% confidence
Finding
By returning complete message transcripts from session log files, the endpoint discloses user-provided content and assistant responses in full. This can leak secrets, personal data, proprietary prompts, or internal operational context to any caller who can access the dashboard.

Missing User Warnings

High
Confidence
96% confidence
Finding
This endpoint returns complete transcripts without any access warning, consent checkpoint, or narrowing to metadata-only views. That materially increases privacy and confidentiality risk because operators may retrieve full user content from arbitrary sessions with no friction or safeguards.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The POST endpoints can stop or restart the OpenClaw gateway by invoking subprocess commands, with no authentication or authorization checks visible in this server. Any local webpage or script able to reach the service could trigger disruption of the platform's control plane.

Context Leakage

High
Category
Data Exfiltration
Content
return;
              }
              phase = 'connected';
              // Now send chat message
              const sessionKey = sessionId ? `agent:${agent}:${sessionId}` : `agent:${agent}:main`;
              const chatId = 'm-' + Date.now();
              const idempotencyKey = 'rts-' + Date.now() + '-' + Math.random().toString(36).substring(2, 8);
Confidence
85% confidence
Finding
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.

Natural-Language Policy Violations

Medium
Confidence
84% confidence
Finding
The primary descriptive content is presented in Chinese, and the README does not indicate that other language options are available. This can violate a language/locale policy when users are not given a choice or warned that the skill documentation is language-specific.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill instructs the agent to use environment-dependent behavior and to start services, install packages, and read configuration, but it declares no explicit tool scope or permission boundaries. This increases the chance the skill is invoked with broader-than-necessary capabilities, making unintended filesystem, process, and configuration access more likely.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The invocation guidance is broad enough that common user requests to 'open' or 'launch' a dashboard could trigger package installation, background process creation, and service startup. Overbroad activation raises the risk of the agent taking impactful actions without the user understanding that software will be installed or a persistent service started.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The stop instruction forcefully terminates any process bound to port 4320 without warning, ownership checks, or graceful shutdown. This can kill unrelated processes and cause data loss or service disruption, especially if another application is using that port.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The skill states that it generates and stores device keys locally for authentication but provides no user-facing warning or consent flow. Silent creation of persistent credentials can expand trust relationships and leave sensitive authentication material on disk without the user's informed approval.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- On first launch, generates a keypair and saves to `.device-keys.json`
- Signs each `connect.challenge` nonce using v3 protocol
- Gateway token is auto-discovered from `OPENCLAW_GATEWAY_TOKEN` env var or `gateway.auth.token` in `openclaw.json`
- Localhost connections are auto-approved by Gateway (no manual pairing needed)
- **No `dangerouslyDisableDeviceAuth` or `allowInsecureAuth` required**

## Cross-Platform
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The document declares `lang="zh-CN"`, and the visible UI text and time formatting are consistently fixed to Chinese/China locale. The policy requires flagging language or locale constraints when they are forced without user opt-in, and this file does not offer any language selection or explain that the skill is region-specific.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
The dashboard retrieves and displays session chat history and allows message sending without any visible disclosure about sensitivity, retention, or operator access. This is risky because conversation content may contain secrets or private data, and the UI normalizes access to that content without warning or access-scoping cues.

Missing User Warnings

Medium
Confidence
82% confidence
Finding
The gateway control flow performs destructive backend actions with lightweight inline confirmation but no robust safeguard such as re-authentication, typed confirmation, or permission checks visible in the client. That makes accidental or coerced shutdown/restart easier, especially in a tool framed primarily as a dashboard.

Context-Inappropriate Capability

Medium
Confidence
86% confidence
Finding
The skill generates and persists a device private key used for gateway operator authentication in a local JSON file. While not inherently malicious, storing long-lived operator credentials on disk increases the attack surface: compromise of the dashboard host or file permissions could let an attacker impersonate an operator to the gateway.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.exposed_secret_literal

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
server.js:25

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
server.js:110