Back to skill

Security audit

All-skill-list

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent local skill-listing purpose, but it automatically persists full local skill contents and uses unsafe Pickle cache loading that can execute tampered cache data.

Install only if you are comfortable with a tool that reads and caches all local OpenClaw skill instructions. Avoid using JSON/Markdown export on skills containing secrets or private operational notes, and replace the Pickle cache with JSON before broad use.

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

Error
Location
scripts/skill-list.py:20
Finding
Unsafe Pickle Deserialization Enables Arbitrary Code Execution## Vulnerability Details **File Location**: `scripts/skill-list.py`, lines 20–30 **Vulnerability Type**: Unsafe deserialization of a local cache file **Risk Level**: High ### Vulnerable Code ```python def load_cache() -> Dict[str, Any]: """加载缓存文件""" try: if not CACHE_FILE.exists(): return {"skill_names": [], "skills": [], "updated_at": ""} with open(CACHE_FILE, "rb") as f: cache = pickle.load(f) return cache except Exception as e: print(f"⚠️ 加载缓存失败: {e}") return {"skill_names": [], "skills": [], "updated_at": ""} ``` The cache is loaded automatically during normal execution: ```python # 1. 加载缓存 cache = load_cache() cache_skill_names = cache.get("skill_names", []) ``` ### Technical Analysis The application uses `pickle.load()` to deserialize `scripts/skills_cache.pickle`. Python Pickle is not a data-only serialization format: specially constructed objects can invoke arbitrary callables during deserialization through mechanisms such as `__reduce__`. Consequently, validation performed after `pickle.load()` cannot prevent exploitation because the payload executes while the file is being decoded. The surrounding exception handler also provides no protection against code execution that has already occurred. An attacker must first obtain the ability to create or modify the cache file. Plausible sources include another process or Skill running under the same account, an insecure deployment that permits modification of the Skill directory, or a tampered cache distributed alongside the project. The audited package did not contain a malicious cache file, so this finding identifies an exploitable coding flaw rather than confirmed malicious behavior. ### Attack Path 1. The attacker obtains write access to `scripts/skills_cache.pickle` or to the containing `scripts` directory. 2. The attacker creates a crafted Pickle object whose deserialization routine invokes an attacker-sele ...[truncated 1208 chars]
Remediation
## Remediation Suggestions 1. **Replace Pickle with a data-only format.** Store the cache as JSON and decode it with `json.load()`. The existing fields consist of strings, booleans, lists, and dictionaries, all of which are directly representable in JSON. 2. **Do not migrate an existing Pickle cache by loading it.** Delete or ignore legacy `skills_cache.pickle` files and regenerate the cache by scanning the Skill directories. Loading an old cache for conversion would retain the vulnerability. 3. **Validate the decoded structure.** After JSON parsing, verify that: - The top-level value is a dictionary. - `skill_names` is a list containing only strings. - `skills` is a list of dictionaries with expected fields and types. - `updated_at` is a string. - Unexpected or oversized values are rejected. 4. **Protect cache integrity.** Store the cache in a user-private cache directory with restrictive permissions. Reject symlinked cache files and verify that the resolved path remains in the expected directory. 5. **Use atomic writes.** Write new cache data to a securely created temporary file in the same directory, set restrictive permissions, flush it, and atomically replace the destination. This reduces corruption and race-condition risks. 6. **Apply least privilege.** Run the Skill in a constrained environment without unnecessary access to credentials, sensitive directories, or privileged system interfaces.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (7)

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill advertises installation of and interaction with a local skills directory, and the documentation clearly describes reading from and writing cache/export files, but it does not declare any explicit tool scope or permission boundary. That mismatch can cause operators or calling systems to underestimate the skill's filesystem access, increasing the chance of unintended local file exposure or modification.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill is designed to aggregate and export the full contents of all local SKILL.md files, which may include secrets, internal paths, operational notes, or sensitive prompt content from other local skills. Because the documentation presents this as a normal export feature without any warning, consent step, or filtering guidance, it creates a real risk of bulk local data disclosure.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
This code file contains extensive user-facing natural-language strings exclusively in Chinese, including help text, status messages, errors, and prompts. The script does not offer any language selection, fallback, or documented justification for a Chinese-only locale, which is a language-policy violation under the stated rule.

Insecure deserialization: pickle.load()

Medium
Category
Dangerous Code Execution
Content
return {"skill_names": [], "skills": [], "updated_at": ""}
        
        with open(CACHE_FILE, "rb") as f:
            cache = pickle.load(f)
            return cache
    except Exception as e:
        print(f"⚠️ 加载缓存失败: {e}")
Confidence
98% confidence
Finding
The script deserializes a local cache file with pickle.load(), which is unsafe for untrusted or tampered input because pickle can execute arbitrary code during loading. In this skill, the cache file sits in the script directory and is automatically loaded on startup, so any attacker who can replace or modify that file can achieve code execution when the tool runs.

Description-Behavior Mismatch

Medium
Confidence
88% confidence
Finding
The manifest describes a local skill directory aggregator supporting listing, description extraction, caching, diffing, and auto-update. However, this code also writes full aggregated exports to new files (`skills_export.json` and `all_skills.md`), which is an additional persistence/output behavior not stated in the manifest description. This is more than an internal implementation detail because it creates durable artifacts containing the collected skill metadata.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
The natural-language instructions and description are presented entirely in Chinese, which effectively imposes a specific language on users without opt-in. The file does not indicate that the skill is region-specific or offer an alternative language choice.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The inline comment states that paths are output '仅在详细或半详细模式' (only in detailed or half-detailed mode). The actual condition also prints paths when `verbose` is true, even if `show_level` is `simple`. This is a direct contradiction between code documentation and behavior.

Static analysis

No suspicious patterns detected.