Back to skill

Security audit

OpenClaw Trends

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed internet trend aggregator; it has an unsafe embedded YouTube API key and makes external requests, but I found no hidden persistence, destructive actions, or private-data exfiltration.

Install only if you are comfortable with it making outbound web requests for OpenClaw searches. The embedded YouTube API key should be treated as exposed; a safer version would remove it, require a user-provided key through a protected environment or secret store, and document the exact third-party services contacted. Avoid adding the cron example unless you intentionally want scheduled background checks.

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/fetch_trends.py:23
Finding
Hardcoded YouTube API Key Exposed and Transmitted in Request URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fetch_trends.py`, lines 23-64 **Vulnerability Type**: Hardcoded secret and insecure credential handling **Risk Level**: Medium ### Vulnerable Code ```python # YouTube API Key (set via environment or default) YOUTUBE_API_KEY = os.environ.get("YOUTUBE_API_KEY", "AIzaSyC-4zq2k2ohAwtQM7lF8jPct_QhzpENG88") ``` The credential is subsequently included in an HTTP query parameter: ```python def search_youtube(days: int = 3, max_results: int = 10) -> list[dict]: """Search YouTube for OpenClaw videos using Data API v3.""" results = [] if not YOUTUBE_API_KEY: print("Warning: No YouTube API key set", file=sys.stderr) return results published_after = (datetime.now() - timedelta(days=days)).isoformat() + "Z" for query in SEARCH_TERMS[:2]: # Limit queries try: url = "https://www.googleapis.com/youtube/v3/search" params = { "part": "snippet", "q": query, "type": "video", "order": "date", "publishedAfter": published_after, "maxResults": max_results, "key": YOUTUBE_API_KEY, } full_url = f"{url}?{urllib.parse.urlencode(params)}" req = urllib.request.Request(full_url) with urllib.request.urlopen(req, timeout=10) as response: data = json.loads(response.read().decode()) ``` ### Technical Analysis The source code contains a reusable Google/YouTube API key as the default value used when `YOUTUBE_API_KEY` is not defined in the environment. Anyone who can read or download the Skill package can extract this credential without executing the script. The key is appended to the request URL as the `key` query parameter and sent to `www.googleapis.com`. HTTPS protects the request while it is in transit, but query strings can be retained by appli ...[truncated 2368 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Immediately revoke or rotate the exposed API key in the associated Google Cloud project. 2. Remove the hardcoded fallback and require runtime configuration: ```python YOUTUBE_API_KEY = os.environ.get("YOUTUBE_API_KEY") ``` 3. If the variable is absent, skip YouTube searches or terminate with a clear configuration error rather than silently using a shared credential. 4. Store the replacement key in an approved secret manager or protected runtime environment variable. Do not commit it to the repository, documentation, examples, or generated artifacts. 5. Restrict the replacement key to the YouTube Data API only. 6. Apply the strongest feasible application restrictions, quota limits, budget alerts, and usage monitoring in Google Cloud. 7. Use a separate project and credential for this Skill so compromise does not affect unrelated services. 8. Review source-control history, package releases, build artifacts, and logs for copies of the exposed key. 9. Avoid recording complete request URLs containing credentials. Redact the `key` parameter from application, proxy, and diagnostic logs. 10. Add automated secret scanning to development and release workflows to prevent future credential commits. 11. Update `SKILL.md` to remove the statement that a key is embedded and clearly document secure user-provided credential configuration. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (13)

Tainted flow: 'req' from os.environ.get (line 67, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
full_url = f"{url}?{urllib.parse.urlencode(params)}"
            
            req = urllib.request.Request(full_url)
            with urllib.request.urlopen(req, timeout=10) as response:
                data = json.loads(response.read().decode())
            
            for item in data.get("items", []):
Confidence
91% confidence
Finding
This request sends a YouTube API key in outbound network traffic, and the skill embeds a default key in code while also allowing environment-supplied credentials. Hardcoded or loosely handled API credentials can be abused if the source is exposed, logs leak URLs, or the key is reused beyond its intended scope.

Tainted flow: 'req' from os.environ.get (line 67, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
"User-Agent": "Mozilla/5.0 (compatible; OpenClaw-Trends/1.0)"
        })
        
        with urllib.request.urlopen(req, timeout=15) as response:
            html = response.read().decode("utf-8", errors="ignore")
        
        # Parse results from HTML
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The documented purpose says the skill fetches general OpenClaw trends, but the behavior includes use of an embedded external API credential and specific external-service interactions not transparently declared. This mismatch can mislead users and reviewers about the real trust boundary, data flows, and secret handling, making credential misuse and over-privileged execution more likely.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Content
b.request.urlopen(req, timeout=10) as response:
                data = json.loads(response.read().decode())
            
            for item in data.get("items", []):
                video_id = item["id"]["videoId"]
                snippet = item["snippet"]
                results.append({
                    "source": "YouTube",
                    "title": snippet["title"],
                    "description": snippet.get("description", "")[:200],
                    "url": f"https://youtube.com/watch?v={video_id}",
                    "date": snippet["publishedAt"][:10],
                    "thumbnail": snippet["thumbnails"].get("default", {}).get("url", ""),
                })
        except Exception as e:
            print(f"YouTube search error: {e}", file=sys.stderr)
    
    return results


def search_github(days: int = 3) -> list[dict]:
    """Search GitHub for OpenClaw repos, discussions, releases."""
    results = []
    
    try:
        # Check if gh CLI is available
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Lp3

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding
The skill documentation indicates capabilities that require shell, network, and possibly environment access, but it declares no explicit tool scope or permissions. This creates an authorization gap where the runtime may grant broader access than users or reviewers expect, increasing the risk of unintended external access or command execution.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The trigger language is broad enough to activate on general OpenClaw discussion, which can cause the skill to run in contexts where the user did not intend external aggregation. In context, this is more dangerous because the skill reaches multiple outside services, so accidental invocation may leak query context or produce unnecessary network activity.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
The skill performs external queries across multiple third-party services, but the description does not clearly warn users that their prompts may trigger outbound requests. This transparency failure can cause privacy, compliance, and user-consent issues, especially in environments where external lookups are restricted or monitored.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
Advertising an embedded YouTube API key is a strong indicator of insecure credential handling. Hardcoded API keys can be extracted, reused, abused for unauthorized API consumption, and may expose the operator to quota exhaustion, billing issues, or account sanctions.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The script handles a YouTube API credential despite being a simple content aggregation skill, and it includes a default embedded key in source. Embedding credentials in code creates unnecessary exposure risk through repository access, redistribution, or accidental disclosure, and expands the blast radius if the key has broader permissions.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
A hardcoded default API key combined with silent outbound requests is risky because users and operators may not realize a credential is being used and transmitted to third-party services. This can lead to secret leakage, unauthorized quota consumption, and compliance/privacy issues if network behavior is not clearly disclosed.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
        # Check if gh CLI is available
        subprocess.run(["gh", "--version"], capture_output=True, check=True)
        
        # Search repos
        cmd = ["gh", "search", "repos", "openclaw", "--limit", "10", "--json", "name,description,url,updatedAt,stargazersCount"]
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Search repos
        cmd = ["gh", "search", "repos", "openclaw", "--limit", "10", "--json", "name,description,url,updatedAt,stargazersCount"]
        proc = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
        
        if proc.returncode == 0:
            repos = json.loads(proc.stdout)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Low
Confidence
77% confidence
Finding
The script executes the external `gh` CLI to query GitHub, which is a subprocess operation. Although stderr messages note that GitHub is being checked, the code does not clearly disclose that an external command will be run on the user's system or document this behavior in a comment or docstring near the operation.

Static analysis

No suspicious patterns detected.