Back to skill

Security audit

Token Audit

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a legitimate token-cost audit tool, but it reads sensitive OpenClaw workspace state and also scans the default global skills directory even when a specific workspace is selected.

Install only if you are comfortable with a local script reading OpenClaw core files, memory files, and installed skill source to compute token counts. Run it in a workspace without sensitive memory if possible, avoid sharing JSON output publicly because it may contain absolute paths, and treat its model coverage claims as incomplete.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/token-audit.py:72
Finding
Overbroad Access to Sensitive Agent State and Unselected Workspace Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/token-audit.py:72-83`, `scripts/token-audit.py:99-136` **Vulnerability Type**: Overbroad filesystem access and cross-workspace scanning **Risk Level**: Medium ### Complete Code Snippet ```python # Core workspace files core_files = [ "SOUL.md", "AGENTS.md", "HEARTBEAT.md", "USER.md", "MEMORY.md", "TOOLS.md", "IDENTITY.md", "MISSION.md", "BOOTSTRAP.md", ] for fname in core_files: fpath = workspace / fname if fpath.exists(): content = read_file_safe(fpath) tokens = count_tokens(content) results["files"].append({ "name": fname, "path": str(fpath), "size_bytes": len(content.encode("utf-8")), "tokens": tokens, "category": "core", }) results["total_context_tokens"] += tokens results["categories"].setdefault("core", 0) results["categories"]["core"] += tokens # Memory files (if loaded into context) memory_dir = workspace / "memory" if memory_dir.exists(): for mf in sorted(memory_dir.glob("*.md")): content = read_file_safe(mf) tokens = count_tokens(content) results["files"].append({ "name": f"memory/{mf.name}", "path": str(mf), "size_bytes": len(content.encode("utf-8")), "tokens": tokens, "category": "memory", "note": "loaded per AGENTS.md rules (daily + cross-session)", }) # Only count today's + yesterday's + cross-session as always-loaded results["categories"].setdefault("memory", 0) results["categories"]["memory"] += tokens # Installed skills skill_dirs = [] for skills_root in [ workspace / "skills", Path.home() / ".openclaw" / "workspace" / "skills" ]: if skills_root.exists(): for skill_md in skills_root.rglob("SKILL.md"): skill_dir = skill_md.parent if skill_dir not in skill_dirs: ...[truncated 2876 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict scanning to the explicitly selected workspace by default. Remove the unconditional default-workspace skills root: ```python skill_roots = [workspace / "skills"] ``` 2. Introduce an explicit opt-in option such as `--include-global-skills` before accessing `~/.openclaw/workspace/skills`. 3. Require separate opt-in flags such as `--include-memory` and `--include-identity-files` for files likely to contain private agent state. 4. Resolve and validate every candidate path before reading it: ```python workspace = workspace.expanduser().resolve() candidate = candidate.resolve() if candidate != workspace and workspace not in candidate.parents: raise ValueError("Refusing to scan outside the selected workspace") ``` 5. Avoid following symlinks that resolve outside the authorized workspace, or apply the containment check after symlink resolution. 6. Use filesystem byte sizes for basic estimates where possible instead of reading complete sensitive files. If tokenization requires content, process data incrementally and discard it immediately. 7. Redact absolute paths by default. Return workspace-relative paths unless a separate `--show-absolute-paths` option is supplied. 8. Clearly document which state files are read, and display the final scan roots before execution so users can provide informed consent. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
The code broadly matches the declared theme: it is a workspace token/cost analyzer for OpenClaw that scans files, estimates token usage, and produces cost and optimization guidance without using API keys. However, there are material description-to-behavior gaps. Most importantly, the advertised multi-model coverage is overstated: the implemented pricing/comparison set does not include Gemini, Llama, Mistral, or Qwen, and instead includes DeepSeek models not mentioned in the description. Also, the claimed ability to identify redundant files is not actually implemented as a detection feature; the script only infers possible overlap from aggregate core token size. Finally, the 'zero dependencies' claim is not fully accurate because the script optionally depends on tiktoken for accurate counting. These are substantive enough to count as a mismatch, though the primary purpose remains aligned.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The manifest says the skill estimates token counts across GPT-4, Claude, Gemini, Llama, Mistral, and Qwen, but the code only defines pricing for Claude, GPT-4o, and DeepSeek variants. The report's cross-model comparison likewise omits Gemini, Llama, Mistral, and Qwen, so the actual behavior does not match the advertised scope.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The tool reads multiple core files, memory files, and skill files across the workspace to estimate token usage. While scanning is central to the tool's purpose, the current usage text does not explicitly warn users that file contents across these directories will be read, which is relevant for privacy-sensitive workspaces.

Missing User Warnings

Low
Confidence
81% confidence
Finding
The script collects absolute paths for workspace files and later includes them in JSON output, which can disclose local filesystem structure. Although this is not destructive, it is a user/system data exposure operation and the file does not provide an explicit warning in output, prompt, or comments about exporting those paths.

Intent-Code Divergence

Low
Confidence
86% confidence
Finding
The comment states 'Only count files the agent actually loads (SKILL.md + direct scripts)', which implies a limited subset. However, the implementation traverses all files under each skill directory matching several extensions, including markdown, JSON, and source files beyond direct scripts, so the documentation overstates selectivity.