Back to skill

Security audit

Task Director

Security checks for vulnerabilities and agentic risk

Overview

This is a visible task-planning helper, but a path-handling flaw could let its CLI read or overwrite JSON files outside its stated storage folder.

Review the generated scenes before approving them, because command fields may install packages, remove files, or make network calls when the agent executes them. Avoid passing hand-written or untrusted movie IDs until the path traversal issue is fixed, and prefer a version that validates movie IDs and confines all reads and writes to ~/.openclaw/memory/movies/.

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/director.py:45
Finding

Arbitrary JSON File Access Through Unsanitized Movie IDs

Content
View full analysis

Vulnerability Details

File Location: scripts/director.py:45-54
Vulnerability Type: Path traversal and arbitrary file read/write
Risk Level: Medium

Vulnerable Code

python
def load_movie(movie_id):
    f = get_data_dir() / f"{movie_id}.json"
    if not f.exists():
        return None
    return json.loads(f.read_text(encoding="utf-8"))


def save_movie(movie):
    f = get_data_dir() / f"{movie['id']}.json"
    f.write_text(json.dumps(movie, ensure_ascii=False, indent=2), encoding="utf-8")

Technical Analysis

The application directly interpolates a CLI-controlled movie ID into a filesystem path without validating its format or verifying that the resolved path remains inside ~/.openclaw/memory/movies/.

A movie ID containing ../ components can traverse outside the intended storage directory. An absolute path is also unsafe because Python's pathlib discards the preceding base path when the right-hand operand is absolute.

This affects every command that passes an untrusted --id value to load_movie. After loading a document, mutating commands call save_movie, which independently trusts the id property stored inside that document. An attacker can therefore control both the source path and, through the loaded JSON content, the subsequent destination path.

The mandatory .json suffix limits accessible targets to paths ending in .json, and successful mutation requires the selected document to contain the structure expected by the invoked command. These constraints reduce, but do not eliminate, the vulnerability.

Attack Path

  1. The attacker identifies or creates a structurally valid JSON document outside ~/.openclaw/memory/movies/, such as /tmp/movie.json.
  2. The attacker supplies an absolute or traversal-based identifier to a command that accepts --id, for example:
    bash
    python scripts/director.py approve --id /tmp/movie
    
  3. load_movie() constructs the path from the unvalidated identifier an ...[truncated 1120 chars]
Remediation
View remediation

Remediation Suggestions

  1. Enforce the generated movie-ID format before every read or write:

    python
    import re
    
    MOVIE_ID_PATTERN = re.compile(r"^movie_[0-9]{7}$")
    
    def validate_movie_id(movie_id):
        if not isinstance(movie_id, str) or not MOVIE_ID_PATTERN.fullmatch(movie_id):
            raise ValueError("Invalid movie ID")
        return movie_id
    
  2. Resolve candidate paths and enforce containment within the movie data directory:

    python
    def movie_path(movie_id):
        movie_id = validate_movie_id(movie_id)
        base = get_data_dir().resolve()
        candidate = (base / f"{movie_id}.json").resolve()
        if candidate.parent != base:
            raise ValueError("Movie path escapes the data directory")
        return candidate
    
  3. Use the same validated path helper in both load_movie() and save_movie().

  4. Do not trust the id field read from persisted JSON. Retain the validated ID supplied to load_movie() and use that trusted value when saving. Alternatively, verify that the document's id exactly matches the validated filename ID before processing it.

  5. Reject absolute paths, path separators, dot components, and unexpected Unicode representations in identifiers as defense in depth.

  6. Perform writes atomically by creating a temporary file in the same protected directory, setting restrictive permissions, flushing it, and replacing the destination only after serialization succeeds.

  7. Add regression tests covering absolute IDs, ../ traversal, nested separators, malformed IDs, and persisted documents whose internal id differs from the filename.

Vulnerability Patterns
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (6)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill describes automated workflow execution, persists state under the user's home directory, and directs an agent to read/write files, but it does not declare any tool scope or permissions boundaries. In an agent ecosystem, this can lead to over-broad file access and unsafe execution of user- or skill-generated commands because the runtime has no explicit least-privilege contract to enforce.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest says the skill will 'execute step by step with fallback support,' but cmd_action does not actually execute any task logic. It marks state as running and prints JSON containing action and command for some external agent to execute, making this a workflow/state manager rather than an executor.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The primary title and descriptive content begin in Chinese, while later text switches to English, but the document does not state whether users may choose their preferred language. Under the policy rule, forcing or implicitly assuming a specific language without opt-in can be a natural-language policy concern.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The stated purpose emphasizes planning, review, and execution flow, but the implementation also creates and maintains persistent local state in ~/.openclaw/memory/movies. Persistent filesystem storage may be a meaningful behavior beyond a simple 'turn tasks into movie storyboards' description because it accumulates historical task data and notes across runs.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

The code hard-codes UTC+8 in now_iso(), which determines displayed and stored timestamps for all users. This imposes a locale-specific behavior without offering a user opt-in or explaining why that region-specific setting is required.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The JSON uses Chinese natural-language values for scene names, descriptions, and actions throughout the file. This imposes a specific language/locale on users without any indication of opt-in, alternative locale support, or region-specific justification, which matches the stated language policy concern.

Content

No source excerpt is available for this finding.