Back to skill

Security audit

Token Stats

Security checks for vulnerabilities and agentic risk

Overview

This skill is a token-usage reporter, but it reads local OpenClaw session history and has loose activation and path scoping that should be reviewed before installation.

Install only if you are comfortable with a skill reading local OpenClaw session-history files to summarize token usage. Review or fix the --agent path handling before use in shared or prompt-injection-prone environments, and invoke it only for explicit OpenClaw token-statistics requests.

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_stats.py:32
Finding

Path Traversal Through Unvalidated Agent Identifier

Content
View full analysis

Vulnerability Details

File Location: scripts/token_stats.py, lines 32 and 43-56
Vulnerability Type: Path traversal caused by unsafe path construction
Risk Level: Medium

Vulnerable Code

python
p.add_argument("--agent", default="main", help="Agent ID (default: main)")
python
def find_session_files(agent_id, include_deleted):
    base = os.path.expanduser("~/.openclaw/agents/%s/sessions/" % agent_id)
    patterns = [base + "*.jsonl"]
    if include_deleted:
        patterns.append(base + "*.jsonl.deleted*")
        patterns.append(base + "*.jsonl.bak*")
    files = []
    for pat in patterns:
        files.extend(glob.glob(pat))
    return sorted(set(files), key=os.path.getmtime, reverse=True)


def load_labels(agent_id):
    """Load session labels from sessions.json."""
    sj_path = os.path.expanduser(
        "~/.openclaw/agents/%s/sessions/sessions.json" % agent_id
    )

Technical Analysis

The --agent command-line argument is accepted without validation and interpolated directly into filesystem paths. The application does not reject path separators or .. components, nor does it canonicalize the resulting path and verify that it remains under the intended ~/.openclaw/agents/ directory.

Consequently, an attacker who can influence the command-line arguments can supply a traversal value that causes both find_session_files() and load_labels() to access an unintended, locally accessible directory whose final component is sessions.

Exploitation is constrained by the script's fixed filename patterns and parsers: the target directory must contain matching *.jsonl files or a sessions.json file, and meaningful output requires compatible JSON structures. The script does not print complete message contents, but it can expose derived token statistics, timestamps, session identifiers, and labels from files outside the intended agent directory.

Attack Pa

...[truncated 1418 chars]

Remediation
View remediation

Remediation Suggestions

  1. Restrict agent identifiers to a conservative allowlist, such as letters, digits, underscores, and hyphens:

    python
    import re
    
    AGENT_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]+$")
    
    def validate_agent_id(agent_id):
        if not AGENT_ID_PATTERN.fullmatch(agent_id):
            raise ValueError("Invalid agent ID")
        return agent_id
    
  2. Build paths with pathlib.Path rather than string interpolation.

  3. Resolve both the trusted agents root and candidate directory, then enforce containment before performing any read:

    python
    from pathlib import Path
    
    def get_sessions_dir(agent_id):
        validate_agent_id(agent_id)
    
        agents_root = (Path.home() / ".openclaw" / "agents").resolve()
        sessions_dir = (agents_root / agent_id / "sessions").resolve()
    
        if agents_root not in sessions_dir.parents:
            raise ValueError("Agent path escapes the agents directory")
    
        return sessions_dir
    
  4. Use the validated directory returned by a single shared helper in both find_session_files() and load_labels() so that security checks cannot diverge.

  5. Reject symbolic-link-based escapes by performing containment validation after path resolution. If the environment permits untrusted users to modify directories concurrently, open files using descriptor-relative APIs and appropriate no-follow protections to reduce time-of-check/time-of-use risks.

  6. Add automated tests covering absolute paths, ../ traversal, nested traversal, path separators, symbolic links, valid agent identifiers, and directories that merely share the expected filename structure.

Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill instructs the agent to scan session JSONL files under the user's home directory, which is a file-read capability, but the manifest does not declare any tool scope or allowed-tools restriction. This creates an authorization and transparency gap: an agent may read potentially sensitive conversation logs without explicit permission boundaries, increasing the chance of unintended data exposure.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill description is broad enough to match common questions about usage or cost, which can cause the agent to invoke a file-scanning skill in situations where the user did not clearly request filesystem-backed analysis. Because the skill operates on historical session logs, overbroad activation can unnecessarily expose private usage metadata and conversation-derived information.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The example trigger '看看用量' is too vague and could match many unrelated requests, increasing the likelihood of accidental skill execution. In this skill's context, unintended activation matters because execution entails scanning local session files that may contain sensitive historical metadata.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

This script emits user-facing text in Chinese and formats large numbers using Chinese units like '亿' and '万', while the CLI offers no language or locale selection. That creates a natural-language locale policy issue because the skill enforces a specific language presentation rather than offering a choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The human-readable report headings and labels are hard-coded in Chinese, and there is no documented opt-in or locale selector for users who need a different language. Under the policy, forcing a specific language in user-facing natural-language output is a reportable issue unless the locale restriction is clearly justified.

Content

No source excerpt is available for this finding.