Back to skill

Security audit

Huo15 Cost Tracker

Security checks for vulnerabilities and agentic risk

Overview

This cost tracker does not show exfiltration or hidden code, but it needs review because it can persist and reset cost history while also having accuracy and control gaps.

Review this skill before installing if you rely on its reports for budgeting or audit decisions. It appears local-only, but pricing may be wrong unless the hard-coded path is fixed, malformed or negative inputs can corrupt totals, and reset should be used carefully despite its backup behavior.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/track.sh:86
Finding

Negative numeric inputs can corrupt persistent cost statistics and suppress threshold alerts

Content
View full analysis

Vulnerability Details

File Location: scripts/track.sh, lines 86-163
Vulnerability Type: Insufficient input validation and accounting-data integrity failure
Risk Level: Medium

Vulnerable Code:

bash
record_call() {
    local input_tokens=0
    local output_tokens=0
    local cache_read=0
    local cache_write=0
    local model="unknown"
    local duration=0
    
    while [ $# -gt 0 ]; do
        case "$1" in
            --input-tokens)
                input_tokens=$2
                shift 2
                ;;
            --output-tokens)
                output_tokens=$2
                shift 2
                ;;
            --cache-read-tokens)
                cache_read=$2
                shift 2
                ;;
            --cache-write-tokens)
                cache_write=$2
                shift 2
                ;;
            --model)
                model=$2
                shift 2
                ;;
            --duration)
                duration=$2
                shift 2
                ;;
            *)
                shift
                ;;
        esac
    done
    
    init_stats
    
    local cost=$(calculate_cost $input_tokens $output_tokens "$model")
    
    python3 - "$STATS_FILE" "$input_tokens" "$output_tokens" "$cache_read" "$cache_write" "$model" "$duration" "$cost" << 'PYEOF'
import json
import sys
from datetime import datetime

stats_file = sys.argv[1]
input_tokens = int(sys.argv[2])
output_tokens = int(sys.argv[3])
cache_read = int(sys.argv[4])
cache_write = int(sys.argv[5])
model = sys.argv[6]
duration = int(sys.argv[7])
cost = float(sys.argv[8])

try:
    with open(stats_file, 'r') as f:
        stats = json.load(f)
    
    stats['totalCalls'] += 1
    stats['totalInputTokens'] += input_tokens
    stats['totalOutputTokens'] += output_tokens
    stats['totalCache
...[truncated 2802 chars]
Remediation
View remediation

Remediation Suggestions

  1. Require all token counts, cache counts, and durations to be decimal integers greater than or equal to zero.
  2. Verify that every option requiring a value has a following argument before accessing $2.
  3. Reject unknown options instead of silently ignoring them.
  4. Repeat validation in Python so the persistence boundary does not rely solely on shell validation.
  5. Reject negative or non-finite calculated costs before updating the statistics file.
  6. Apply reasonable upper bounds to prevent accidental or malicious records from creating implausibly large totals.
  7. Return a nonzero exit status when validation or persistence fails.

Example shell-side validation:

bash
validate_nonnegative_integer() {
    local name="$1"
    local value="$2"

    if [[ ! "$value" =~ ^[0-9]+$ ]]; then
        echo "Invalid $name: expected a non-negative integer" >&2
        exit 1
    fi
}

validate_nonnegative_integer "input token count" "$input_tokens"
validate_nonnegative_integer "output token count" "$output_tokens"
validate_nonnegative_integer "cache read token count" "$cache_read"
validate_nonnegative_integer "cache write token count" "$cache_write"
validate_nonnegative_integer "duration" "$duration"

Python should independently enforce the same invariant before modifying persistent state:

python
values = {
    "input_tokens": input_tokens,
    "output_tokens": output_tokens,
    "cache_read": cache_read,
    "cache_write": cache_write,
    "duration": duration,
}

for name, value in values.items():
    if value < 0:
        raise ValueError(f"{name} must be non-negative")

if not math.isfinite(cost) or cost < 0:
    raise ValueError("cost must be finite and non-negative")

T09 · Insecure Skill Coding Practices

Note
Location
scripts/track.sh:43
Finding

Hard-coded pricing configuration path silently produces incorrect cost estimates

Content
View full analysis

Vulnerability Details

File Location: scripts/track.sh, lines 43-59
Vulnerability Type: Hard-coded environment-specific path and fail-open configuration handling
Risk Level: Low

Vulnerable Code:

bash
get_model_pricing() {
    local model="$1"
    
    python3 - "$model" << 'PYEOF'
import json
import sys
model = sys.argv[1]
try:
    with open("/Users/jobzhao/.openclaw/workspace/skills/huo15-cost-tracker/config/pricing.json") as f:
        config = json.load(f)
    if "models" in config and model in config["models"]:
        inp = config["models"][model].get("inputTokens", 0.01)
        out = config["models"][model].get("outputTokens", 0.01)
        print(f"{inp} {out}")
    else:
        print("0.01 0.01")
except:
    print("0.01 0.01")
PYEOF
}

Technical Analysis

Although the script derives the installation-relative configuration directory in CONFIG_DIR, get_model_pricing ignores it and opens an absolute path tied to the original author's macOS home directory.

When the Skill is installed for another user or at another path, opening this file fails. A bare except block suppresses every error—including missing files, malformed JSON, permission failures, and unexpected programming errors—and silently substitutes $0.01 per million input and output tokens.

This fallback can substantially understate the configured prices. For example, the included gpt-4o configuration specifies $2.50 per million input tokens and $10.00 per million output tokens, but an ordinary installation outside the hard-coded directory will use $0.01 for both.

Attack Path

  1. The Skill is installed anywhere other than /Users/jobzhao/.openclaw/workspace/skills/huo15-cost-tracker.
  2. A caller records token use for a configured model such as gpt-4o.
  3. The pricing loader attempts to open the author-specific absolute path.
  4. The file operation fails, and the broad e ...[truncated 1064 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pass the already-derived $CONFIG_DIR/pricing.json path to Python rather than embedding an author-specific absolute path.
  2. Catch specific exceptions such as FileNotFoundError, PermissionError, and json.JSONDecodeError.
  3. Emit configuration errors to standard error and return a nonzero status instead of silently continuing with misleading prices.
  4. If fallback pricing is required by design, make it explicit in configuration and issue a prominent warning whenever it is used.
  5. Validate that loaded prices are finite, numeric, and non-negative.
  6. Treat an unknown model separately from an unavailable or invalid configuration file.

Example hardened implementation:

bash
get_model_pricing() {
    local model="$1"
    local pricing_file="$CONFIG_DIR/pricing.json"

    python3 - "$model" "$pricing_file" << 'PYEOF'
import json
import math
import sys

model = sys.argv[1]
pricing_file = sys.argv[2]

try:
    with open(pricing_file, encoding="utf-8") as handle:
        config = json.load(handle)
except (FileNotFoundError, PermissionError, json.JSONDecodeError) as exc:
    print(f"Unable to load pricing configuration: {exc}", file=sys.stderr)
    raise SystemExit(1)

try:
    model_config = config["models"][model]
    input_price = float(model_config["inputTokens"])
    output_price = float(model_config["outputTokens"])
except (KeyError, TypeError, ValueError) as exc:
    print(f"Invalid or missing pricing for model {model!r}: {exc}", file=sys.stderr)
    raise SystemExit(1)

if (
    not math.isfinite(input_price)
    or not math.isfinite(output_price)
    or input_price < 0
    or output_price < 0
):
    print("Pricing values must be finite and non-negative", file=sys.stderr)
    raise SystemExit(1)

print(input_price, output_price)
PYEOF
}
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill documents shell scripts that read and write local files, including workspace data and configuration, but does not declare any tool scope or permission boundaries. In an agent environment, missing explicit scope can let the skill operate with broader file access than users expect, increasing the chance of unintended file modification or data exposure.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger list includes broad natural-language phrases such as '花费了多少' and 'token 统计', which could match ordinary conversation and invoke the skill unexpectedly. Unintended activation is risky here because the skill can run local scripts and interact with persistent usage data, potentially causing side effects without clear user intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill exposes a reset operation that appears to clear stored statistics, but the documentation gives no warning that this is destructive or irreversible. If invoked accidentally or through unintended triggering, users could lose billing, token, or session tracking history needed for audit or cost control.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The file’s natural-language content, including the title, comments, and all user-facing output strings, is entirely in Chinese. This imposes a specific language on users without any opt-in, fallback, or documented region-specific justification, which matches the locale-policy violation criteria.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script's comments, help text, and status/output strings are consistently written in Chinese, forcing a specific language for user interaction. There is no mechanism to let the user choose a language or documentation indicating that the skill is intentionally limited to a Chinese-speaking context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The reset_stats function clears accumulated usage history and writes the emptied structure back to the stats file, which is a destructive file-write operation. Although it prints success after the fact, there is no prior confirmation prompt or warning before the irreversible reset action is performed.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This JSON file contains human-readable descriptions such as "模型定价配置" and "MiniMax M2.1 模型" in Chinese. Under the policy criteria, forcing a specific language without opt-in or justification is a natural-language locale violation, and the file does not indicate that the skill is region-specific or offers a language choice.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.