Back to skill

Security audit

OpenClaw WhatsApp GIF

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its WhatsApp GIF purpose, but it can send external messages without an explicit confirmation step and has unsafe media download and temp-file handling that warrants review.

Review this before installing if the agent can send WhatsApp messages automatically. Prefer requiring explicit user confirmation and recipient verification before any send, keep telemetry and remote URL fallback disabled, and avoid enabling web-scrape fallback unless you accept the weaker sourcing path. The downloader and temp cache should be hardened before use in shared or higher-risk environments.

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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/send_gif.py:84
Finding

Redirect Allowlist Bypass and Unbounded Media Response Buffering

Content
View full analysis
max_bytes: raise ValueError(f"media too large ({len(data)} bytes)") ext = infer_extension(url, content_type) if ext not in {".mp4", ".gif", ".webm"}: raise ValueError(f"unsupported content type: {content_type or 'unknown'}") ``` ### Technical Analysis The function validates only the hostname in the initial candidate URL. Python's `urllib.request.urlopen` follows HTTP redirects automatically, but the code does not validate `resp.geturl()` or otherwise confirm that the final response still belongs to an allowed host. Consequently, an approved media URL that redirects can cross the configured network trust boundary. This is relevant if an approved provider is compromised, returns an attacker-influenced redirect, or exposes an open-redirect behavior. The configured `maxBytes` limit also does not impose an actual download or memory limit. `resp.read()` buffers the entire response in process memory before `len(data)` is compared with `max_byte ...[truncated 1764 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/send_gif.py:51
Finding

Predictable Shared Temporary Files Permit Symlink-Based File Overwrite

Content
View full analysis
str: return str(Path(tempfile.gettempdir()) / "openclaw-whatsapp-gif") ``` ```python def download_media(url: str, out_dir: Path, min_bytes: int, max_bytes: int, allowed_hosts, retries: int = 3): if not is_allowed_host(url, allowed_hosts): raise ValueError("url host is not allowed by policy") out_dir.mkdir(parents=True, exist_ok=True) last = None for i in range(retries): try: req = urllib.request.Request(url, headers={"User-Agent": "openclaw-whatsapp-gif/1.5"}) with urllib.request.urlopen(req, timeout=20) as resp: data = resp.read() content_type = resp.headers.get("Content-Type", "") if len(data) < min_bytes: raise ValueError(f"media too small ({len(data)} bytes)") if len(data) > max_bytes: raise ValueError(f"media too large ({len(data)} bytes)") ext = infer_extension(url, content_type) if ext not in {".mp4", ".gif", ".webm"}: raise ValueError(f"unsupported content type: {content_type or 'unknown'}") out_path = out_dir / f"gif_{hashlib.sha1(url.encode()).hexdigest()[:12]}{ext}" out_path.write_bytes(data) ``` ### Technical Analysis The default cache path is a fixed directory under the operating system's shared temporary directory. Media filenames are deterministic because they are generated from the first 12 hexadecimal characters of the SHA-1 hash of a public media URL. The implementation calls `mkdir(..., exist_ok=True)` without checking whether the existing path is owned by the current user, is a real directory rather than a link, or has secure permissions. It then uses `Path.write_bytes`, which ...[truncated 2123 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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The skill claims a narrowly scoped, safe, deterministic GIF workflow, but it also supports telemetry logging of user queries/media URLs, remote URL delivery/fallback, and generic target-based payload generation. In context, this is more dangerous because the skill operates in a messaging environment where user intent, recipient identifiers, and media URLs may be sensitive, and hidden logging or broader delivery modes can lead to privacy leakage or misuse.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
88% confidence
Finding

The skill claims a narrowly scoped, safe, deterministic GIF workflow, but it also supports telemetry logging of user queries/media URLs, remote URL delivery/fallback, and generic target-based payload generation. In context, this is more dangerous because the skill operates in a messaging environment where user intent, recipient identifiers, and media URLs may be sensitive, and hidden logging or broader delivery modes can lead to privacy leakage or misuse.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill invokes shell scripts, accesses environment variables, performs network fetches, and writes to temporary storage, but it does not declare any explicit tool scope or permissions. This weakens policy enforcement and reviewability because a host agent may grant broader capabilities than users or platform controls expect, especially for a skill that can download media and prepare outbound WhatsApp messages.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill description and default prompt emphasize sending a GIF in WhatsApp but contain no requirement for user confirmation, preview, or warning before performing the outbound message action. Because sending a message is a side effect in an external communication channel, lack of explicit confirmation can lead to accidental or unauthorized sends, reputational harm, or misuse if invoked in the wrong context.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The default prompt and implicit invocation allow this skill to be triggered by broad, common requests for GIFs in WhatsApp, which can cause the agent to take an external action without clear user intent specific to this skill. In a messaging context, ambiguous auto-invocation increases the chance of unintended content sourcing or sending to the wrong conversation, making this a real safety and security concern rather than a purely cosmetic prompt issue.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/providers.md (reported line 11)May include surrounding context.

md
return []
    params = {"api_key": key, "q": compact_query(expand_query(query)), "limit": str(limit), "rating": "g", "lang": "en"}
    try:
        data = http_get_json("https://api.giphy.com/v1/gifs/search?" + urllib.parse.urlencode(params))
    except Exception:
        return []
    out = []

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/find_gif.py (reported line 173)May include surrounding context.

python
return []
    params = {"api_key": key, "q": compact_query(expand_query(query)), "limit": str(limit), "rating": "g", "lang": "en"}
    try:
        data = http_get_json("https://api.giphy.com/v1/gifs/search?" + urllib.parse.urlencode(params))
    except Exception:
        return []
    out = []

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The optional search_tenor_web path scrapes Tenor HTML and extracts media URLs via regex, bypassing the stronger structure and metadata constraints provided by official APIs. In this skill's context, that weakens the claimed deterministic/safe sourcing model and can increase the chance of unstable behavior, unexpected content selection, or policy bypass if the page structure or embedded URLs change.

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/integration_test.py (reported line 17)May include surrounding context.

python
def run(cmd):
    return subprocess.check_output(cmd, text=True)


def main():

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/send_gif.py (reported line 37)May include surrounding context.

python
def run_find_gif(script_dir: Path, query: str, limit: int):
    py = sys.executable or "python3"
    cmd = [py, str(script_dir / "find_gif.py"), query, "--limit", str(limit), "--json"]
    out = subprocess.check_output(cmd, text=True)
    return json.loads(out)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

When telemetry is enabled, the script appends the raw user query, selected URL, errors, and mode to a temp-directory log file without any in-code notice, minimization, or retention controls. User queries in messaging contexts can contain sensitive personal data, and writing them to shared or weakly protected local storage can create an unintended privacy leak.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This code sends the user's query to external services (Tenor and Giphy) and uses sensitive environment variables for authentication, but there is no confirmation prompt, user-facing log/print, or explanatory comment/docstring warning that external providers will receive the query. For a code file, these outbound data transmissions and credential use meet the missing-warning criteria.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The stated purpose is to find and send reaction GIFs in WhatsApp chats; credential access is an implementation capability not justified by the manifest text itself. While common for API-backed integrations, this still expands the skill's operational capability beyond the user-facing purpose description.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

Accessing environment variables for API keys is a capability not evident from the manifest's narrow description of selecting and sending GIF reactions. This is likely an implementation detail, but the manifest does not disclose direct secret access by the skill.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
96% confidence
Finding

The request hard-codes the Giphy API language parameter to "en", which imposes a specific language/locale without any user choice or opt-in. This matches the natural-language policy violation category for forced language settings.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.