Back to skill

Security audit

Clawculator

Security checks for vulnerabilities and agentic risk

Overview

This is a relevant OpenClaw cost analyzer, but it under-discloses sensitive local data access and has output paths that can expose configuration, identifiers, or third-party network metadata.

Review carefully before installing. The cost-analysis goal is legitimate, but the skill should more clearly disclose transcript and web-chat scanning, avoid returning raw config in JSON, redact identifiers consistently, remove external font loads, and narrow triggers. Do not share generated reports without checking for sensitive names, IDs, tokens, or commands, and avoid running the optional dashboard or unpinned npx examples on sensitive systems until these issues are fixed.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (8)

T09 · Insecure Skill Coding Practices

Error
Location
analyzer.js:965
Finding
Full OpenClaw Configuration and Credentials Exposed Through JSON Output<![CDATA[ ## Vulnerability Details **File Location**: `analyzer.js:965-975`, `run.js:79-83` **Vulnerability Type**: Sensitive configuration disclosure **Risk Level**: High ### Vulnerable Code ```js return { scannedAt: new Date().toISOString(), configPath, sessionsPath, primaryModel: configResult.primaryModel, findings: allFindings, summary: { // ... }, sessions: sessionResult.sessions || [], webChatSessions, config: configResult.config, }; ``` ```js if (flags.json) { console.log(JSON.stringify(analysis, null, 2)); process.exit(0); } ``` ### Technical Analysis The analyzer includes the complete parsed `openclaw.json` object in its result. The JSON output mode then serializes the entire result to standard output without redaction. The configuration is known to contain sensitive properties because the analyzer itself examines fields such as Telegram `botToken`, Discord `token`, Signal phone numbers, hook tokens, channel policies, and other operational settings. Returning the source configuration is unnecessary for cost analysis and exceeds the minimum data required by the report. No direct external exfiltration is implemented in this path. Nevertheless, credentials can be exposed through terminal history, command logs, redirected output, CI logs, agent transcripts, or downstream programs consuming the JSON. ### Attack Path 1. The victim stores API keys, bot tokens, or other credentials in `openclaw.json`. 2. The victim or an automated agent invokes `node run.js --json`. 3. `runAnalysis()` returns the complete source configuration as `analysis.config`. 4. `JSON.stringify()` writes the configuration and credentials to standard output. 5. A log collector, redirected file, AI-agent transcript, or downstream process captures the secrets. 6. Anyone with access to that output can reuse exposed credentials according to their original privileges. ### Impact Assessment An attacker obtaining the output may gain access to messagi ...[truncated 283 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `config: configResult.config` from the returned analysis object. - Construct a minimal result containing only fields needed for cost reporting. - Implement recursive redaction as a defense-in-depth measure for keys matching patterns such as `token`, `secret`, `password`, `apiKey`, `credential`, `authorization`, and private-key material. - Ensure all output modes operate on a sanitized view rather than the raw analysis object. - Add automated tests using representative secret-bearing configurations and assert that no secret appears in terminal, Markdown, JSON, HTML, or snapshot output. - Warn users that older reports and logs may already contain credentials and recommend rotating any exposed tokens. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
analyzer.js:585
Finding
Undisclosed Broad Access to All Agent and Web-Chat Transcripts<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:16-25`, `analyzer.js:521-523`, `analyzer.js:585-618`, `analyzer.js:875-905` **Vulnerability Type**: Excessive filesystem access and least-privilege violation **Risk Level**: Medium ### Vulnerable Code The declared access scope is limited: ```markdown **Files this skill reads:** - `~/.openclaw/openclaw.json` — your OpenClaw config - `~/.openclaw/agents/main/sessions/sessions.json` — session token usage - `~/clawd/` — workspace root file count only (no file contents read) - `/tmp/openclaw` — log directory (read only, if present) ``` The implementation reads complete transcript files: ```js function parseTranscript(jsonlPath) { try { const content = fs.readFileSync(jsonlPath, 'utf8').trim(); if (!content) return null; ``` It also enumerates every agent and web-chat transcript directory: ```js function discoverAgentDirs() { const openclawHome = process.env.OPENCLAW_HOME || path.join(os.homedir(), '.openclaw'); const agentsDir = path.join(openclawHome, 'agents'); const dirs = []; try { for (const agent of fs.readdirSync(agentsDir)) { const sessionsDir = path.join(agentsDir, agent, 'sessions'); if (fs.existsSync(sessionsDir) && fs.statSync(sessionsDir).isDirectory()) { dirs.push({ agent, sessionsDir }); } } } catch { /* agents dir doesn't exist */ } return dirs; } function discoverWebChatSessions() { const openclawHome = process.env.OPENCLAW_HOME || path.join(os.homedir(), '.openclaw'); const webChatDir = path.join(openclawHome, 'web-chat'); const sessions = []; try { for (const file of fs.readdirSync(webChatDir)) { if (file.endsWith('.jsonl')) { sessions.push(path.join(webChatDir, file)); } } } catch { /* web-chat dir doesn't exist */ } return sessions; } ``` ### Technical Analysis The normal analysis path scans additional agent directories, untracked or deleted transcript files, and web-chat transcr ...[truncated 1301 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Restrict the default scan to the explicitly selected main-agent metadata. - Require separate opt-in flags for multi-agent, untracked-session, and web-chat scans. - Update `SKILL.md` to disclose every directory and file type that may be accessed. - Parse transcripts as streams and retain only usage records instead of loading complete files. - Where supported, obtain usage totals from a dedicated metadata file rather than conversation transcripts. - Display an explicit consent prompt before scanning data outside the main agent. - Add tests verifying that the default invocation does not open unrelated transcript files. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
analyzer.js:790
Finding
Session Identifiers Are Disclosed Despite the Truncation Guarantee<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:27`, `analyzer.js:790-791`, `mdReport.js:98-105` **Vulnerability Type**: Sensitive identifier disclosure **Risk Level**: Medium ### Vulnerable Code The Skill makes an explicit privacy guarantee: ```markdown **Session keys are truncated in all output** (first 8 chars + ellipsis) to avoid exposing sensitive identifiers. ``` However, findings are created with complete keys: ```js if (orphaned.length > 0) findings.push({ severity: 'high', source: 'sessions', message: `${orphaned.length} orphaned session(s) — still holding tokens on paid models`, detail: orphaned.map(s => `${s.key}: ${s.tokens.toLocaleString()} tokens ($${s.cost.toFixed(4)})` ).join('\n '), ...FIXES.ORPHANED_SESSIONS }); if (large.length > 0) findings.push({ severity: 'medium', source: 'sessions', message: `${large.length} session(s) with >50k tokens per conversation`, detail: large.map(s => `${s.key}: ${s.tokens.toLocaleString()} tokens` ).join('\n '), ...FIXES.LARGE_SESSIONS }); ``` Markdown output prints those details unchanged: ```js lines.push(`**${f.message}**`); lines.push(''); if (f.detail) lines.push(`${f.detail}`); ``` ### Technical Analysis Session-table rendering truncates long keys, but the finding construction path embeds full identifiers before output formatting. Markdown and terminal reports therefore disclose complete keys for orphaned or large sessions. Session keys may encode channel names, user identifiers, group identifiers, cron names, or other operational information. The vulnerability is particularly relevant because the documented Skill workflow instructs an AI agent to return the full Markdown report inline. ### Attack Path 1. A session has a sensitive key and is classified as orphaned or exceeds the token threshold. 2. The user invokes the documented `node run.js --md` command. 3. The analyzer inserts the full key into `finding.detail`. 4. The Markdown generator pr ...[truncated 505 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Redact identifiers when findings are constructed, rather than relying on individual renderers. - Introduce one centralized function such as `redactSessionKey()` and apply it to all finding messages, details, tables, JSON fields, database records, and dashboard events. - Avoid emitting raw `sessionId` values in machine-readable output unless the user explicitly requests them. - Review short identifiers as well; do not leave them unredacted merely because their length is below a threshold. - Add regression tests for orphaned sessions, large sessions, untracked sessions, all agents, and all supported output formats. ]]>

T01 · Skill Instruction Hijacking

Error
Location
mdReport.js:93
Finding
Unescaped Metadata Enables Markdown Instruction Injection<![CDATA[ ## Vulnerability Details **File Location**: `analyzer.js:238-253`, `analyzer.js:371-390`, `mdReport.js:93-110` **Vulnerability Type**: Indirect prompt and Markdown instruction injection **Risk Level**: High ### Vulnerable Code Configuration-derived names are incorporated into findings: ```js findings.push({ severity: tier === 'expensive' ? 'high' : 'medium', source: 'multi_agent', message: `Agent "${agent.id}" using expensive model: ${agentModel}`, detail: `Each agent has its own sessions, heartbeat, and hooks — all bill independently`, monthlyCost: monthly, ...FIXES.MULTI_AGENT_PAID(agent.id), }); ``` ```js findings.push({ severity: 'high', source: 'hooks', message: `Hook "${name}" running on ${hookModel} — switch to Haiku or local`, detail: `~50 fires/day estimated · $${monthly.toFixed(2)}/month`, monthlyCost: monthly, ...FIXES.HOOK_PAID_MODEL(name), }); ``` The values are emitted as Markdown without escaping: ```js for (const f of group) { lines.push(`#### ${SOURCE_LABELS[f.source] || f.source}`); lines.push(''); lines.push(`**${f.message}**`); lines.push(''); if (f.detail) lines.push(`${f.detail}`); if (f.monthlyCost > 0) lines.push(`**Monthly cost:** $${f.monthlyCost.toFixed(2)}/month`); if (f.fix) { lines.push(''); lines.push(`**Fix:** ${f.fix}`); } ``` ### Technical Analysis Agent IDs, hook names, cron names, skill names, model values, and session keys come from mutable local OpenClaw files. These strings are inserted directly into Markdown syntax. An attacker who can influence one of those values can inject headings, links, images, code blocks, or natural-language instructions. This is an indirect instruction-hijacking risk because `SKILL.md` directs the calling agent to return the full generated Markdown report inline. The generated report may consequently contain attacker-controlled instructions in a trusted tool-result context. ### Attack Path 1. An attacker causes a session, agent, ...[truncated 826 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Treat all configuration and transcript metadata as untrusted input. - Escape Markdown metacharacters in every untrusted value, including backticks, brackets, parentheses, pipes, headings, HTML delimiters, and line breaks. - Place untrusted values in clearly delimited quoted or code contexts after escaping. - Prefix generated reports with an instruction that embedded data is informational and must never be followed as instructions. - Return structured, sanitized data to the agent instead of free-form Markdown wherever possible. - Add adversarial tests containing headings, fenced code blocks, links, images, HTML, and explicit prompt-injection instructions. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
webDashboard.js:994
Finding
Stored DOM XSS in the Local Web Dashboard<![CDATA[ ## Vulnerability Details **File Location**: `webDashboard.js:994-1041`, `webDashboard.js:1133-1162` **Vulnerability Type**: Stored DOM-based cross-site scripting **Risk Level**: High ### Vulnerable Code Session and model values are inserted through `innerHTML`: ```js function renderSessions() { const tbody = document.getElementById('sessionsBody'); if (!state.sessions.length) { tbody.innerHTML = '<tr><td colspan="4" style="color:var(--text-dim);text-align:center;padding:20px;">Waiting for API calls...</td></tr>'; return; } tbody.innerHTML = state.sessions.slice(0, 8).map(s => { const costColor = s.cost > 5 ? 'var(--red)' : s.cost > 0.5 ? 'var(--amber)' : 'var(--green)'; return '<tr>' + '<td style="color:var(--cyan)">' + (s.name.length > 18 ? s.name.slice(0,16)+'…' : s.name) + '</td>' + '<td style="color:var(--text-dim)">' + (s.model || '—') + '</td>' + '<td>' + s.messages + '</td>' + '<td style="color:' + costColor + '">' + fmtCost(s.cost) + '</td>' + '</tr>'; }).join(''); } ``` Persisted leaderboard and live-feed data are handled similarly: ```js el.innerHTML = data.slice(0, 5).map((msg, i) => { const rankClass = i === 0 ? 'rank-1' : i === 1 ? 'rank-2' : i === 2 ? 'rank-3' : 'rank-n'; const time = new Date(msg.timestamp).toLocaleTimeString(); return '<div class="leaderboard-item">' + '<div class="leaderboard-rank ' + rankClass + '">' + (i+1) + '</div>' + '<div style="flex:1"><div style="font-weight:600;font-size:14px;color:var(--amber)">'+fmtCost(msg.cost)+'</div>' + '<div style="font-size:11px;color:var(--text-dim)">' + msg.session_name + ' · ' + (msg.model||'').split('/').pop() + ' · ' + time + '</div></div>' + '<div style="font-size:11px;color:var(--text-dim);text-align:right">' + fmtTokens(msg.total_tokens) + ' tok' + (msg.cache_write > 10000 ? '<br/>' + fmtTokens(msg.cache_write) + ' cache' : '') + '</div>' + '</div>'; }).join(''); ``` ### Technical Analysis ...[truncated 1551 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace HTML-string concatenation with DOM APIs and assign untrusted values through `textContent`. - If HTML templating is necessary, apply a proven context-sensitive HTML sanitizer. - Escape data separately for text nodes, attributes, URLs, CSS, and JavaScript contexts. - Add a restrictive Content Security Policy that disallows inline scripts and event handlers. - Avoid storing unsanitized display values when a normalized identifier would suffice. - Apply equivalent escaping to `htmlReport.js`. - Add browser security tests using payloads in session names and model names. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
webDashboard.js:356
Finding
Unauthenticated Cross-Origin SSE Endpoint Exposes Session and Billing Metadata<![CDATA[ ## Vulnerability Details **File Location**: `webDashboard.js:267-319`, `webDashboard.js:356-371`, `webDashboard.js:434-447` **Vulnerability Type**: Missing authentication and permissive cross-origin data access **Risk Level**: High ### Vulnerable Code The data returned by the API includes complete identifiers: ```js function getTodaySummary() { return { cost: today.cost, messages: today.messages, tokens: today.tokens, cacheRead: today.cacheRead, cacheWrite: today.cacheWrite, models: today.models, sessions: Object.entries(today.sessions).map(([id, s]) => ({ id, name: s.name, model: s.model, cost: s.cost, messages: s.messages, tokens: s.tokens, lastSeen: s.lastSeen, })).sort((a, b) => b.cost - a.cost), peakCostPerMsg, avgCostPerMsg: today.messages > 0 ? today.cost / today.messages : 0, }; } ``` The SSE endpoint allows every origin and requires no authorization: ```js if (url.pathname === '/api/stream') { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'Access-Control-Allow-Origin': '*', }); res.write(`data: ${JSON.stringify({ type: 'init', today: getTodaySummary(), recent: recentEvents.slice(0, 20) })}\n\n`); sseClients.add(res); req.on('close', () => sseClients.delete(res)); return; } ``` The service uses a predictable local address: ```js server.listen(port, '127.0.0.1', () => { console.log(` 🦞 Dashboard: \x1b[1m\x1b[36mhttp://localhost:${port}\x1b[0m`); ``` ### Technical Analysis Loopback binding prevents ordinary remote hosts from directly connecting, but it does not protect localhost services from a webpage running in the user’s browser. The fixed port and wildcard CORS policy allow an attacker-controlled web origin to establish an `EventSource` connection. The initialization and subsequent broadcasts expose full session IDs, names, model information, costs, timestamps, an ...[truncated 963 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `Access-Control-Allow-Origin: *`. - Reject requests whose `Origin` is not the dashboard’s own origin; account for requests with missing or opaque origins. - Generate a cryptographically random per-launch access token and require it for every API and SSE request. - Avoid placing long-lived tokens in URLs where they may enter history or logs. - Use an unpredictable available port where practical. - Omit full session IDs from browser responses and use temporary display identifiers. - Add `Cache-Control: no-store`, `X-Content-Type-Options: nosniff`, a restrictive CSP, and other relevant security headers. - Consider Unix-domain sockets or a desktop wrapper if browser access is not essential. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
snapshotCard.js:137
Finding
Snapshot and Dashboard Violate Declared Offline, No-Shell, and Write-Scope Guarantees<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:22-25`, `SKILL.md:40-46`, `snapshotCard.js:137`, `snapshotCard.js:349-350`, `webDashboard.js:446-447`, `webDashboard.js:471` **Vulnerability Type**: Undisclosed network request, file write, and subprocess execution **Risk Level**: Medium ### Vulnerable Code The Skill documentation states: ```markdown **Files this skill may write (only when `--md` is used):** - `./clawculator-report.md` — markdown report - Custom path via `--out=PATH` **No network requests are made. No shell commands are spawned.** ``` Snapshot mode writes an additional HTML file: ```js const htmlPath = path.join(outputDir, 'clawculator-snapshot.html'); fs.writeFileSync(htmlPath, html, 'utf8'); ``` The generated snapshot and dashboard HTML load Google Fonts: ```css @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700;800&family=Outfit:wght@400;500;600;700;800;900&display=swap'); ``` The web dashboard launches a browser through a shell: ```js const { exec } = require('child_process'); exec(`open "http://localhost:${port}" 2>/dev/null || xdg-open "http://localhost:${port}" 2>/dev/null`); ``` ### Technical Analysis The snapshot command is directly documented in `SKILL.md`, but its file write is omitted from the declared write list. Opening the generated HTML as instructed causes the browser to contact Google Fonts. The bundled web dashboard similarly imports remote fonts and invokes a shell command to open a browser. The shell command uses an internally generated numeric port rather than direct user-controlled input, so no command-injection exploit was confirmed. The issue is inaccurate disclosure and unnecessary privilege rather than arbitrary command execution. ### Attack Path 1. The user invokes the documented `--snapshot` command. 2. The Skill writes `clawculator-snapshot.html` despite the statement that only `--md` writes files. 3. The output instructs the user to open the generat ...[truncated 829 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove remote font imports and use system fonts or package locally bundled font files. - Update `SKILL.md` and command help to list every file that may be written. - Make snapshot HTML generation optional if only terminal output is required. - Avoid shell-based browser launching; print the URL or use a platform API that does not invoke a shell. - If external resources remain available, make them explicit opt-in behavior and disclose the contacted domains. - Add offline integration tests that fail if any generated asset references an HTTP or HTTPS resource. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:30
Finding
Unpinned Remote npm Package Execution and Native Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `README.md:30-35`, `README.md:100-105`, `webDashboard.js:17-26` **Vulnerability Type**: Supply-chain exposure from mutable package versions **Risk Level**: Medium ### Vulnerable Code The primary usage and installation instructions execute unpinned packages: ```markdown ```bash npx clawculator ``` ``` ```markdown ```bash npm i -g clawhub clawhub install clawculator ``` ``` The dashboard also directs users to install an unpinned native module: ```js try { Database = require('better-sqlite3'); } catch { console.error('\n\x1b[31m ✗ better-sqlite3 is required for --web\x1b[0m\n'); console.error(' Install it with:\n'); console.error(' \x1b[36mnpm install -g better-sqlite3\x1b[0m'); console.error(' \x1b[90m# or, if installed locally:\x1b[0m'); console.error(' \x1b[36mcd $(npm root -g)/clawculator && npm install better-sqlite3\x1b[0m\n'); console.error(' \x1b[90mThis is a native module that compiles on install.\x1b[0m'); ``` ### Technical Analysis `npx clawculator` resolves and executes a mutable package release. The global installation instructions likewise omit exact versions and integrity constraints. The optional `better-sqlite3` dependency includes native compilation, which increases exposure to lifecycle scripts, build tooling, transitive dependencies, and compromised package releases. No evidence was found that the currently referenced packages are malicious. The vulnerability is the unsafe installation model and lack of reproducible dependency constraints. ### Attack Path 1. An attacker compromises a future release of one of the referenced packages or a transitive dependency. 2. The victim follows the documentation after the compromised version becomes the latest matching release. 3. npm downloads the mutable package version. 4. npm executes applicable lifecycle or native build scripts with the invoking user’s privileges. 5. Malicious install-time or runtime code can acces ...[truncated 380 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin exact package versions in all installation and execution examples. - Publish and maintain lockfiles with integrity hashes for reproducible installations. - Document package provenance and release-signing or verification procedures. - Minimize lifecycle scripts and clearly disclose when native compilation occurs. - Prefer local, least-privileged installation over global installation. - Use `npm ci` for controlled deployments and audit transitive dependencies regularly. - Where practical, provide signed standalone artifacts or a bundled release whose contents match the reviewed source. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (35)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This is the most serious mismatch: if the code starts an HTTP server, watches files continuously, uses SQLite/native modules, launches a browser via child_process, and exposes API/SSE endpoints, then the skill is operating far beyond a simple offline cost analyzer. Those behaviors substantially expand attack surface: local service exposure, persistent data storage, continuous monitoring of user files, command execution, and browser launch all introduce opportunities for privacy loss or abuse. In the context of a user-invocable skill advertised as bundled/offline, this discrepancy is highly dangerous because it defeats informed consent and least privilege expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This is the most serious mismatch: if the code starts an HTTP server, watches files continuously, uses SQLite/native modules, launches a browser via child_process, and exposes API/SSE endpoints, then the skill is operating far beyond a simple offline cost analyzer. Those behaviors substantially expand attack surface: local service exposure, persistent data storage, continuous monitoring of user files, command execution, and browser launch all introduce opportunities for privacy loss or abuse. In the context of a user-invocable skill advertised as bundled/offline, this discrepancy is highly dangerous because it defeats informed consent and least privilege expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This is the most serious mismatch: if the code starts an HTTP server, watches files continuously, uses SQLite/native modules, launches a browser via child_process, and exposes API/SSE endpoints, then the skill is operating far beyond a simple offline cost analyzer. Those behaviors substantially expand attack surface: local service exposure, persistent data storage, continuous monitoring of user files, command execution, and browser launch all introduce opportunities for privacy loss or abuse. In the context of a user-invocable skill advertised as bundled/offline, this discrepancy is highly dangerous because it defeats informed consent and least privilege expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This is the most serious mismatch: if the code starts an HTTP server, watches files continuously, uses SQLite/native modules, launches a browser via child_process, and exposes API/SSE endpoints, then the skill is operating far beyond a simple offline cost analyzer. Those behaviors substantially expand attack surface: local service exposure, persistent data storage, continuous monitoring of user files, command execution, and browser launch all introduce opportunities for privacy loss or abuse. In the context of a user-invocable skill advertised as bundled/offline, this discrepancy is highly dangerous because it defeats informed consent and least privilege expectations.

Vague Triggers

High
Confidence
98% confidence
Finding
The trigger phrase 'snapshot' is highly ambiguous and likely to collide with ordinary user language. Because this mode is described as producing a shareable grade card, accidental invocation could expose account usage characteristics or generate disclosure-oriented output when the user did not intend to share billing-related information. In a skill that accesses local usage/session data, such an underspecified trigger materially increases privacy risk.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
},
  ORPHANED_SESSIONS: {
    fix: 'Delete sessions.json to clear orphaned sessions — they auto-rebuild on next use',
    command: 'rm ~/.openclaw/agents/main/sessions/sessions.json',
  },
  LARGE_SESSIONS: {
    fix: 'Reduce root-level .md files in your workspace to shrink session context size',
Confidence
95% confidence
Finding
The skill embeds a destructive shell command, `rm ~/.openclaw/agents/main/sessions/sessions.json`, as a recommended fix. In agent ecosystems that may surface or auto-run suggested commands, this creates a command-injection-by-design hazard and can cause data loss or unsafe file deletion if copied, executed blindly, or adapted to a different environment.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The card explicitly claims '100% offline' and 'your data never leaves your machine' while the HTML includes an external Google Fonts import. This is dangerous because it creates a deceptive privacy guarantee: users may open potentially sensitive cost-analysis snapshots believing no network traffic occurs, when in fact a third party is contacted and can observe access metadata.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill metadata explicitly says nothing is fetched at runtime, but the dashboard injects an external Google Fonts import. That causes outbound network access, leaks usage metadata such as IP/user agent/referrer context to a third party, and breaks the trust boundary promised by the package description.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The README instructs users to execute `npx clawculator` without pinning a specific package version. Because `npx` resolves the latest published package by default, a compromised maintainer account, malicious new release, or dependency hijack could cause users to execute unreviewed code at install/runtime. The skill context increases risk because the tool is explicitly marketed for easy one-command execution.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
This usage example invokes `npx clawculator` without a pinned version, which allows execution of whatever version is current on the package registry at the time of use. If the package or publishing pipeline is compromised, users may run attacker-controlled code simply by following the documentation.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The README promotes `npx clawculator --md` without constraining the package version. That creates a supply-chain execution risk because the command may fetch and run a newly published or tampered package version at invocation time.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
Using `npx clawculator --report` without a version pin exposes users to untrusted future package updates. In practice, documentation-driven copy/paste can turn a registry compromise into immediate code execution on analyst workstations.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The unpinned `npx clawculator --json` example permits fetching the latest package version dynamically. That is dangerous because even a benign utility becomes an execution vector if a malicious version is published later.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
This example combines unpinned `npx` execution with writing output to a file, but the primary issue is still remote code execution risk via package resolution to the latest version. The offline/deterministic claims in the README do not mitigate the initial package fetch and execution path.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The command `npx clawculator --config=/path/to/openclaw.json` is unpinned and may execute an attacker-controlled package version if the registry artifact changes. Given that the command processes local configuration files, compromise could also expose sensitive local data handled by the tool.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The `npx clawculator --help` example is also an unpinned remote execution path. Even seemingly harmless commands like `--help` still require package resolution and startup code execution, so the attack surface remains the same.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
This JSON piping example uses `npx clawculator --json` without a fixed version, so a user following the README may execute whatever package is currently served from the registry. The presence of `jq` does not change the initial risk: the code has already run before its output is filtered.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
The command `npx clawculator` in the HTML output section is another unpinned package execution instruction. Repeated promotion of unpinned `npx` amplifies exposure because many users will copy the first command they see without reviewing supply-chain implications.

Rp1

Medium
Category
MCP Rug Pull
Confidence
95% confidence
Finding
This command again instructs users to run an unpinned package. In the context of a security-adjacent analysis tool, users may run it on systems containing configuration files, API keys, and logs, so registry compromise could have meaningful local impact.

Vague Triggers

Medium
Confidence
87% confidence
Finding
The README says users can type `clawculator` in OpenClaw webchat to invoke the skill, but it does not provide any negative examples or activation constraints beyond the bare phrase. In a chat-driven agent environment, a single common noun-like command without explicit scope guidance can create ambiguity about when the skill should activate versus when the term is merely being discussed.

Skill Enumeration

Medium
Category
Agent Snooping
Content
mkdir -p ~/clawd/skills/clawculator

BASE=https://raw.githubusercontent.com/echoudhry/clawculator/main/skills/clawculator
curl -o ~/clawd/skills/clawculator/SKILL.md      $BASE/SKILL.md
curl -o ~/clawd/skills/clawculator/run.js         $BASE/run.js
curl -o ~/clawd/skills/clawculator/analyzer.js    $BASE/analyzer.js
curl -o ~/clawd/skills/clawculator/reporter.js    $BASE/reporter.js
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill declares no explicit tool scope or permissions, yet its documented behavior includes reading sensitive local files and invoking Node to execute bundled code. Even if the code is intended to be offline, the absence of declared permissions reduces transparency and makes it harder for users or platforms to constrain filesystem, environment, shell, or potential network access. Given the static note that code capabilities include env, network, and shell, this mismatch increases the risk of over-privileged execution.

Vague Triggers

Medium
Confidence
95% confidence
Finding
Broad trigger phrases like 'check my costs' or 'cost report' can cause accidental invocation in unrelated conversations, leading the skill to read local billing/session files without the user clearly intending that action. The risk here is not code execution itself, but unintended access to potentially sensitive local data because the activation language is too generic.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
webDashboard.js:34

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
webDashboard.js:106