Back to skill

Security audit

wild-idea

Security checks for vulnerabilities and agentic risk

Overview

This is a real brainstorming skill, but it uses unsafe search and credential patterns that users should review before installing.

Install only if you are comfortable with the skill sending search terms to Tavily and using a local Tavily API key. Avoid using it with confidential product ideas unless external search is approved, and prefer replacing the curl/.env examples and unpinned home-directory helper with a scoped, reviewed API client.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
references/tavily-curl-example.md:34
Finding
Shell Command Injection Through Unescaped Search Queries<![CDATA[ ## Vulnerability Details **File Location**: `references/tavily-curl-example.md`, lines 34-53 **Vulnerability Type**: Shell command injection and unsafe secret handling **Risk Level**: High ### Vulnerable Code ```python key = terminal("grep TAVI ~/.openclaw/.env | cut -d'=' -f2", timeout=5)['output'].strip() queries = [ "候选建议1", "候选建议2", ] for q in queries: r = terminal( f'''curl -s "https://api.tavily.com/search" -H "Content-Type: application/json" -d '{{ "api_key": "{key}", "query": "{q}", "search_depth": "basic", "max_results": 3 }}' | python3 -c "import sys,json; d=json.load(sys.stdin); [print(r['title']+' | '+r['url'][:60]) for r in d.get('results',[])]"''', timeout=15 ) status = "🚫" if r['output'].strip() else "✅" print(f"{status} | {q}") ``` ### Technical Analysis The batch-search example interpolates `q` directly into a command string passed to `terminal`, which invokes a shell. The query is placed inside a single-quoted JSON argument without shell escaping or safe JSON serialization. A query containing a single quote can terminate the JSON argument. Subsequent shell metacharacters, such as `;`, `|`, command substitution, or redirection, can then be interpreted by the shell. Escaping for JSON alone would not be sufficient because shell parsing occurs separately. The Tavily API key is also interpolated into the command line. Depending on the terminal implementation and operating system, the resulting command may be exposed through process inspection, command logging, debugging output, or exception reports. Although the example currently contains static placeholder queries, it is explicitly designed as a reusable batch-search integration. It becomes exploitable when query values are derived from user input, generated counterpart names, search terms, or other untrusted content. ### Attack Path 1. An attacker supplies or causes the Skill to g ...[truncated 1360 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not construct shell commands from query text or credentials. 2. Replace `terminal` and `curl` with a native HTTP client such as `urllib.request` or a vetted Python HTTP library. 3. Build the request body using `json.dumps` rather than string interpolation. 4. Obtain the credential from the process environment instead of parsing a shared environment file: ```python import json import os import urllib.request key = os.environ["TAVILY_API_KEY"] for q in queries: if not isinstance(q, str): raise TypeError("Search query must be a string") if len(q) > 1000: raise ValueError("Search query is too long") payload = json.dumps({ "api_key": key, "query": q, "search_depth": "basic", "max_results": 3, }).encode("utf-8") request = urllib.request.Request( "https://api.tavily.com/search", data=payload, headers={"Content-Type": "application/json"}, method="POST", ) with urllib.request.urlopen(request, timeout=15) as response: result = json.load(response) ``` 5. Where supported by the API, transmit credentials in an authorization header rather than in the JSON body. 6. Ensure logs and error messages redact credentials. 7. If an external program must be used, invoke it with an argument array and without a shell. Do not place secrets directly in command-line arguments. 8. Add tests containing quotes, semicolons, command substitution syntax, newlines, and Unicode control characters to confirm that query values cannot alter command execution. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/search_char.py:14
Finding
Execution of an Unverified Mutable Script from the User Home Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/search_char.py`, lines 14 and 35-42 **Vulnerability Type**: Unverified external code dependency and dependency hijacking **Risk Level**: Medium ### Vulnerable Code ```python TAVILY_SCRIPT = pathlib.Path.home() / ".hermes" / "skills" / "openclaw-imports" / "openclaw-tavily-search" / "scripts" / "tavily_search.py" ``` ```python def tavily_search(query, max_results=3): """调用Tavily脚本搜索""" if not TAVILY_SCRIPT.exists(): return None result = subprocess.run( [sys.executable, str(TAVILY_SCRIPT), "--query", query, "--max-results", str(max_results), "--format", "brave"], capture_output=True, text=True, timeout=30 ) ``` ### Technical Analysis The Skill executes Python code located outside the audited package at a predictable path beneath the current user’s home directory. It verifies only that the target path exists. It does not verify: - The file’s cryptographic digest. - Its package version or provenance. - Ownership and write permissions. - Whether the path resolves through a symbolic link. - Whether the installed component matches a reviewed release. Using an argument array prevents shell injection through `query`, but it does not mitigate replacement of `tavily_search.py`. Python directly executes whatever content is present at that path. The dependency is also not declared as a pinned package in the manifest. Consequently, the effective code executed during the normal search workflow can differ from the code reviewed in this project. ### Attack Path 1. An attacker gains the ability to create or modify files under the expected `~/.hermes/skills/openclaw-imports/openclaw-tavily-search/` path. This could occur through another compromised Skill, an unsafe installer, a supply-chain compromise, an overly permissive directory, or prior access to the user account. 2. The attacker creates or replaces `scripts/tavily_search.py` with malicious Python code. ...[truncated 1115 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer implementing the Tavily HTTPS request directly inside this audited package, avoiding execution of external Python source files. 2. If a third-party library is required, install it through a controlled dependency mechanism and pin an exact version and cryptographic hash. 3. Do not resolve executable code through a hardcoded path in the user’s home directory. 4. If external execution is unavoidable: - Require an explicit trusted path in configuration. - Resolve the path with `Path.resolve()`. - Reject symbolic links and unexpected path traversal. - Verify file ownership and ensure it is not writable by other users. - Verify the file against a pinned cryptographic digest before every execution. - Fail closed if any integrity check fails. 5. Run the integration with reduced privileges and a minimal environment. 6. Restrict filesystem and network access through sandboxing where the host platform supports it. 7. Document the exact trusted dependency version and installation source in `package.json` and the Skill documentation. 8. Consider replacing the external integration with a self-contained implementation such as: ```python import json import os import urllib.request def tavily_search(query, max_results=3): payload = json.dumps({ "api_key": os.environ["TAVILY_API_KEY"], "query": query, "search_depth": "basic", "max_results": max_results, }).encode("utf-8") request = urllib.request.Request( "https://api.tavily.com/search", data=payload, headers={"Content-Type": "application/json"}, method="POST", ) with urllib.request.urlopen(request, timeout=30) as response: data = json.load(response) return data.get("results", []) ``` ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (28)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description promises a mechanism for injecting concrete examples from specific remote domains and directly pairing them with user-domain counterparts under two gates: a comfort check and prior-web-verification check. The supplied code does none of that. It is a simple random picker over a static list of Mao-era sayings and abstract thematic labels, outputting selected entries as JSON. There is no handling of user-domain counterparts, no remote-domain inventory matching the declaration, no gating logic, no web access, and no reroll behavior. This is a clear material mismatch in primary purpose and capabilities.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description describes a high-level content-generation mechanism: choosing concrete examples from specific remote domains, placing them directly against user-domain counterparts, and applying two gates (comfort and prior-web-verification). The supplied code instead implements a utility for generating a search query from random Chinese characters and fetching search results through an external search script. Its primary purpose is search/query generation, not analogy/domain injection. There is no logic related to the listed domains, no user-domain counterpart handling, no juxtaposition output, no comfort filter, and no reroll or already-done verification. This is a material description-behavior mismatch.

Credential Access

High
Category
Privilege Escalation
Content
```bash
# 读取 API key
KEY=$(grep TAVILY_API_KEY ~/.openclaw/.env | cut -d'=' -f2)

# 单条搜索
curl -s "https://api.tavily.com/search" \
Confidence
98% confidence
Finding
The example instructs readers to extract a credential from ~/.openclaw/.env, demonstrating direct access to local secrets from within skill content. In this context, the skill's stated purpose is ideation and verification, so embedding local credential harvesting behavior is broader than necessary and normalizes unsafe secret access patterns.

External Script Fetching

High
Category
Supply Chain
Content
KEY=$(grep TAVILY_API_KEY ~/.openclaw/.env | cut -d'=' -f2)

# 单条搜索
curl -s "https://api.tavily.com/search" \
  -H "Content-Type: application/json" \
  -d '{
    "api_key": "'"$KEY"'",
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Credential Access

High
Category
Privilege Escalation
Content
import json
from hermes_tools import terminal

key = terminal("grep TAVI ~/.openclaw/.env | cut -d'=' -f2", timeout=5)['output'].strip()

queries = [
    "候选建议1",
Confidence
99% confidence
Finding
This Python example programmatically shells out to grep a local .env file for credentials, then reuses that secret in later requests. That combines credential access with command execution and makes the skill materially more dangerous because it operationalizes secret extraction rather than merely mentioning configuration.

External Script Fetching

High
Category
Supply Chain
Content
for q in queries:
    r = terminal(
        f'''curl -s "https://api.tavily.com/search" -H "Content-Type: application/json" -d '{{
            "api_key": "{key}",
            "query": "{q}",
            "search_depth": "basic",
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Description-Behavior Mismatch

High
Confidence
96% confidence
Finding
The skill description claims targeted use of concrete remote domains such as hydrothermal vents, monasteries, bee dances, and similar categories. This code loads a file of common Chinese characters, randomly combines two characters or accepts an arbitrary query, and searches for that term, which does not implement the stated domain-injection or counterpart-juxtaposition behavior.

Lp3

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding
The skill declares operational behavior that includes file access, shell execution, and network access, but it does not define any tool scope or permission boundaries. In an agent environment, this increases the chance that the skill can invoke capabilities beyond what a user would reasonably expect, especially because the workflow explicitly calls scripts and external APIs.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The skill activates its second-iteration mode when the user says phrases like "iterate" or "go deeper," which are common conversational expressions and not specific to this skill's operation. The file provides no exclusion conditions or negative examples to distinguish ordinary discussion from an invocation signal.

External Transmission

Medium
Category
Data Exfiltration
Content
No fabricating.

## Web Verification (Tavily)
Use curl + Tavily API to verify counterparts are novel:
```bash
curl -s "https://api.tavily.com/search" \
  -H "Content-Type: application/json" \
Confidence
94% confidence
Finding
The skill instructs the agent to send generated search queries to Tavily via curl, which is an external transmission of potentially sensitive user-derived content. Because the skill's purpose is to transform user problems into novel counterparts, those queries may contain confidential product ideas, internal terminology, or business context that would be disclosed to a third party.

External Transmission

Medium
Category
Data Exfiltration
Content
## Web Verification (Tavily)
Use curl + Tavily API to verify counterparts are novel:
```bash
curl -s "https://api.tavily.com/search" \
  -H "Content-Type: application/json" \
  -d '{"api_key": "'"$TAVILY_API_KEY"'", "query": "your search", "search_depth": "basic", "max_results": 5}'
```
Confidence
95% confidence
Finding
This finding confirms a concrete outbound destination, https://api.tavily.com, and shows that the skill uses an API key plus user-influenced query text for web verification. In context, the danger is elevated because the skill repeatedly rerolls and searches for novelty, which can amplify leakage of proprietary prompts, concepts, or outputs to an external service.

Natural-Language Policy Violations

Medium
Confidence
83% confidence
Finding
The entire skill reference file is written only in Chinese and provides no indication that language choice is optional or that the skill is intentionally limited to a Chinese-speaking context. Under the policy rules, forcing a specific language without user opt-in can be a natural-language policy violation.

Natural-Language Policy Violations

Medium
Confidence
87% confidence
Finding
The document is written as output-format instructions in Chinese and explicitly prescribes presentation formats and phrases, but it does not mention any user opt-in for language selection. Under the policy rule, forcing a specific language without user choice is a natural-language policy concern unless the locale restriction is clearly documented and justified.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
This markdown file is entirely written in Chinese and the invocation guidance does not indicate any language choice, opt-in, or region-specific justification. Under the policy, a skill that effectively requires a specific language without user opt-in is a natural-language policy violation.

External Transmission

Medium
Category
Data Exfiltration
Content
KEY=$(grep TAVILY_API_KEY ~/.openclaw/.env | cut -d'=' -f2)

# 单条搜索
curl -s "https://api.tavily.com/search" \
  -H "Content-Type: application/json" \
  -d '{
    "api_key": "'"$KEY"'",
Confidence
89% confidence
Finding
This code transmits data to an external service, including a locally retrieved API key and search query content. External transmission is expected for web search, but embedding it in skill documentation with direct secret injection increases the chance that sensitive local context or credentials are sent out without clear authorization boundaries.

External Transmission

Medium
Category
Data Exfiltration
Content
KEY=$(grep TAVILY_API_KEY ~/.openclaw/.env | cut -d'=' -f2)

# 单条搜索
curl -s "https://api.tavily.com/search" \
  -H "Content-Type: application/json" \
  -d '{
    "api_key": "'"$KEY"'",
Confidence
89% confidence
Finding
This code transmits data to an external service, including a locally retrieved API key and search query content. External transmission is expected for web search, but embedding it in skill documentation with direct secret injection increases the chance that sensitive local context or credentials are sent out without clear authorization boundaries.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The example code invokes a terminal helper to run shell commands and reads a local secret from ~/.openclaw/.env even though this reference file is framed as search guidance, not local secret handling. It also interpolates both the API key and query text directly into a shell command, creating unnecessary command-execution and secret-exposure risk if copied into an agent workflow.

External Transmission

Medium
Category
Data Exfiltration
Content
for q in queries:
    r = terminal(
        f'''curl -s "https://api.tavily.com/search" -H "Content-Type: application/json" -d '{{
            "api_key": "{key}",
            "query": "{q}",
            "search_depth": "basic",
Confidence
94% confidence
Finding
The batch example sends dynamically constructed queries to a remote endpoint through a shell command assembled with Python f-strings. Because the query value is inserted into shell syntax, an attacker-controlled or malformed query could break quoting and trigger unintended shell behavior in addition to external data transmission.

External Transmission

Medium
Category
Data Exfiltration
Content
for q in queries:
    r = terminal(
        f'''curl -s "https://api.tavily.com/search" -H "Content-Type: application/json" -d '{{
            "api_key": "{key}",
            "query": "{q}",
            "search_depth": "basic",
Confidence
94% confidence
Finding
The batch example sends dynamically constructed queries to a remote endpoint through a shell command assembled with Python f-strings. Because the query value is inserted into shell syntax, an attacker-controlled or malformed query could break quoting and trigger unintended shell behavior in addition to external data transmission.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
This Python file contains user-facing natural-language content entirely in Chinese, including the module docstring and the seed texts it emits as output. Under the stated policy, forcing a specific language without user opt-in can be a locale-policy violation when no choice or justification is provided.

Context-Inappropriate Capability

Medium
Confidence
84% confidence
Finding
The script executes another skill script from a user-home directory path that is outside this project and not integrity-checked. In a skill ecosystem, this expands the trust boundary: if that external file is replaced or tampered with, running this script will execute attacker-controlled code under the current user's privileges.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
The manifest describes a dual-gate behavior including web verification of whether an idea was already done and rerolling if so. In this file, the code merely generates or accepts a query, invokes a Tavily search helper, and prints up to three results; there is no logic to determine whether a juxtaposition is already done, no comfort check, and no reroll path.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
User-provided or randomly generated queries are sent to an external search component without clear notice or opt-in, which creates a privacy and data-handling risk. In a skill context, users may not expect their input to be transmitted off-box, and sensitive terms could be disclosed to third-party services or logs.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""调用Tavily脚本搜索"""
    if not TAVILY_SCRIPT.exists():
        return None
    result = subprocess.run(
        [sys.executable, str(TAVILY_SCRIPT), "--query", query,
         "--max-results", str(max_results), "--format", "brave"],
        capture_output=True, text=True, timeout=30
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The template hard-codes Chinese output (for example, the title and body labels) without any apparent user language or locale negotiation. This can cause unsafe or misleading behavior when users expect another language, leading to misunderstanding of generated content and reduced usability, though it is not a direct code-execution or data-exfiltration issue.

Static analysis

No suspicious patterns detected.