Back to skill

Security audit

OpenClaw Dashboard

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real local OpenClaw dashboard, but confirmed security bugs could expose sensitive local operational data if the dashboard is shared or compromised.

Review before installing. Keep it bound to loopback with a strong OPENCLAW_AUTH_TOKEN, do not expose it to untrusted networks, and leave Copilot/config-detail features disabled unless needed. The SQL injection, cron file path handling, and DOM XSS issues should be fixed before use in any shared, remote, or multi-user environment.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
backend/providers/ledger.js:110
Finding

Authenticated SQL Injection in Ledger Drift Endpoint

Content
View full analysis
= date('now', 'localtime', '-${days} days') GROUP BY day ORDER BY day `); jsonReply(res, 200, { provider, days, rows }); } ``` The affected route is registered as follows: ```js router.add('GET', '/api/ledger/drift', (req, res, q) => handleDrift(req, res, q)); ``` ### Technical Analysis The `provider` query parameter is copied directly into a single-quoted SQL expression without parameter binding, escaping, or allowlist validation: ```sql WHERE provider = '${provider}' ``` Although `sqliteJson()` invokes `sqlite3` through `execFileSync()` with an argument array, that only prevents shell metacharacter interpretation. It does not prevent SQL injection because the constructed string is still parsed as SQL by SQLite. An authenticated caller can terminate the `provider` string and alter the query. Depending on the injected statement and SQLite CLI behavior, this can include `UNION`-based extraction of other database contents or execution of additional statements. The `days` parameter is passed through `parseInt()`, which reduces direct SQL injection risk for that field, but it is not checked for finiteness or bounded to a reasonable range. ### Attack Path 1. The attacker obtains a valid dashboard Bearer token or an authenticated browser context. 2. The attacker sends a request to `/api/ledger/drift` with a crafted ` ...[truncated 1516 chars]
Remediation
View remediation
= date('now', 'localtime', ?) GROUP BY day ORDER BY day `).all(provider, `-${days} days`); ``` 2. If continued use of the `sqlite3` CLI is unavoidable, apply a strict allowlist to `provider`. Do not attempt to secure arbitrary input using only quote replacement. ```js const ALLOWED_PROVIDERS = new Set([ 'anthropic', 'openai', 'google', 'ollama', ]); if (!ALLOWED_PROVIDERS.has(provider)) { return errorReply(res, 400, 'Unsupported provider'); } ``` 3. Validate and clamp `days`: ```js const parsedDays = Number.parseInt(query.days || '30', 10); if (!Number.isInteger(parsedDays) || parsedDays < 1 || parsedDays > 365) { return errorReply(res, 400, 'Invalid days value'); } ``` 4. Open the ledger database in read-only mode for dashboard queries where supported. 5. Add regression tests that submit quotes, comments, `UNION`, and statement separators through `provider` and verify that each is rejected without reaching SQLite. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
backend/providers/cron.js:28
Finding

Authenticated Path Traversal and Unbounded File Read in Cron Run Loader

Content
View full analysis
{ const ta = Date.parse(a.finishedAt || a.startedAt || 0); const tb = Date.parse(b.finishedAt || b.startedAt || 0); return tb - ta; }); return runs.slice(0, limit); } catch { return []; } } ``` The handler accepts `jobId` without validating its format or resolved path: ```js function handleCronRuns(req, res, query) { const jobId = query.jobId; if (!jobId) return jsonReply(res, 400, { error: 'jobId re ...[truncated 2893 chars]
Remediation
View remediation
100) { return jsonReply(res, 400, { error: 'Invalid limit' }); } ``` 5. Do not read the entire file synchronously. Stream the file with a strict byte ceiling, or implement a bounded tail reader because only the most recent records are needed. 6. Reject files that exceed an operationally justified size before parsing. 7. Add tests for: - `../` traversal - URL-encoded traversal - absolute path attempts - valid and invalid job identifiers - excessively large and negative limits - oversized JSONL files ]]>

T09 · Insecure Skill Coding Practices

Error
Location
frontend/tabs/overview.js:61
Finding

Stored DOM XSS Through Unescaped Model Metadata

Content
View full analysis
mNorm.includes(normModelStr(k))); if (key) return MODEL_COLORS[key]; if (!mNorm) return '#6b7280'; return _hashColor(mNorm); } function shortModel(m) { const raw = (m || '').replace(/-preview$/, ''); if (!raw || raw === 'unknown') return 'unknown'; const rawNorm = normModelStr(raw); const pair = MODEL_DISPLAY_NAMES.find( ([pattern]) => rawNorm.includes(normModelStr(pattern)) ); return pair ? pair[1] : raw.replace(/^[^/]+\//, '') .replace(/[-_]/g, ' ') .replace(/\.gguf$/i, '') .trim(); } ``` The returned value is inserted into `innerHTML`, including an HTML attribute context: ```js barHtml += `
`; barHtml += `● ${shortModel(m)} ` + `${pct}%`; mixEl.innerHTML = barHtml; ``` Another active sink renders `topModel` without escaping: ```js ${r.topModel ? `
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (55)

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The declared description frames the skill as an operations dashboard, but the content explicitly includes read access to workspace files, skill listings, and configuration/file-browsing endpoints. This mismatch can conceal sensitive local-data exposure behind an innocuous dashboard label, causing users to authorize or install a skill without understanding that it can surface files, configs, and operational metadata over HTTP.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

The declared description frames the skill as an operations dashboard, but the content explicitly includes read access to workspace files, skill listings, and configuration/file-browsing endpoints. This mismatch can conceal sensitive local-data exposure behind an innocuous dashboard label, causing users to authorize or install a skill without understanding that it can surface files, configs, and operational metadata over HTTP.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description frames the skill as an operations dashboard, but the content explicitly includes read access to workspace files, skill listings, and configuration/file-browsing endpoints. This mismatch can conceal sensitive local-data exposure behind an innocuous dashboard label, causing users to authorize or install a skill without understanding that it can surface files, configs, and operational metadata over HTTP.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description frames the skill as an operations dashboard, but the content explicitly includes read access to workspace files, skill listings, and configuration/file-browsing endpoints. This mismatch can conceal sensitive local-data exposure behind an innocuous dashboard label, causing users to authorize or install a skill without understanding that it can surface files, configs, and operational metadata over HTTP.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 68)May include surrounding context.

md
| `sessions.js` | `/ops/sessions`, `/api/sessions` | Session stats + model |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 73)May include surrounding context.

md
| `system.js` | `/ops/system` | Host metrics (CPU/RAM/disk) |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 107)May include surrounding context.

md
1. **No duplicate function definitions** across `api.js` and `ui-utils.js`. Shared utilities belong in `api.js` (loaded first). `ui-utils.js` only holds `timeSi

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 182)May include surrounding context.

md
1. **No duplicate function definitions** across `api.js` and `ui-utils.js`. Shared utilities belong in `api.js` (loaded first). `ui-utils.js` only holds `timeSi

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 109)May include surrounding context.

md
3. **Runtime data goes to `~/.openclaw/dashboard/`**, not skill root. Path is set in `backend/lib/config.js` via `OPENCLAW_DASHBOARD_TASKS` env or default.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 183)May include surrounding context.

md
3. **Runtime data goes to `~/.openclaw/dashboard/`**, not skill root. Path is set in `backend/lib/config.js` via `OPENCLAW_DASHBOARD_TASKS` env or default.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The inline documentation says the proxy paths are "GET only" and that write paths were removed, conveying a read-only intent for dashboard access. However, hubProxy explicitly forwards req.method and pipes the request body upstream, so if it is reused or exposed on non-GET routes it performs full transparent proxying rather than a read-only fetch.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · backend/providers/tasks.js (reported line 17)May include surrounding context.

js
// ── Task routes (read-only) ─────────────────────────────────────────
// Dashboard is read-only. Task writes (create/update/delete/spawn) go via Discord or CLI.
// Removed: POST /tasks, PATCH /tasks/:id, DELETE /tasks/:id,
//          POST /tasks/:id/notes, POST /tasks/:id/spawn
function register(router) {
  // List tasks

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · frontend/index.html (reported line 31)May include surrounding context.

html
<img src="/icon.svg" alt="" class="nav-brand-mark">
      <div class="nav-brand-copy">
        <div class="logo">OpenClaw</div>
        <div class="logo-sub">Operations Dashboard</div>
      </div>
    </div>
    <div class="nav-section-label">Workspace</div>
    <nav class="tab-bar">
      <button class="tab-btn active" data-tab="overview" data-title="Overview" data-title-zh="总览" data-description="System activity and attention signals" data-description-zh="系统活动与待处理信号">
        <svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
        <span id="tabOverviewLabel">Overview</span>
      </button>
      <button class="tab-btn" data-tab="copilot" data-title="Copilot" data-title-zh="会议助手" data-description="Realtime transcript, insight, and retrieval" data-descript

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · frontend/index.html (reported line 68)May include surrounding context.

html
</aside>

  <div class="shell-main">
  <!-- Header -->
  <header class="topbar">
    <div class="page-heading">
      <div class="breadcrumb"><span>OpenClaw</span><span aria-hidden="true">›</span><strong id="pageTitle">Overview</strong></div>

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · frontend/index.html (reported line 155)May include surrounding context.

html
</div>
  </div>

  <!-- System Info Bar (fixed, always visible) -->
  <div id="systemInfoBar" class="glass-card" style="padding:10px 16px;margin-bottom:12px;font-size:.75rem;display:none">
    <div style="display:flex;flex-wrap:wrap;gap:6px 16px;align-items:center" id="systemInfoContent"></div>
  </div>

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · frontend/index.html (reported line 167)May include surrounding context.

html
</div>
  </div>

  <!-- Today's Cron Runs -->
  <div class="agent-sessions-panel" id="cronRunsPanel">
    <div class="agent-sessions-header" onclick="this.classList.toggle('collapsed');this.nextElementSibling.style.maxHeight=this.classList.contains('collapsed')?'0':'400px'">
      <div class="agent-sessions-title">

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · frontend/index.html (reported line 338)May include surrounding context.

html
<div class="card-sub">DGX Spark Agent — 任务调度 · GPU 利用率 · PR Hunter</div>
    </div>

    <!-- Today Summary Row -->
    <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin-bottom:16px" id="sparkSummaryCards">
      <div class="glass-card" style="padding:14px;text-align:center">
        <div style="font-size:1.6rem;font-weight:700;color:var(--green)" id="sparkTasksDone">—</div>

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 32)May include surrounding context.

md
cd "$SCRIPT_DIR"

NODE_ARGS=()
if [[ -f .env ]]; then
  NODE_ARGS+=(--env-file=.env)
fi

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · start.sh (reported line 8)May include surrounding context.

sh
cd "$SCRIPT_DIR"

NODE_ARGS=()
if [[ -f .env ]]; then
  NODE_ARGS+=(--env-file=.env)
fi

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This markdown file uses a single forced language throughout and does not indicate that the user can choose another language or that the content is intentionally limited to a Chinese-speaking audience. Under the natural-language policy rules, forcing a specific language without opt-in is a locale policy concern.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · CHANGELOG.md (reported line 274)May include surrounding context.

md
- Added system + workspace skills scan (56 skills)
- Added mobile PWA support
- Added Cron Timeline + Vision Ingestion panels
- Added macOS LaunchAgent plist example
- Replaced Kanban tasks with operational monitoring

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises capabilities that imply access to environment variables, network services, and shell-driven installation/runtime behavior, but it does not declare an explicit tool or permission scope. That creates an authorization and review gap: operators and automated policy engines cannot easily constrain what the skill is allowed to do, increasing the chance of over-privileged execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The code opens a WebSocket connection to an external DashScope endpoint and authenticates with an API key, after which meeting audio and resulting transcripts are processed by that provider. There is no confirmation prompt or user-facing warning in this file that meeting content will leave the local system and be transmitted to a third party.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The hard-coded instruction string requires the model to act and respond in Chinese formatting and wording, which imposes a specific language/locale choice on all users. The file does not offer a language selection mechanism or explain a region-specific reason for this constraint.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The status endpoint reports channelIsolation: true unconditionally, but the websocket flow can still enable legacyCompat and subscribe/publish to shared legacy channels such as meeting.transcript, meeting.rag_hits, and meeting.insights. This creates a security-signaling mismatch that can cause operators or dependent components to assume per-meeting isolation when some sessions may still receive or emit data on shared channels, increasing the risk of cross-meeting data exposure.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/test-dashboard.js:219

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/test-dashboard.js:222