Back to skill

Security audit

Cost Guardian by Dexter Labs

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its cost-reporting purpose, but needs review because its script reads local OpenClaw session metadata and does not contain the agent path it accepts.

Review before installing if session labels or usage history are sensitive. Run the script only for trusted agent IDs, avoid passing path-like values to --agent, and prefer a patched version that validates agent IDs and enforces that resolved files stay under ~/.openclaw/agents. Treat cron setup as an explicit opt-in recurring job.

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/cost-report.py:57
Finding

Unrestricted Agent Identifier Allows Filesystem Path Traversal

Content
View full analysis

Vulnerability Details

File Location: scripts/cost-report.py, lines 57–65; user-controlled argument declared at line 224
Vulnerability Type: Path traversal and insufficient path validation
Risk Level: Medium

Vulnerable Code

python
def load_sessions(agent_id="main"):
    """Load session data from OpenClaw's session store."""
    home = Path.home()
    sessions_file = home / ".openclaw" / "agents" / agent_id / "sessions" / "sessions.json"
    
    if not sessions_file.exists():
        print(f"Error: Session file not found at {sessions_file}", file=sys.stderr)
        sys.exit(1)
    
    with open(sessions_file) as f:
        return json.load(f)

The value passed to this function is obtained directly from the command-line argument:

python
parser.add_argument("--agent", default="main", help="Agent ID (default: main)")

Technical Analysis

The user-controlled --agent value is inserted directly into a filesystem path without validating its syntax or confirming that the resolved path remains beneath the intended ~/.openclaw/agents directory.

Python's pathlib permits absolute path components and parent-directory components such as ... An absolute agent_id can replace the preceding base path, while traversal components can escape the expected agents directory. The program then appends sessions/sessions.json and reads the resulting file using the privileges of the running process.

Exploitation is constrained to targets whose resolved path ends in sessions/sessions.json. The target must also contain JSON compatible with the session-reporting logic to produce meaningful output. Nevertheless, no authorization or containment boundary is enforced.

Attack Path

  1. An attacker able to influence invocation arguments supplies an absolute or traversal-based value through --agent.

  2. For example, an invocation can reference an attacker-selected directory:

    ` ...[truncated 1306 chars]

Remediation
View remediation

Remediation Suggestions

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

    python
    import re
    
    AGENT_ID_PATTERN = re.compile(r"^[A-Za-z0-9_-]+$")
    
    def load_sessions(agent_id="main"):
        if not AGENT_ID_PATTERN.fullmatch(agent_id):
            raise ValueError("Invalid agent ID")
    
        agents_root = (Path.home() / ".openclaw" / "agents").resolve()
        sessions_file = (
            agents_root / agent_id / "sessions" / "sessions.json"
        ).resolve()
    
        try:
            sessions_file.relative_to(agents_root)
        except ValueError:
            raise ValueError("Agent path escapes the configured agents directory")
    
        with sessions_file.open(encoding="utf-8") as f:
            return json.load(f)
    
  2. Explicitly reject absolute paths, path separators, . components, and .. components even if an allowlist is also used.

  3. Resolve both the trusted base directory and final path, then enforce containment with Path.relative_to() before opening the file.

  4. Consider rejecting symlinked agent directories or opening files through a directory file descriptor with platform-supported no-follow protections if local untrusted users can modify the agent store.

  5. Handle OSError, JSONDecodeError, and schema-validation failures without exposing unnecessary absolute path details.

  6. Validate the loaded JSON against the expected session schema before processing it.

  7. Add tests covering absolute paths, nested separators, ../ traversal, symlink escapes, malformed JSON, and valid agent identifiers.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

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

md
Tell your agent:
> "Set up a daily cost report that runs at 8 AM and alerts me if I'm over $5/day"

The agent should create a cron job that:
1. Runs `python3 <skill-dir>/scripts/cost-report.py --hours 24 --budget 5.00`
2. Delivers the report via your preferred channel

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script reads ~/.openclaw/agents//sessions/sessions.json directly from the user's local session store without any notice, consent flow, or visible disclosure at the point of access. Even though this aligns with the skill's stated purpose of cost reporting, session metadata can reveal conversation labels, model usage, timestamps, and token volumes, so silent collection creates a privacy and transparency issue.

Content

No source excerpt is available for this finding.