Back to skill

Security audit

Skill Pilot

Security checks for vulnerabilities and agentic risk

Overview

SkillPilot appears to be a real scheduler, but it gives invoked tools broad inherited environment access and stores user request history in plaintext.

Install only if you are comfortable letting this skill execute other local skills and CLIs. Run it in a restricted environment with minimal environment variables, avoid placing secrets under the skill directory before packaging, and treat its history and reports as sensitive local data because queries, URLs, errors, and context may be retained in plaintext.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/engine.py:352
Finding
Unrestricted Environment Variable Disclosure to Child Skills and External Tools<![CDATA[ ## Vulnerability Details **File Location**: `scripts/engine.py:352-359`; repeated at `scripts/engine.py:467-473`, `scripts/engine.py:528-533`, and `scripts/engine.py:569-574` **Vulnerability Type**: Violation of least privilege through unrestricted credential inheritance **Risk Level**: High ### Vulnerable Code ```python # Execute the script while inheriting environment variables env = os.environ.copy() result = subprocess.run( cmd, capture_output=True, text=True, timeout=timeout, env=env ) ``` Equivalent unrestricted inheritance occurs when invoking the OpenClaw, mcporter, and web-fetch command-line tools: ```python result = subprocess.run( cmd, capture_output=True, text=True, timeout=30, env=os.environ.copy() ) ``` ### Technical Analysis The execution engine copies the entire parent process environment and supplies it to every selected skill script and external command-line tool. The environment may contain `TAVILY_API_KEY`, `BRAVE_API_KEY`, `OPENCLAW_TOKEN`, proxy credentials, cloud credentials, database credentials, CI/CD secrets, and unrelated application tokens. Individual skills generally require only a small subset of these variables. Passing the complete environment breaks the principle of least privilege and expands the trust boundary from SkillPilot to every discovered or configured executable. The use of `shell=False` and list-form command arguments prevents ordinary shell injection in these execution paths, but it does not protect environment secrets from the child process itself. A child process can directly read inherited variables and use its legitimate network access to disclose them. ### Attack Path 1. An attacker introduces or compromises a skill under the local OpenClaw skills directory. 2. The skill presents a supported script such as `scripts/search.py`, `scripts/fetch.py`, or `scripts/search.js`. 3. The skill is selected through the configured tool pool, default routing, or full c ...[truncated 1231 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Construct a minimal environment instead of copying `os.environ`: ```python base_env = { "PATH": os.environ.get("PATH", ""), "LANG": os.environ.get("LANG", "C.UTF-8"), } ``` 2. Define a per-skill allowlist of required variables: ```python SKILL_ENV_ALLOWLIST = { "tavily-search": {"TAVILY_API_KEY"}, "brave-search": {"BRAVE_API_KEY"}, "multi-search-engine": set(), } ``` 3. Add only the explicitly approved variables for the selected skill: ```python env = base_env.copy() for name in SKILL_ENV_ALLOWLIST.get(self.skill_name, set()): value = os.environ.get(name) if value is not None: env[name] = value ``` 4. Do not forward `OPENCLAW_TOKEN`, cloud credentials, proxy passwords, or unrelated application secrets unless a documented invocation specifically requires them. 5. Run third-party skills in a restricted subprocess, container, or sandbox with limited filesystem and network access. 6. Require an explicit trust decision before executing newly discovered scripts. 7. Resolve external executables through trusted absolute paths and verify ownership and permissions. 8. Add tests confirming that undeclared secrets are absent from every child process environment. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/learning.py:37
Finding
Plaintext Persistence of Sensitive Queries, URLs, Errors, and Execution Context<![CDATA[ ## Vulnerability Details **File Location**: `scripts/learning.py:37-53` and `scripts/learning.py:69-71` **Vulnerability Type**: Sensitive-data exposure through excessive plaintext logging **Risk Level**: Medium ### Vulnerable Code ```python record = { 'timestamp': time.time(), 'datetime': datetime.now().isoformat(), 'category': getattr(request, 'category', 'unknown'), 'query': getattr(request, 'query', None), 'query_hash': self._hash_query(getattr(request, 'query', '')), 'url': getattr(request, 'url', None), 'selected_skill': getattr(result, 'used_skill', None), 'success': getattr(result, 'success', False), 'response_time': getattr(result, 'response_time', 0), 'fallback_count': getattr(result, 'fallback_count', 0), 'tried_skills': getattr(result, 'tried_skills', []), 'error': getattr(result, 'error', None), 'context': context or {}, } ``` ```python os.makedirs(os.path.dirname(self.history_file), exist_ok=True) with open(self.history_file, 'a', encoding='utf-8') as f: f.write(json.dumps(record, ensure_ascii=False) + '\n') ``` ### Technical Analysis The history subsystem generates a hash of each query but also stores the original query in the same record. The hash therefore provides no privacy protection. The log also retains complete URLs, error messages, and arbitrary context. These fields can contain: - Confidential search terms or user information. - API tokens embedded in URL query parameters. - Signed URLs and session identifiers. - Internal hostnames and resource paths. - Credentials accidentally included in errors. - Environmental or user-preference metadata. Records are appended to a plaintext JSON Lines file. The implementation does not apply field-level redaction, encryption, bounded retention, or explicit restrictive file permissions. History remains until a user manually invokes the clearing functionality. ### Attack Path 1. A user submits a confidential search query or a URL ...[truncated 1173 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not store raw queries by default. Retain only a keyed pseudonymous digest when correlation is required. 2. Remove the redundant raw `query` field or make it an explicit opt-in feature with a clear privacy warning. 3. Sanitize URLs before storage by removing user information, fragments, and sensitive query parameters. 4. Redact common secret patterns from errors and context, including API keys, bearer tokens, cookies, passwords, authorization headers, and signed URL parameters. 5. Replace arbitrary context persistence with an allowlist of non-sensitive statistical fields. 6. Create history directories with mode `0700` and files with mode `0600`. 7. Implement automatic retention, such as deletion or aggregation after a short configurable period. 8. Store aggregate performance statistics rather than request-level content wherever possible. 9. Provide a setting that disables history completely. 10. Document the exact retained fields and ensure report and packaging features cannot include raw history without explicit consent. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/package_skill.py:65
Finding
Skill Packaging Recursively Includes Potential Credentials and Runtime Telemetry<![CDATA[ ## Vulnerability Details **File Location**: `scripts/package_skill.py:65-90` **Vulnerability Type**: Accidental sensitive-file disclosure caused by permissive recursive packaging **Risk Level**: Medium ### Vulnerable Code ```python exclude_patterns = { "__pycache__", "*.pyc", "*.pyo", ".git", ".DS_Store", "*.log", "dist", } def should_exclude(path: Path) -> bool: for pattern in exclude_patterns: if pattern in str(path) or path.name == pattern or path.match(pattern): return True return False # Package files with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: for file_path in skill_path.rglob('*'): if file_path.is_file() and not should_exclude(file_path): relative_path = file_path.relative_to(skill_path) zipf.write(file_path, relative_path) print(f" Added: {relative_path}") ``` ### Technical Analysis The packaging routine recursively archives every regular file that does not match a short exclusion set. The exclusions cover bytecode, Git metadata, logs, and the distribution directory, but they do not cover common sensitive artifacts such as: - `.env` and `.env.*` files. - Private keys and certificates. - Credential or token configuration files. - Generated execution history. - Environment caches and local state. - Diagnostic reports. - Editor and temporary backup files. - Local databases or credential stores. Because the archive is intended for publication or sharing, local-only data below the selected skill directory can cross a security boundary without the user realizing it. The included file list is printed, but there is no approval gate, secret scan, or safe default manifest. ### Attack Path 1. A developer runs SkillPilot locally and generated caches, history, reports, or configuration accumulate below the skill directory. 2. The developer also stores a credential file, private key, `.env` file, or local configuration in ...[truncated 969 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace recursive default inclusion with an explicit allowlist of distributable files and directories. 2. Include only required artifacts such as `SKILL.md`, approved source files, reference documents, strategies, and non-sensitive default configuration. 3. Explicitly reject common sensitive patterns, including: ```text .env .env.* *.pem *.key *.p12 *.pfx id_rsa* credentials* secrets* token* history/ reports/ env_cache.json *.db *.sqlite ``` 4. Resolve each candidate path and reject symlinks or paths that escape the intended package root. 5. Run an automated secret scanner against the package contents before archive creation. 6. Generate a proposed archive manifest and require explicit confirmation before writing or publishing it. 7. Distinguish version-controlled source files from generated runtime files, preferably packaging only tracked or manifest-listed files. 8. Add automated tests demonstrating that credentials, history, cache, and report files are excluded. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/test.py:28
Finding
Shell Command Injection Through an Unquoted Project Path in the Test Utility<![CDATA[ ## Vulnerability Details **File Location**: `scripts/test.py:28` **Vulnerability Type**: Shell command injection **Risk Level**: Low ### Vulnerable Code ```python os.system(f"python3 {skill_dir}/scripts/validate.py {skill_dir}") ``` ### Technical Analysis `os.system()` passes the constructed string to a command shell. The value of `skill_dir` is derived from the script's filesystem location and is inserted twice without shell quoting. A directory name containing shell metacharacters, command substitution syntax, whitespace, or redirection operators changes how the shell interprets the command. Although a normal installation path is unlikely to contain such characters, an attacker who controls where the project is extracted or cloned can construct a malicious path. The path is not ordinary remote input, which limits exploitability. However, invoking a shell is unnecessary because the intended operation is a direct Python process execution. ### Attack Path 1. An attacker distributes or places the project inside a directory whose name contains shell syntax. 2. A user changes into the project and runs `python scripts/test.py`. 3. The script interpolates the attacker-controlled path into the `os.system()` command string. 4. The operating-system shell parses the metacharacters embedded in the path. 5. The injected command executes with the privileges of the user running the test. ### Impact Assessment Successful exploitation permits arbitrary command execution as the invoking local user. The attacker could read or modify any file available to that user, access the user's environment and credentials, install user-level persistence, or execute network commands. The risk is rated Low because exploitation requires control over the project path and user execution of the test utility. It does not affect normal routing operations unless the test script is run. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions Replace `os.system()` with list-form subprocess execution and avoid invoking a shell: ```python import subprocess subprocess.run( [ sys.executable, str(skill_dir / "scripts" / "validate.py"), str(skill_dir), ], check=True, shell=False, ) ``` Additional hardening measures: 1. Resolve and validate `skill_dir` before execution. 2. Verify that `validate.py` is a regular file within the expected project directory. 3. Handle `subprocess.CalledProcessError` and report validation failures accurately. 4. Add a regression test using a project path containing spaces and shell metacharacters. 5. Avoid all shell-string construction when arguments can be supplied as a list. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (94)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
python3 run_search.py "今日热点"  # ✅ 正常执行

# 注入尝试
python3 run_search.py "test; rm -rf /"  # ❌ 拒绝执行
python3 run_search.py "test | cat /etc/passwd"  # ❌ 拒绝执行
python3 run_search.py 'test $(whoami)'  # ❌ 拒绝执行
```
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Env Variable Harvesting

High
Category
Data Exfiltration
Content
**3. 环境变量传递** (`scripts/engine.py`)
```python
# 检查点:subprocess.run 的 env 参数
env = os.environ.copy()  # 继承环境变量
result = subprocess.run(cmd, env=env, ...)
```
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
```python
# ❌ 漏洞代码
cmd = f'''bash "skills/multi-search-engine/scripts/search.sh" "{query}"'''
subprocess.run(cmd, shell=True, ...)
```

**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
85% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Chaining Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Chaining Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Chaining Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Chaining Abuse

High
Category
Tool Misuse
Content
**风险**: 用户输入的 `query` 直接嵌入 shell 命令,攻击者可注入恶意命令
```bash
# 攻击示例
query = "test; rm -rf /"
# 执行:bash "skills/.../search.sh" "test; rm -rf /"
```
Confidence
75% confidence
Finding
Tool calls are chained to bypass individual safety checks or escalate capabilities beyond what any single tool call would allow.

Credential Access

High
Category
Privilege Escalation
Content
# 预期:拒绝执行,返回错误

# 3. 注入尝试 - 管道
python3 run_search.py "test | cat /etc/passwd"
# 预期:拒绝执行,返回错误

# 4. 注入尝试 - 命令替换
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# 预期:拒绝执行,返回错误

# 3. 注入尝试 - 管道
python3 run_search.py "test | cat /etc/passwd"
# 预期:拒绝执行,返回错误

# 4. 注入尝试 - 命令替换
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
This finding includes an undeclared filesystem write location for reports under the workspace, which is a real capability expansion if not clearly surfaced in permissions or behavior docs. Hidden or insufficiently declared writes can overwrite user data, persist sensitive outputs, or create an unexpected foothold for later processing, especially in an orchestration skill that handles broad inputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
This finding includes an undeclared filesystem write location for reports under the workspace, which is a real capability expansion if not clearly surfaced in permissions or behavior docs. Hidden or insufficiently declared writes can overwrite user data, persist sensitive outputs, or create an unexpected foothold for later processing, especially in an orchestration skill that handles broad inputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
This finding includes an undeclared filesystem write location for reports under the workspace, which is a real capability expansion if not clearly surfaced in permissions or behavior docs. Hidden or insufficiently declared writes can overwrite user data, persist sensitive outputs, or create an unexpected foothold for later processing, especially in an orchestration skill that handles broad inputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
This finding includes an undeclared filesystem write location for reports under the workspace, which is a real capability expansion if not clearly surfaced in permissions or behavior docs. Hidden or insufficiently declared writes can overwrite user data, persist sensitive outputs, or create an unexpected foothold for later processing, especially in an orchestration skill that handles broad inputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
This finding includes an undeclared filesystem write location for reports under the workspace, which is a real capability expansion if not clearly surfaced in permissions or behavior docs. Hidden or insufficiently declared writes can overwrite user data, persist sensitive outputs, or create an unexpected foothold for later processing, especially in an orchestration skill that handles broad inputs.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This finding includes an undeclared filesystem write location for reports under the workspace, which is a real capability expansion if not clearly surfaced in permissions or behavior docs. Hidden or insufficiently declared writes can overwrite user data, persist sensitive outputs, or create an unexpected foothold for later processing, especially in an orchestration skill that handles broad inputs.

Static analysis

No suspicious patterns detected.