Back to skill

Security audit

Clawzempic

Security checks for vulnerabilities and agentic risk

Overview

This is mostly a read-only OpenClaw audit skill, but its SSH remote mode and unsafe terminal rendering deserve review before installation.

Install only if you are comfortable with a diagnostic skill reading OpenClaw metadata under ~/.openclaw. Use --remote only for hosts you administer and trust, prefer --json when auditing less-trusted data, and treat --fix output as suggested manual commands to review rather than automatic remediation.

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
lean-report.py:558
Finding
Unsanitized Audited Data Enables Terminal Escape-Sequence Injection<![CDATA[ ## Vulnerability Details **File Location**: `lean-report.py:187`, `lean-report.py:230-231`, `lean-report.py:277-294`, and `lean-report.py:558-567` **Vulnerability Type**: Terminal escape-sequence injection through untrusted report fields **Risk Level**: Medium ### Vulnerable Code Cron job names and IDs are read from the audited installation without sanitization: ```python name = job.get("name", job.get("id", "unknown"))[:50] ``` The untrusted value is incorporated into report findings and remediation metadata: ```python if issues: for issue in issues: results["findings"].append(("warn", f" [{name}] {issue}")) results["issues"].append({"job": name, "issue": issue}) else: results["findings"].append(("pass", f" [{name}] model={model} thinking={thinking} ✓")) ``` Session-store keys are similarly incorporated into findings without sanitization: ```python is_main = key.endswith(":main") # Flag bloated non-main sessions if pct >= 40 and not is_main: penalties += 8 results["waste_tokens"] += tokens results["findings"].append(("fail", f" {key}: {pct}% ({tokens:,} tokens) — bloated")) results["stale"] += 1 elif pct >= 25 and age_hours > 24 and not is_main: penalties += 4 results["waste_tokens"] += tokens results["findings"].append(("warn", f" {key}: {pct}% ({tokens:,} tokens), {age_hours:.0f}h stale")) results["stale"] += 1 elif pct >= 60 and is_main: penalties += 5 results["findings"].append(("warn", f" {key}: {pct}% — main session running hot")) ``` The resulting messages are written directly to an ANSI-capable terminal: ```python for level, msg in audit["findings"]: if level == "fail": print(f" {RED}✗{RESET} {msg}") elif level == "warn": print(f" {YELLOW}⚠{RESET} {msg}") elif level == "pass": print(f" {GREEN}✓{RESET} {msg}") else: print(f" {DIM}ℹ{RESET} {msg}") ``` ### Technical Analysis Cron names, cron IDs, and se ...[truncated 2401 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Introduce a terminal-output sanitizer and apply it to every string derived from audited files before rendering: ```python import re import unicodedata ANSI_ESCAPE_RE = re.compile( r""" \x1B (?: \[[0-?]*[ -/]*[@-~] # CSI | \][^\x07\x1B]*(?:\x07|\x1B\\) # OSC | [PX^_][^\x1B]*(?:\x1B\\) # DCS/SOS/PM/APC | [@-_] # Two-character escape ) """, re.VERBOSE, ) def safe_terminal_text(value): text = str(value) text = ANSI_ESCAPE_RE.sub("", text) return "".join( ch for ch in text if ch in "\t\n" or unicodedata.category(ch) not in {"Cc", "Cf"} ) ``` 2. Sanitize values at the output boundary rather than relying only on individual audit checks: ```python safe_msg = safe_terminal_text(msg) print(f" {YELLOW}⚠{RESET} {safe_msg}") ``` 3. Also sanitize cron names and session keys when creating findings. This provides defense in depth and prevents unsafe values from reaching other text-output paths such as `print_fixes()`. 4. Avoid permitting carriage returns in terminal messages because they can overwrite the current line. If multiline values are unnecessary, replace all newlines and tabs with visible escaped representations as well. 5. Keep raw values in structured JSON output only when required. Continue using `json.dumps()` so control characters remain JSON-escaped. 6. Add tests containing CSI cursor movement, screen-clearing sequences, OSC hyperlinks, OSC title changes, carriage returns, backspaces, and malformed escape sequences. Verify that human-readable output contains no untrusted control bytes. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill is presented primarily as a zero-dependency, stdlib-only audit, but the documented behavior includes remote SSH execution and a --fix mode that may alter state. This mismatch can cause users or higher-level agents to trust and run the skill in contexts intended for safe read-only diagnostics, when it actually has network reach and possible modification capability.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The script includes a built-in remote execution path that sends the local script to a remote host and executes it via SSH. In an audit utility, this expands the trust boundary significantly: misuse, operator confusion, or future script modification could cause commands to run on unintended systems, making the skill more dangerous than its stated purpose suggests.

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill advertises executable audit behavior, including reading local installation state, but does not declare any explicit tool scope such as permissions or allowed-tools. That omission weakens transparency and reviewability, making it easier for an agent or operator to invoke file-accessing behavior without a clearly bounded capability contract.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
Remote audit mode executes commands over SSH on another machine, but the skill description does not prominently warn about that operational boundary crossing. Users may assume the skill is confined to the local environment and unintentionally authorize command execution against remote infrastructure, increasing the risk of misuse or unexpected exposure.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The script behavior does not match the stated 'stdlib-only local audit' description: it can transmit and execute itself on a remote host over SSH and depends on an external helper script. This is dangerous because users may run it with trust assumptions that do not reflect its actual execution boundary, increasing the chance of unintended remote execution and misleading security review or approval.

Natural-Language Policy Violations

Low
Confidence
75% confidence
Finding
The phrase "When Jeffrey asks you" bakes in a specific named user rather than describing behavior generically or allowing user choice. This can create a policy/personalization issue because the skill appears scoped to one identity without documenting that restriction or offering a neutral alternative.

Static analysis

No suspicious patterns detected.