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

Content
View full analysis
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
View remediation
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
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.

Content

Scanner excerpt · scripts/fetch_trends.py (reported line 68)May include surrounding context.

python
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", []):

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/fetch_trends.py (reported line 180)May include surrounding context.

python
"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

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.

Content

No source excerpt is available for this finding.

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
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · scripts/fetch_trends.py (reported line 77)May include surrounding context.

python
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

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/fetch_trends.py (reported line 94)May include surrounding context.

python
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"]

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/fetch_trends.py (reported line 98)May include surrounding context.

python
# 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)

Missing User Warnings

Low
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.