Back to skill

Security audit

Ellya--Your Virtual Companion

Security checks for vulnerabilities and agentic risk

Overview

This skill largely does what it claims, but it handles personal photos and derived body details with weak boundaries and has a file-path flaw that could expose local Markdown files.

Review this before installing. Only use it with photos you have permission to process, assume uploaded/reference images and detailed derived prompts may be sent to Google Gemini and saved locally, avoid sensitive or intimate images, and fix the style-name path traversal plus add clear consent and cleanup controls before broader use.

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

Error
Location
scripts/genai_media.py:264
Finding
Path Traversal in Style Loading Enables Unauthorized Local Markdown File Disclosure<![CDATA[ ## Vulnerability Details **File Location**: `scripts/genai_media.py:264-270`, with the vulnerable input flow at `scripts/genai_media.py:292-304` **Vulnerability Type**: Path traversal and unauthorized local file disclosure **Risk Level**: High ### Vulnerable Code ```python def load_style_prompt(style_name: str) -> str: style_file = STYLES_DIR / f"{style_name}.md" if not style_file.exists(): print(f"Style not found, skip: {style_name}") return "" with open(style_file, "r", encoding="utf-8") as f: content = f.read().strip() if not content: print(f"Style is empty, skip: {style_name}") return content ``` The attacker-controlled style name reaches this function through the following code: ```python def resolve_final_prompt(prompt: str | None, styles: list[str] | None, api_key: str) -> str: if styles: selected = styles[:3] loaded = [load_style_prompt(name) for name in selected] loaded = [s for s in loaded if s] if loaded: fused = fuse_style_prompts(loaded, api_key).strip() return fused or DEFAULT_PROMPT print("No valid style content found. Falling back to default prompt.") return DEFAULT_PROMPT return (prompt or "").strip() or DEFAULT_PROMPT ``` ### Technical Analysis The `style_name` argument originates from the repeatable `-s` command-line option and is incorporated directly into a filesystem path: ```python STYLES_DIR / f"{style_name}.md" ``` Although `sanitize_style_name()` exists elsewhere in the script, it is only used when creating style names and is not applied when loading them. The loader does not reject absolute paths, directory separators, or `..` traversal components. It also does not resolve the resulting path and verify that it remains beneath `STYLES_DIR`. Consequently, a value such as `../../private/notes` resolves to a path outside the intended style directory while retaining the automatically a ...[truncated 1988 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply strict validation to every style name before performing a lookup. Only permit the same normalized character set used when style files are created: ```python def load_style_prompt(style_name: str) -> str: normalized = sanitize_style_name(style_name) if not normalized or normalized != style_name: raise ValueError("Invalid style name") style_file = (STYLES_DIR / f"{normalized}.md").resolve() styles_root = STYLES_DIR.resolve() try: style_file.relative_to(styles_root) except ValueError: raise ValueError("Style path escapes the styles directory") if not style_file.is_file(): print(f"Style not found, skip: {normalized}") return "" return style_file.read_text(encoding="utf-8").strip() ``` 2. Reject names containing `/`, `\`, `..`, absolute path syntax, null bytes, or any character outside a narrow allowlist such as `[a-z0-9_]`. 3. Resolve and validate the final path with `Path.resolve()` and `Path.relative_to()` before checking or opening it. 4. Use `is_file()` rather than only `exists()` to ensure the target is a regular file. 5. If symbolic links may exist in `styles/`, reject symlinks or ensure the resolved target remains under the resolved style directory. 6. Add tests covering relative traversal, absolute paths, encoded separators, symlinks, empty names, and valid normalized style names. 7. Avoid forwarding file-derived content to an external model unless the file is confirmed to be an authorized style record. ]]>

other

Warning
Location
ANALYSIS_PROMPT.md:6
Finding
Style Analysis Collects and Persists Excessive Sensitive Physical Details<![CDATA[ ## Vulnerability Details **File Location**: `ANALYSIS_PROMPT.md:6-14`, with external image processing at `scripts/genai_media.py:792-798` **Vulnerability Type**: Excessive sensitive-data collection and third-party processing **Risk Level**: Medium ### Vulnerable Configuration and Code The analysis prompt explicitly requests intimate anatomical and highly identifying physical details: ```markdown ## Facial Features - Micro Analysis **Contour and Skin Texture**: Precise description of jawline sharpness, micro-texture of skin (pore visibility, radiance), cool or warm undertones. **Facial Features**: Eye detail (eyelash texture, lower lid blush, pupil highlight position), nose micro-shape (nostril flare, tip tilt), lip micro-movement (lip line depth, Cupid's bow sharpness, mouth corner angle). **Makeup and Hair**: Makeup color gradation (eyeshadow transition, highlight placement), hair gravity performance (tight or voluminous), root details, hairline shape, hair color changes under different lights. ## Body Features - Proportion and Physiological Details **Bone Structure**: Sharpness of right-angle shoulders, depth of clavicle, protrusion of elbows and ankles, ridges of the spine. **Core Deconstruction**: Chest (height, fullness, gathering, spacing, bottom line), Waist and Abs (waistline position, muscle definition, tautness), Buttocks (roundness, lateral curve, lift, waist-to-hip ratio span). **Skin and Marks**: Skin tone under tension, muscle line shadows, precise mole locations, faint veins, tattoo details. ``` The uploaded image and this instruction are sent to Gemini: ```python try: client = genai.Client(api_key=api_key) response = client.models.generate_content( model=DEFAULT_MODEL, contents=[part, instruction], ) text = extract_first_text(response) except Exception as exc: print(f"Analyze error: {exc}") return ``` The generated analysis is then persisted locally: ```python STYLES_DIR.mkdir(exist_ok=T ...[truncated 2595 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Redesign the analysis prompt to collect only information necessary for visual-style learning: - Clothing and accessories - Color palette - Fabric and texture - Composition and framing - Lighting - Background and scene - General, non-intimate pose characteristics - Broad aesthetic tags 2. Remove requests for: - Chest, buttock, and other intimate anatomical measurements - Mole and vein locations - Tattoos unless the user explicitly asks to preserve them - Skin pore and facial micro-geometry analysis - Other persistent identifying characteristics unrelated to style 3. Clearly notify users before analysis that the image will be sent to Google Gemini for processing. Obtain explicit consent before transmission. 4. Ask users to confirm that they own the image or have permission from the depicted person. 5. Add a post-processing filter that removes intimate anatomy, biometric-like details, and persistent identifying marks before saving the style record. 6. Store only the minimum required style description and define an expiration or user-controlled deletion mechanism for uploaded images and generated style files. 7. Restrict filesystem permissions on `styles/` and avoid placing sensitive derived profiles in broadly accessible plaintext files. 8. Provide commands or UI controls that allow users to inspect, edit, and permanently delete stored style records. 9. Document the external provider, data categories transmitted, retention behavior, and applicable privacy controls. ]]>
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
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (17)

Hidden Instructions

High
Category
Prompt Injection
Content
---
name: Ellya
description: OpenClaw virtual companion skill. Use it to bootstrap runtime files (SOUL and base image), guide user personalization, learn and store style prompts from uploaded photos, generate selfies from user prompts or autonomous style strategy, and generate a multi-pose photo series from a selected image.
---
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Hidden Instructions

High
Category
Prompt Injection
Content
---
name: Ellya
description: OpenClaw virtual companion skill. Use it to bootstrap runtime files (SOUL and base image), guide user personalization, learn and store style prompts from uploaded photos, generate selfies from user prompts or autonomous style strategy, and generate a multi-pose photo series from a selected image.
---
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Credential Access

High
Category
Privilege Escalation
Content
from dotenv import load_dotenv

load_dotenv()  # Load .env from project root (or any parent dir)

from google import genai
from google.genai import types
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The prompt explicitly instructs the model to perform highly granular analysis of intimate body regions and physiological details, including chest, buttocks, skin marks, veins, and body proportions, without any stated business need or safety boundary. That materially increases the risk of sexualized body analysis, invasive biometric inference, and misuse on images of private individuals or potentially minors.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The file includes a hard requirement to 'Use English' as part of the output instructions. This is a natural-language locale policy constraint and the prompt does not offer the user any language choice or explain a justified region-specific need.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The README explicitly instructs the skill to save user-provided photos, learned style data, and generated images to local paths such as assets/, styles/, and output/ without any notice about retention, access controls, or cleanup. In a companion-image skill handling personal photos, silent local persistence can expose sensitive biometric or intimate media to other local users, backups, logs, or later unintended reuse.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README tells operators to transmit generated images to users via the OpenClaw messaging command, but does not mention that media will leave the local generation context and be sent through an external messaging channel. For user photos and AI-generated likenesses, lack of transmission disclosure can cause privacy violations, accidental sharing to the wrong recipient, or non-consensual dissemination of sensitive content.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill explicitly instructs saving uploaded appearance photos and modifying personalization files like SOUL.md, but it does not require clear user notice, consent, or retention boundaries before persisting that data. In a companion/image-generation context, uploaded photos are sensitive personal data, so silent storage and profile modification can create privacy, consent, and data-governance risks.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The series-generation flow allows a selected user image to be analyzed, transformed into multiple derivative images, stored in an output directory, and then transmitted back, without an explicit privacy warning or confirmation step. Because this expands one uploaded image into a larger derived dataset, the absence of consent and storage/transmission disclosure increases privacy exposure.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
cmd += ["--message", message]

    try:
        subprocess.run(cmd, check=True)
        print("Sent via OpenClaw.")
    except FileNotFoundError:
        print("Warning: openclaw command not found. Skipping send.")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This code uploads user-provided image content to an external generative AI service during scene/character extraction without an explicit runtime disclosure or consent check. In a skill context, users may reasonably expect local processing, so silent third-party transmission can expose sensitive personal or biometric information.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The code sends scene and character descriptions derived from a user's image to an external model for classification and variation generation, again without an explicit warning at the point of use. Derived descriptions may still reveal sensitive attributes about a person, location, or activity, creating privacy and compliance risk.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
Series generation transmits the reference image and prompts to an external image model without an explicit user-facing disclosure in the execution path. Because this workflow is built around identity-preserving portrait generation, the privacy sensitivity is elevated: face images and descriptive prompts can contain personal data.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The generate command uploads local reference images and prompts to a remote AI provider with no explicit inline disclosure or confirmation. This can unintentionally leak private images, proprietary artwork, or sensitive prompt contents to a third-party service.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The analyze command sends a user-supplied image to an external AI service and stores the resulting style analysis locally, but does not clearly warn the user that third-party processing occurs. This is especially sensitive when images may include faces, private settings, or copyrighted material.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The template sets `language: en-US` as a fixed user-overridable setting, and nowhere in the file does it indicate that the user can choose another language or must opt into this locale. This is a natural-language policy concern because it imposes a specific language/locale by default rather than offering explicit choice.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The docstring states this function is kept only for backward compatibility and should not be called directly, implying it is effectively non-operational. In reality, it constructs an OpenClaw command and invokes subprocess.run to send media or messages, so the documentation understates the function's active side effects.

Static analysis

No suspicious patterns detected.