Back to skill

Security audit

Api Cost Optimizer

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly performs disclosed OpenClaw cost estimates, but its bundled scripts have unsafe path handling that can allow local code execution in crafted environments.

Review before installing. The skill does not appear designed to steal data or persist on the system, but its scripts should be fixed to pass paths to Python through arguments instead of interpolating them into python3 -c strings. Run it only in a trusted local environment, and treat its cost numbers as rough estimates rather than provider billing data.

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/analyze.sh:24
Finding

Python Code Injection Through Unescaped Filesystem Paths

Content
View full analysis

Vulnerability Details

File Location: scripts/analyze.sh:24-49, scripts/analyze.sh:74-86, scripts/heartbeat_diagnosis.sh:19-40, and scripts/heartbeat_diagnosis.sh:49-70
Vulnerability Type: Python source-code injection caused by embedding shell-derived paths into python3 -c programs
Risk Level: Medium

Vulnerable Code

From scripts/analyze.sh:24-49:

bash
if [ "$HEARTBEAT_INTERVAL" = "auto" ]; then
  if [ -f "$CONFIG_FILE" ]; then
    HEARTBEAT_INTERVAL=$(python3 -c "
import json, sys, re
try:
    with open('$CONFIG_FILE', 'r') as f:
        cfg = json.load(f)
    # heartbeat is at cfg.agents.defaults.heartbeat.every
    hb = cfg.get('agents', {}).get('defaults', {}).get('heartbeat', {})
    if isinstance(hb, dict):
        every = hb.get('every', 'auto')
    elif isinstance(hb, str):
        every = hb
    else:
        every = 'auto'
    if every not in ('auto', None, '') and every is not None:
        m = re.match(r'(\d+)([smh])', str(every))
        if m:
            val, unit = int(m.group(1)), m.group(2)
            print(str(val * {'s': 1, 'm': 60, 'h': 3600}[unit]))
        else:
            print('auto')
    else:
        print('auto')
except:
    print('auto')
" 2>/dev/null || echo "auto")
  fi

From scripts/analyze.sh:74-86:

bash
if [ -f "$CONFIG_FILE" ]; then
  CONFIG_TOOLS=$(python3 -c "
import json, sys
try:
    with open('$CONFIG_FILE', 'r') as f:
        cfg = json.load(f)
    tools = cfg.get('tools', cfg.get('skills', cfg.get('plugins', [])))
    if isinstance(tools, list):
        print(len(tools))
    else:
        print(0)
except:
    print(0)
" 2>/dev/null || echo "0")

From scripts/heartbeat_diagnosis.sh:19-40:

bash
  if [ -f "$CONFIG_FILE" ]; then
    # Try to extract heartbeat interval from openclaw.json
    # heartbeat is at cfg.agents.defaults.heartbeat.every
    INTERVAL=$(python3 -c "
import json, sys, re
try:
    with open('$CONFIG_FILE', 'r') as f:
        cfg = json.load(f)
    h
...[truncated 3897 chars]
Remediation
View remediation

Remediation Suggestions

Do not interpolate paths or any other shell-derived values into Python source code. Pass them as positional arguments and retrieve them through sys.argv.

For example:

bash
HEARTBEAT_INTERVAL=$(
  python3 - "$CONFIG_FILE" 2>/dev/null <<'PY'
import json
import re
import sys

config_file = sys.argv[1]

try:
    with open(config_file, "r", encoding="utf-8") as f:
        cfg = json.load(f)

    hb = cfg.get("agents", {}).get("defaults", {}).get("heartbeat", {})
    if isinstance(hb, dict):
        every = hb.get("every", "auto")
    elif isinstance(hb, str):
        every = hb
    else:
        every = "auto"

    if every not in ("auto", None, ""):
        match = re.fullmatch(r"(\d+)([smh])", str(every))
        if match:
            value = int(match.group(1))
            unit = match.group(2)
            print(value * {"s": 1, "m": 60, "h": 3600}[unit])
        else:
            print("auto")
    else:
        print("auto")
except (OSError, ValueError, TypeError, json.JSONDecodeError):
    print("auto")
PY
) || HEARTBEAT_INTERVAL="auto"

Apply the same pattern to every use of $CONFIG_FILE and $conf. Additional hardening should include:

  1. Use quoted arrays or null-delimited traversal rather than unquoted glob iteration when scanning agent files.
  2. Reject unexpected configuration paths where practical.
  3. Replace broad except: handlers with explicit exception classes so programming errors and injection attempts are not silently hidden.
  4. Use re.fullmatch() for heartbeat values to reject trailing data.
  5. Add regression tests with paths containing single quotes, double quotes, backslashes, spaces, newlines, and Python-like expressions.
  6. Run the scripts with the minimum required account privileges and avoid exposing sensitive credentials through inherited environment variables.
Vulnerability Patterns
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The code is broadly aligned with the stated cost-analysis theme: it inspects OpenClaw configuration, estimates spend, evaluates heartbeat interval, and generates recommendations. However, the description overstates what it does by claiming analysis of 'API usage patterns.' The script does not inspect actual API calls, logs, telemetry, bills, or historical usage; instead it relies on fixed assumptions such as 20 tasks/day, 6000 input tokens, and 1500 output tokens. It also counts skills/tools locally, but its tool-overhead calculation is effectively broken because it references an undefined variable, so that part of the diagnosis is not actually performed. This is a description-to-behavior mismatch, though not a malicious one: the implementation is a heuristic configuration-based estimator rather than a true usage-pattern analyzer.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 59)May include surrounding context.

md
./scripts/analyze.sh

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 104)May include surrounding context.

md
./scripts/analyze.sh

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 74)May include surrounding context.

md
./scripts/heartbeat_diagnosis.sh

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 110)May include surrounding context.

md
./scripts/heartbeat_diagnosis.sh

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 84)May include surrounding context.

md
./scripts/estimate.sh <heartbeat_seconds> <tasks_per_day> <avg_input_tokens> <avg_output_tokens>

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 107)May include surrounding context.

md
./scripts/estimate.sh <heartbeat_seconds> <tasks_per_day> <avg_input_tokens> <avg_output_tokens>

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

This markdown/manifest content includes generic invocation conditions such as 'agent costs are higher than expected' and 'monthly API bill surprises you,' which are broad situational phrases rather than specific trigger terms. Without explicit scope limits or negative examples, the skill could be invoked in loosely related cost discussions.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.