Back to skill

Security audit

Image Generation (Ollama x/z-image-turbo · macOS only)

Security checks for vulnerabilities and agentic risk

Overview

The skill’s image generation purpose is coherent, but it combines broad activation with local command execution and WhatsApp sending without enough user control or safety bounds.

Install only if you are comfortable with a skill that can run a local Ollama command and send generated images or captions to WhatsApp. Use it with explicit recipient confirmation, avoid sensitive prompts, keep any FastAPI service bound to localhost, and add authentication, input limits, rate limits, and safe argument passing before broader use.

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

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:10
Finding

Shell Command Injection Through Prompt Interpolation in the Documented Agent Workflow

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 10-15
Vulnerability Type: Shell command injection
Risk Level: High

Vulnerable Code:

bash
Exécuter via `exec` avec **pty=true** (obligatoire) :
```bash
python3 /Users/openclaw/.openclaw/skills/ollama-x-z-image-turbo/runner.py \
  --prompt "<PROMPT>" \
  --width 1024 --height 1024 --steps 20 \
  --out /Users/openclaw/.openclaw/workspace/tmp/ollama_image.png -v

Technical Analysis

The Skill instructs the Agent to insert an untrusted, user-supplied image prompt directly into a shell command. Wrapping the placeholder in double quotes does not provide adequate shell escaping. A prompt containing a double quote can terminate the intended argument, after which shell operators or substitutions can be interpreted by the command processor.

Although runner.py invokes Ollama safely through subprocess.run() with an argument list and without shell=True, that protection does not address the outer shell command recommended by SKILL.md. The injection occurs before Python starts, when the Agent's execution tool parses the constructed command.

Attack Path

  1. An attacker submits an image-generation request containing shell metacharacters and a double quote designed to terminate the --prompt argument.
  2. The Agent replaces <PROMPT> with the attacker-controlled text as instructed by SKILL.md.
  3. The Agent sends the resulting string to a shell-capable execution tool.
  4. The shell interprets the injected operators or substitutions as commands rather than prompt data.
  5. The injected command executes with the operating-system privileges and environment available to the Agent process.

Impact Assessment

Successful exploitation could provide arbitrary command execution under the Agent's account. The attacker could access files readable or writable by that account, modify workspace or Skill content, invoke locally availa ...[truncated 206 chars]

Remediation
View remediation

Remediation Suggestions

  • Do not build a shell command by interpolating the prompt into a command string.
  • Invoke the Python executable through a structured argument-array interface, passing the entire prompt as one argument without shell parsing.
  • If the execution environment cannot accept argument arrays, pass the prompt through standard input or a securely created data file.
  • If shell execution is unavoidable, apply a platform-appropriate escaping function to every dynamic argument. Manual wrapping with quotes is not sufficient.
  • Update SKILL.md to prescribe a structured tool invocation rather than a copyable shell template.
  • Run the image generator with least privilege and without access to unrelated credentials or sensitive directories.

T09 · Insecure Skill Coding Practices

Warning
Location
generate_image.py:13
Finding

Unauthenticated and Unbounded Image Generation Enables Resource Exhaustion

Content
View full analysis

Vulnerability Details

File Location: generate_image.py, lines 13-57
Vulnerability Type: Missing authentication, input bounds, and resource controls
Risk Level: Medium

Vulnerable Code:

python
class ImageRequest(BaseModel):
    prompt: str
    width: int = 1024
    height: int = 1024
    steps: int = 20
    seed: int | None = None
    negative: str | None = None

@app.post("/generate-image/", response_class=Response)
async def generate_image(request: ImageRequest):
    url = f"{OLLAMA_URL}/api/generate"
    payload = {
        "model": MODEL,
        "prompt": request.prompt,
        "stream": False,
        "options": {
            "width": request.width,
            "height": request.height,
            "steps": request.steps,
        },
    }
    if request.seed is not None:
        payload["options"]["seed"] = request.seed
    if request.negative:
        payload["options"]["negative"] = request.negative

    r = requests.post(url, json=payload, timeout=180)
    if r.status_code != 200:
        raise HTTPException(status_code=500, detail=r.text)

    data = r.json()
    # Ollama image models return base64 in images[]
    if "images" in data and data["images"]:
        img_b64 = data["images"][0]
    elif "response" in data:
        img_b64 = data["response"]
    else:
        raise HTTPException(status_code=500, detail="No image data in response")

    try:
        img = base64.b64decode(img_b64)
    except Exception:
        raise HTTPException(status_code=500, detail="Invalid image base64")

    return Response(content=img, media_type="image/png")

Technical Analysis

The image-generation route has no authentication, authorization, rate limiting, or concurrency control. The request model accepts arbitrary integers for width, height, and steps, while prompt and negative-prompt lengths are unrestricted. Attackers who can reac ...[truncated 1530 chars]

Remediation
View remediation

Remediation Suggestions

  • Require authentication for the endpoint and authorize users before permitting model execution.
  • Bind the service to loopback by default unless remote access is explicitly required.
  • Enforce strict Pydantic constraints for prompt length, negative-prompt length, dimensions, and step count.
  • Reject non-positive dimensions and step values, and define conservative maximum values supported by the deployment.
  • Add per-user rate limits, global concurrency limits, request queues, and resource quotas.
  • Replace blocking requests calls in asynchronous handlers with an asynchronous HTTP client, or execute blocking work in a bounded thread pool.
  • Enforce maximum upstream response and decoded-image sizes before retaining data in memory.
  • Return sanitized upstream errors rather than exposing the complete Ollama response through detail=r.text.
  • Monitor generation latency, rejected requests, concurrency, and resource consumption.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
whatsapp_integration.py:7
Finding

Unauthenticated WhatsApp Relay Permits Unauthorized Outbound Messaging

Content
View full analysis

Vulnerability Details

File Location: whatsapp_integration.py, lines 7-17
Vulnerability Type: Missing authentication and outbound-message authorization
Risk Level: Medium

Vulnerable Code:

python
@app.post("/send-to-whatsapp/")
async def send_to_whatsapp(image_url: str, message: str, recipient: str):
    # Logique pour envoyer l'image à WhatsApp
    whatsapp_api_url = "https://api.whatsapp.com/send"
    payload = {
        "phone": recipient,
        "body": f"{message} \n Voici votre image : {image_url}"
    }

    response = requests.post(whatsapp_api_url, data=json.dumps(payload), headers={'Content-Type': 'application/json'})
    return response.json()

Technical Analysis

The endpoint accepts an arbitrary recipient, message, and image URL without authenticating the caller or verifying that the caller is authorized to contact the specified recipient. No rate limit, recipient allowlist, message-length restriction, or URL validation is applied.

The handler also performs synchronous outbound I/O in an asynchronous route and does not configure a request timeout. A slow or unresponsive upstream connection may therefore block a worker indefinitely. The practical messaging behavior depends on the external WhatsApp endpoint and any session or credentials available in the deployment; the source code itself contains no hard-coded WhatsApp credentials.

Attack Path

  1. An unauthorized caller reaches /send-to-whatsapp/.
  2. The caller supplies an arbitrary recipient, message, and image URL.
  3. The server constructs a WhatsApp payload directly from those values.
  4. The server submits the attacker-controlled payload to the external WhatsApp endpoint.
  5. If the deployment has a usable authenticated WhatsApp context and the external API accepts the request, the attacker can cause unauthorized messaging.
  6. Independently, repeated calls or deliberately slow upstream responses can occupy ...[truncated 513 chars]
Remediation
View remediation

Remediation Suggestions

  • Require strong authentication for the route.
  • Authorize each caller for the requested recipient and maintain an allowlist or verified-recipient registry where appropriate.
  • Use an officially supported, authenticated WhatsApp API and keep credentials in a dedicated secret manager.
  • Apply per-user and global rate limits, messaging quotas, and abuse monitoring.
  • Validate recipient syntax and enforce maximum lengths for messages and URLs.
  • Restrict image URLs to approved schemes and, where appropriate, approved hosts.
  • Configure finite connection and response timeouts for every outbound request.
  • Use an asynchronous HTTP client in the async route or move blocking calls to a bounded worker pool.
  • Handle non-JSON responses and upstream failures safely rather than returning response.json() unconditionally.
  • Record auditable sender, recipient, and request-result metadata without logging sensitive message content unnecessarily.
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 (12)

Tainted flow: 'url' from os.getenv (line 23, credential/environment) → requests.post (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 · generate_image.py (reported line 39)May include surrounding context.

python
if request.negative:
        payload["options"]["negative"] = request.negative

    r = requests.post(url, json=payload, timeout=180)
    if r.status_code != 200:
        raise HTTPException(status_code=500, detail=r.text)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

La description indique que l’image générée est envoyée sur WhatsApp sans avertissement clair sur la transmission de données à un service externe. Si le prompt ou l’image contient des informations sensibles, l’utilisateur peut ne pas comprendre qu’un contenu dérivé de sa demande sera partagé hors de l’environnement local.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Le déclenchement repose sur des formulations très générales autour de la génération d’image, ce qui peut activer la skill lors de demandes ambiguës ou conversationnelles. Comme la skill exécute ensuite une commande locale puis envoie automatiquement le résultat sur WhatsApp, une activation involontaire peut entraîner une action externe non désirée et une fuite de contenu vers un tiers.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

L’exemple « Fais une image de … » est suffisamment vague pour recouvrir du langage courant, ce qui augmente le risque de déclenchement involontaire. Dans ce contexte, ce risque est aggravé par le fait que la skill ne se limite pas à générer un fichier local mais prévoit aussi sa transmission via un canal externe.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

With no manifest available, the skill's purpose is unknown, so invoking the external ollama CLI as a subprocess is an unjustified capability under the provided policy. The code is not merely formatting or parsing data; it launches another program and depends on its side effects to generate files.

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 · runner.py (reported line 90)May include surrounding context.

python
t0 = time.time()
    try:
        res = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout, cwd=cwd)
    except subprocess.TimeoutExpired:
        elapsed = time.time() - t0
        log.error("Timeout after %.1fs", elapsed)

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · generate_image.py (reported line 39)May include surrounding context.

python
payload = {"model": "x/z-image-turbo", "prompt": "a cartoon cat astronaut on the moon, with big eyes and colorful fur"}

# Appel de l'API
response = requests.post(url, json=payload)

# Affichage du résultat
if response.status_code == 200:

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · test_api_call.py (reported line 10)May include surrounding context.

python
payload = {"model": "x/z-image-turbo", "prompt": "a cartoon cat astronaut on the moon, with big eyes and colorful fur"}

# Appel de l'API
response = requests.post(url, json=payload)

# Affichage du résultat
if response.status_code == 200:

External Transmission

Medium
Category
Data Exfiltration
Confidence
86% confidence
Finding

The code performs an external network transmission to WhatsApp using data derived directly from request parameters. External transmission is expected for this feature, but without safeguards such as consent, access control, validation, and abuse prevention, it creates data exfiltration and unauthorized messaging risk.

Content

Scanner excerpt · whatsapp_integration.py (reported line 10)May include surrounding context.

python
@app.post("/send-to-whatsapp/")
async def send_to_whatsapp(image_url: str, message: str, recipient: str):
    # Logique pour envoyer l'image à WhatsApp
    whatsapp_api_url = "https://api.whatsapp.com/send"
    payload = {
        "phone": recipient,
        "body": f"{message} \n Voici votre image : {image_url}"

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This endpoint transmits user-supplied recipient, message, and image URL to an external WhatsApp service without any visible authorization, confirmation, validation, or abuse controls. In a skill context, this can enable unintended third-party messaging, privacy leakage, and spam/abuse if an upstream agent or user can invoke the endpoint freely.

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 prompt payload to a local HTTP API using requests.post, but there is no confirmation prompt, warning comment about data transmission, or other user disclosure beyond a generic example label. Because network calls can transmit user-provided content, the file should make that behavior explicit.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language strings in the skill are written in French, including the inline comment and the user-visible message fragment 'Voici votre image'. This imposes a specific language on the interaction without any mechanism for user choice or documented locale constraint.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.