Back to skill

Security audit

Agent Token Usage

Security checks for vulnerabilities and agentic risk

Overview

The skill's token reporting purpose is coherent, but the optional UI installer makes persistent and broad local UI changes that users should review first.

Install the CLI portion only if you just need token summaries. Before running apply-ui.sh, review that it will patch discovered OpenClaw Control UI installs, add a macOS LaunchAgent that refreshes every 5 minutes, and uninstall by removing the UI data directory. Avoid the UI patch in environments where local agent names or generated data files could be influenced by untrusted users until the HTML rendering is escaped or rebuilt with DOM text APIs.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/token-usage-button.iife.js:104
Finding
Stored DOM XSS Through Unescaped Agent Names<![CDATA[ ## Vulnerability Details **File Location**: `scripts/token-usage-button.iife.js`, lines 104–116 and 166–170 **Vulnerability Type**: Stored DOM-based cross-site scripting / HTML injection **Risk Level**: Medium ### Vulnerable Code ```javascript const rows = agents.map((a) => { const pct = Math.round(((a.total || 0) / max) * 100); const bar = `<span class="milly-tk-bar" style="width:${Math.max(2, pct * 0.6)}px"></span>`; return ` <tr> <td>${a.agent}${bar}</td> <td>${a.calls || 0}</td> <td>${fmt(a.input)}</td> <td>${fmt(a.output)}</td> <td>${fmt(a.cacheRead)}</td> <td>${fmt(a.cacheWrite)}</td> <td><b>${fmt(a.total)}</b></td> <td>${fmt(a.billable)}</td> </tr>`; }).join(""); ``` The generated markup is subsequently assigned to `innerHTML`: ```javascript const resp = await fetch(DATA_URL + "?t=" + Date.now(), { cache: "no-cache" }); if (!resp.ok) throw new Error("HTTP " + resp.status + " — " + DATA_URL); const data = await resp.json(); body.innerHTML = render(data); ``` ### Technical Analysis The `a.agent` value is not a trusted constant. The Python aggregator derives it from a directory name beneath `~/.openclaw/agents` and includes it directly in the generated JSON: ```python agent = path.split(os.sep + "agents" + os.sep)[1].split(os.sep)[0] ``` The browser payload interpolates this value into an HTML template without escaping and assigns the result to `body.innerHTML`. Consequently, markup contained in an agent directory name or in a tampered `agent-token-usage.json` file is interpreted as HTML rather than displayed as text. A malicious value can inject arbitrary elements into the token-usage modal. Depending on the Control UI Content Security Policy and browser enforcement, an event-handler or equivalent browser-compatible payload may execute JavaScript. Even where CSP blocks script execution, arbitrary HTML injection remains possible and can be used for UI spoofing, misleading link ...[truncated 2318 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Do not render fetched data with `innerHTML`.** Construct the table using DOM APIs and assign all external values through `textContent`: ```javascript const td = document.createElement("td"); td.textContent = String(a.agent ?? ""); row.appendChild(td); ``` 2. **Separate trusted presentation markup from untrusted values.** Static elements such as the usage bar may be created with `document.createElement()` and styled through validated numeric properties. 3. **Validate the JSON schema before rendering.** - Require `agent`, `date`, and model names to be strings. - Require token and call values to be finite, non-negative numbers. - Reject unexpected properties or malformed records. - Apply reasonable length limits to names. 4. **Validate agent names in the Python aggregator.** Prefer deriving agent identifiers with `os.path.relpath()` and reject names containing control characters or markup-significant characters. This is defense in depth and must not replace safe browser rendering. 5. **If HTML templates are retained, apply context-appropriate escaping** to every JSON-derived string before interpolation. Escaping should cover at least `&`, `<`, `>`, `"`, and `'`. A maintained sanitizer may be used only when intentional HTML support is required. 6. **Apply a restrictive Control UI CSP** that disallows inline script, inline event handlers, `javascript:` navigation, and unauthorized network destinations. CSP should be treated as defense in depth rather than the primary fix. 7. **Protect the generated data file.** Ensure it is writable only by the intended user or service and served only through the authenticated Control UI. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (28)

Chaining Abuse

High
Category
Tool Misuse
Content
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LABEL="com.symbolstar.openclaw.token-usage-refresh"
PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
[[ -f "$PLIST" ]] && { launchctl unload "$PLIST" 2>/dev/null || true; rm -f "$PLIST"; echo "✓ launchd removed"; }

ROOTS=()
if command -v npm >/dev/null 2>&1; then
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Chaining Abuse

High
Category
Tool Misuse
Content
ORIG="${BAK%.tk.bak}"
      [[ "$ORIG" == *.tk-*.js ]] && ORIG_BASE="${ORIG%.tk-*}.js" || ORIG_BASE="$ORIG"
      mv "$BAK" "$ORIG_BASE"
      [[ "$ORIG" != "$ORIG_BASE" && -f "$ORIG" ]] && rm -f "$ORIG"
      echo "✓ restored $ORIG_BASE"
    done
    [[ -f "$INDEX.tk.bak" ]] && { mv "$INDEX.tk.bak" "$INDEX"; echo "✓ restored $INDEX"; }
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Chaining Abuse

High
Category
Tool Misuse
Content
ORIG="${BAK%.tk.bak}"
      [[ "$ORIG" == *.tk-*.js ]] && ORIG_BASE="${ORIG%.tk-*}.js" || ORIG_BASE="$ORIG"
      mv "$BAK" "$ORIG_BASE"
      [[ "$ORIG" != "$ORIG_BASE" && -f "$ORIG" ]] && rm -f "$ORIG"
      echo "✓ restored $ORIG_BASE"
    done
    [[ -f "$INDEX.tk.bak" ]] && { mv "$INDEX.tk.bak" "$INDEX"; echo "✓ restored $INDEX"; }
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Chaining Abuse

High
Category
Tool Misuse
Content
ORIG="${BAK%.tk.bak}"
      [[ "$ORIG" == *.tk-*.js ]] && ORIG_BASE="${ORIG%.tk-*}.js" || ORIG_BASE="$ORIG"
      mv "$BAK" "$ORIG_BASE"
      [[ "$ORIG" != "$ORIG_BASE" && -f "$ORIG" ]] && rm -f "$ORIG"
      echo "✓ restored $ORIG_BASE"
    done
    [[ -f "$INDEX.tk.bak" ]] && { mv "$INDEX.tk.bak" "$INDEX"; echo "✓ restored $INDEX"; }
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
echo "✓ restored $ORIG_BASE"
    done
    [[ -f "$INDEX.tk.bak" ]] && { mv "$INDEX.tk.bak" "$INDEX"; echo "✓ restored $INDEX"; }
    [[ -d "$BASE/data" ]] && rm -rf "$BASE/data" && echo "✓ removed $BASE/data"
  done
done
Confidence
95% 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).

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: agent-token-usage
description: Summarize per-agent LLM token consumption for OpenClaw multi-agent setups by parsing `~/.openclaw/agents/*/sessions/<id>.jsonl` session logs (type=message, role=assistant). Ships both a CLI (Python) and an optional 📊 button injected into the Control UI header next to Search. Use when the user asks "今天哪个 agent 用了多少 token / 消耗了多少 token / token 排行 / token 统计 / how much did agent X spend today / which agent burns the most tokens / token usage breakdown / billable token estimate", or asks to install/remove the 📊 token-usage button in Control UI. Returns a ranked table with input / output / cacheRead / cacheWrite / total (and equivalent-billable token estimate). NOT for: dollar cost (use codexbar/model-usage skill), per-message inspection (use sessions_history), or non-OpenClaw runtimes.
---

# agent-token-usage 📊
Confidence
60% 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.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The script presents itself as an optional UI patch, but on macOS it also installs a persistent LaunchAgent that executes every 5 minutes. That mismatch is security-relevant because it obscures ongoing system modification and background execution beyond a one-time UI change, reducing informed user consent and increasing the chance of unnoticed persistence.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script appends code into an existing OpenClaw bundle, renames assets/index references, creates backup files, writes data files, and may install a recurring LaunchAgent, all without a prompt or strong upfront warning. Silent persistent modification of another application's installation increases integrity and persistence risk, especially in a skill context where users may expect helper automation rather than system-level alteration.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The script writes a user LaunchAgent plist and loads it with launchctl, creating recurring execution at login/session scope. Persistent background execution is a sensitive capability and is not necessary for a one-time UI patch unless explicitly justified and consented to, so this is a real security concern even if the apparent purpose is token-usage refresh.

Session Persistence

Medium
Category
Rogue Agent
Content
if [[ "$OSTYPE" == "darwin"* ]]; then
  LABEL="com.symbolstar.openclaw.token-usage-refresh"
  PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
  mkdir -p "$HOME/Library/LaunchAgents" "$HOME/.openclaw/logs"
  cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
Confidence
82% confidence
Finding
This finding appears to be a duplicate lexical match on the plist file path rather than a distinct security issue beyond the broader LaunchAgent persistence. The underlying concern is real, but this specific duplicate hit does not add a separate vulnerability.

Session Persistence

Medium
Category
Rogue Agent
Content
if [[ "$OSTYPE" == "darwin"* ]]; then
  LABEL="com.symbolstar.openclaw.token-usage-refresh"
  PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
  mkdir -p "$HOME/Library/LaunchAgents" "$HOME/.openclaw/logs"
  cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
Confidence
82% confidence
Finding
This finding appears to be a duplicate lexical match on the plist file path rather than a distinct security issue beyond the broader LaunchAgent persistence. The underlying concern is real, but this specific duplicate hit does not add a separate vulnerability.

Session Persistence

Medium
Category
Rogue Agent
Content
LABEL="com.symbolstar.openclaw.token-usage-refresh"
  PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
  mkdir -p "$HOME/Library/LaunchAgents" "$HOME/.openclaw/logs"
  cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
Confidence
90% confidence
Finding
Writing the plist contents to disk defines a recurring job specification for launchd. This is a concrete persistence mechanism and should be treated as a real security-relevant behavior because it causes future execution outside the original invocation flow.

Session Persistence

Medium
Category
Rogue Agent
Content
mkdir -p "$HOME/Library/LaunchAgents" "$HOME/.openclaw/logs"
  cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>$LABEL</string>
Confidence
78% confidence
Finding
This repeated match again points to the plist content section and does not represent a separate vulnerability from the same persistence mechanism. It is a true signal but duplicative.

Session Persistence

Medium
Category
Rogue Agent
Content
mkdir -p "$HOME/Library/LaunchAgents" "$HOME/.openclaw/logs"
  cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>$LABEL</string>
Confidence
78% confidence
Finding
This repeated match again points to the plist content section and does not represent a separate vulnerability from the same persistence mechanism. It is a true signal but duplicative.

Session Persistence

Medium
Category
Rogue Agent
Content
cat > "$PLIST" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>$LABEL</string>
  <key>ProgramArguments</key>
Confidence
88% confidence
Finding
The ProgramArguments entry specifies execution of /bin/bash with the refresh script, which is the operative part of the persistent autorun job. This is dangerous because it schedules repeated script execution in the background, increasing the blast radius of any present or future issues in refresh-data.sh.

Session Persistence

Medium
Category
Rogue Agent
Content
<key>StandardOutPath</key><string>$HOME/.openclaw/logs/token-usage-refresh.log</string>
  <key>StandardErrorPath</key><string>$HOME/.openclaw/logs/token-usage-refresh.err.log</string>
</dict>
</plist>
EOF
  launchctl unload "$PLIST" 2>/dev/null || true
  launchctl load "$PLIST" && green "✓ launchd job installed (refreshes every 5min): $LABEL"
Confidence
77% confidence
Finding
This match falls on the end of the plist block and is not a distinct vulnerability beyond the already identified LaunchAgent persistence. It is best understood as duplicate evidence of the same issue.

Session Persistence

Medium
Category
Rogue Agent
Content
</dict>
</plist>
EOF
  launchctl unload "$PLIST" 2>/dev/null || true
  launchctl load "$PLIST" && green "✓ launchd job installed (refreshes every 5min): $LABEL"
fi
Confidence
79% confidence
Finding
This is another duplicate hit adjacent to launchctl usage and plist handling, not a separate vulnerability. The substantive concern remains user-session persistence via LaunchAgent installation.

Session Persistence

Medium
Category
Rogue Agent
Content
</plist>
EOF
  launchctl unload "$PLIST" 2>/dev/null || true
  launchctl load "$PLIST" && green "✓ launchd job installed (refreshes every 5min): $LABEL"
fi

dim "  Refresh your Control UI tab. 📊 button appears next to Search."
Confidence
80% confidence
Finding
This duplicate match references the same launchctl/plist activation line and does not indicate a separate additional vulnerability. It should be merged with the primary RA2 finding at this location.

Session Persistence

Medium
Category
Rogue Agent
Content
</plist>
EOF
  launchctl unload "$PLIST" 2>/dev/null || true
  launchctl load "$PLIST" && green "✓ launchd job installed (refreshes every 5min): $LABEL"
fi

dim "  Refresh your Control UI tab. 📊 button appears next to Search."
Confidence
80% confidence
Finding
This duplicate match references the same launchctl/plist activation line and does not indicate a separate additional vulnerability. It should be merged with the primary RA2 finding at this location.

Session Persistence

Medium
Category
Rogue Agent
Content
</plist>
EOF
  launchctl unload "$PLIST" 2>/dev/null || true
  launchctl load "$PLIST" && green "✓ launchd job installed (refreshes every 5min): $LABEL"
fi

dim "  Refresh your Control UI tab. 📊 button appears next to Search."
Confidence
80% confidence
Finding
This duplicate match references the same launchctl/plist activation line and does not indicate a separate additional vulnerability. It should be merged with the primary RA2 finding at this location.

Session Persistence

Medium
Category
Rogue Agent
Content
set -euo pipefail
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LABEL="com.symbolstar.openclaw.token-usage-refresh"
PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
[[ -f "$PLIST" ]] && { launchctl unload "$PLIST" 2>/dev/null || true; rm -f "$PLIST"; echo "✓ launchd removed"; }

ROOTS=()
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.

Session Persistence

Medium
Category
Rogue Agent
Content
set -euo pipefail
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LABEL="com.symbolstar.openclaw.token-usage-refresh"
PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
[[ -f "$PLIST" ]] && { launchctl unload "$PLIST" 2>/dev/null || true; rm -f "$PLIST"; echo "✓ launchd removed"; }

ROOTS=()
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.

Session Persistence

Medium
Category
Rogue Agent
Content
set -euo pipefail
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LABEL="com.symbolstar.openclaw.token-usage-refresh"
PLIST="$HOME/Library/LaunchAgents/$LABEL.plist"
[[ -f "$PLIST" ]] && { launchctl unload "$PLIST" 2>/dev/null || true; rm -f "$PLIST"; echo "✓ launchd removed"; }

ROOTS=()
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.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The script recursively deletes "$BASE/data" with rm -rf, which is a destructive operation affecting user/system files. While it prints a message after deletion, there is no prior warning, confirmation, or explanatory comment/docstring disclosing that this directory will be removed.

Session Persistence

Medium
Category
Rogue Agent
Content
return `
      <h2>📊 Agent Token Usage <span style="font-weight:400;font-size:12px;color:var(--text-2,#888)">— ${date}</span></h2>
      <div class="milly-tk-meta">Last updated: ${updated} · ${agents.length} agents · ~bill weights: cacheRead ×0.1, cacheWrite ×1.25</div>
      ${agents.length === 0
        ? `<div class="milly-tk-empty">No usage records found for ${date}.<br>Run the scheduler or check <code>~/.openclaw/agents/*/sessions/*.trajectory.jsonl</code></div>`
        : `<table>
Confidence
60% 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.

Static analysis

No suspicious patterns detected.