Back to skill

Security audit

Qwen Vision Rename

Security checks for vulnerabilities and agentic risk

Overview

The skill is related to image renaming, but it defaults to changing local filenames and can send private images and an API key to configurable remote endpoints without enough user control.

Review carefully before installing. Use only on folders you explicitly choose, prefer dry-run first, avoid private or sensitive images unless you accept remote vision processing, and do not configure custom base URLs or public media URL mode unless you trust the endpoint and understand that images may leave your machine or remain publicly reachable.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/vision_rename.py:397
Finding
Local images and API credentials can be transmitted to an arbitrary endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/vision_rename.py:117-126`, `scripts/vision_rename.py:333-341`, and `scripts/vision_rename.py:397-421` **Vulnerability Type**: Unrestricted transmission of sensitive data and credentials **Risk Level**: High ### Evidence ```python def resolve_runtime_base_url(cli_value: str) -> str: skill_env = get_openclaw_skill_env("qwen-vision-rename") return first_non_empty( cli_value, os.getenv("DASHSCOPE_BASE_URL", ""), os.getenv("OPENAI_BASE_URL", ""), skill_env.get("DASHSCOPE_BASE_URL", ""), DEFAULT_BASE_URL, ) ``` ```python def local_image_to_data_url(path_str: str) -> str: path = Path(path_str).expanduser().resolve() if not path.is_file(): raise FileNotFoundError(f"Image file not found: {path}") mime, _ = mimetypes.guess_type(str(path)) if not mime: mime = "image/png" encoded = base64.b64encode(path.read_bytes()).decode("ascii") return f"data:{mime};base64,{encoded}" ``` ```python def call_vision_api( *, base_url: str, api_key: str, model: str, image: str, prompt: str, timeout: int, ) -> str: endpoint = base_url.rstrip("/") + "/chat/completions" payload = { "model": model, "messages": [ { "role": "user", "content": [ {"type": "text", "text": prompt}, {"type": "image_url", "image_url": {"url": image}}, ], } ], "temperature": 0, } resp = requests.post( endpoint, headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, json=payload, timeout=timeout, ) ``` ### Technical Analysis Cloud-based image recognition legitimately requires sending image data to a vision service. Base64 encoding is being used as a transport representation rath ...[truncated 2111 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Allowlist the documented DashScope HTTPS hosts by default. 2. Reject non-HTTPS endpoints and URLs containing embedded credentials. 3. Require an explicit, security-relevant confirmation before using any custom endpoint. 4. Do not automatically honor the generic `OPENAI_BASE_URL` variable for a Skill that uses a DashScope-specific credential. 5. Associate credentials with approved hosts and refuse to send a credential when the endpoint host does not match its provider. 6. Clearly disclose before execution that local image contents will be uploaded to an external vision service. 7. Consider adding a local-model mode for users who cannot transmit images externally. 8. Add automated tests verifying that HTTP endpoints, unexpected hosts, redirects to unapproved hosts, and host/credential mismatches are rejected. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/vision_rename.py:215
Finding
Private images can persist in a publicly served outbound directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/vision_rename.py:215-238` **Vulnerability Type**: Persistent exposure of sensitive media **Risk Level**: High ### Evidence ```python def publish_local_image(path: Path) -> str: media_base_url = resolve_media_base_url() if not has_public_media_base_url(media_base_url): return "" source_path = path prepared_path = prepare_image_for_remote_fetch(path) if prepared_path is not None: source_path = prepared_path publish_root = resolve_media_outbound_dir() / "vision-input" publish_root.mkdir(parents=True, exist_ok=True) stat = source_path.stat() mtime_ns = getattr(stat, "st_mtime_ns", int(stat.st_mtime * 1000000000)) digest_src = "{}:{}:{}".format(source_path.resolve(), stat.st_size, mtime_ns) digest = hashlib.sha1(digest_src.encode("utf-8")).hexdigest()[:10] stamp = dt.datetime.now().strftime("%Y%m%d-%H%M%S") suffix = source_path.suffix.lower() or ".bin" target_name = "vision-{}-{}{}".format(stamp, digest, suffix) target_path = publish_root / target_name if not target_path.exists(): shutil.copy2(str(source_path), str(target_path)) return media_base_url.rstrip("/") + "/vision-input/" + target_name ``` ### Technical Analysis When URL image mode is active and a public media base URL is configured, the script copies the selected image into the `vision-input` subdirectory of the configured outbound media directory. It then constructs a public URL for that copy. Publishing the image may be necessary for an API that only supports remotely fetchable image URLs. The implementation, however, provides no deletion, expiration, access control, or lifecycle management for the published copy. Optimized copies created elsewhere in the same workflow are also cached without automatic cleanup. The generated name uses a timestamp and a ten-character SHA-1-derived value based on the local path and file metadata. This is not a s ...[truncated 1553 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer authenticated direct uploads or Base64 request bodies over publicly hosted media. 2. If public retrieval is unavoidable, use short-lived signed URLs with strict expiration and single-object scope. 3. Generate filenames with a cryptographically secure random token rather than timestamps and truncated metadata hashes. 4. Delete each published copy in a `finally` block immediately after the remote request completes. 5. Implement scheduled cleanup for abandoned files left by crashes or interrupted operations. 6. Apply restrictive local permissions to outbound and cache directories. 7. Require explicit user consent before copying an image into a publicly served location. 8. Display the publication destination and retention period before processing. 9. Avoid logging public media URLs where unrelated users or services can access them. 10. Document and implement equivalent cleanup for `vision-input-cache`. ]]>

T01 · Skill Instruction Hijacking

Warning
Location
SKILL.md:15
Finding
Skill instructions mandate unconfirmed bulk file renaming<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:15-23` **Vulnerability Type**: Unsafe execution instructions overriding confirmation safeguards **Risk Level**: Medium ### Evidence ```markdown ## Runtime behavior (strict) - `qwen-vision-rename` is a skill name, not a built-in tool name. - First use the `read` tool to open this `SKILL.md`, then run the Python command below. - Never emit a tool call named `qwen-vision-rename`. - Always execute the script. Do not fabricate recognition results. - For rename requests, default to direct execution: run `rename-dir --apply`. - If the user explicitly says "预览/试运行/dry-run/先看方案", run without `--apply`. - Do not call `qwen-image` for rename tasks. - Requests like "整理图片/按内容分类整理" still map to this skill. This skill renames by content first; if the user explicitly asks to move files into folders, explain that separately. ``` ### Technical Analysis The Skill text instructs the Agent to always execute the script and to use `rename-dir --apply` by default. A safer workflow would generate a dry-run plan, show the proposed changes and selected directory, and apply the changes only after explicit approval. These instructions affect the Agent's execution policy when the Skill is loaded. Broad user requests such as organizing images are mapped directly to renaming behavior even when the user has not reviewed the target directory or generated names. Rollback support reduces the potential damage but does not eliminate it. The implementation writes the rollback file only after `apply_plan()` successfully returns. If an exception occurs after some files have been renamed but before completion, the expected rollback file may not be created, leaving a partially modified directory without the normal recovery artifact. ### Attack Path 1. A user makes a broad request to organize or rename images without explicitly requesting a preview. 2. The Agent loads `SKILL.md` and follows its strict instruction to run `rename-dir - ...[truncated 895 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make `rename-dir` without `--apply` the mandatory default. 2. Show the resolved target directory, number of affected files, and complete rename plan before mutation. 3. Require explicit user approval before rerunning with `--apply`. 4. Do not treat vague requests such as “organize images” as authorization for immediate filesystem changes. 5. Require confirmation when the directory was selected automatically. 6. Write a complete intended rollback journal before the first rename operation. 7. Update the rollback journal transactionally after each successful rename. 8. On failure, automatically reverse completed operations or provide a guaranteed recovery record. 9. Offer a limit and non-recursive mode by default to minimize the scope of accidental changes. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill description says it is for batch renaming local images, but the documented behavior also includes single-image description, rollback handling, remote API use, and potentially auto-selecting directories without explicit user input. This mismatch is dangerous because users and orchestrators may authorize a seemingly narrow local rename skill while it actually performs broader actions, including transmitting image contents externally and modifying files by default.

Vague Triggers

High
Confidence
95% confidence
Finding
The trigger phrases are broad enough to capture common requests like '整理图片' that may not mean 'rename local files by content using a remote vision service.' Over-broad activation can cause the agent to invoke a file-modifying workflow in situations where the user intended advice, organization suggestions, or non-destructive help, increasing the risk of unintended local changes and privacy-impacting uploads.

Credential Access

High
Category
Privilege Escalation
Content
def resolve_dotenv_paths() -> List[Path]:
    candidates = [
        Path.cwd() / ".env",
        Path(__file__).resolve().parent.parent / ".env",
    ]
    dedup = []  # type: List[Path]
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def resolve_dotenv_paths() -> List[Path]:
    candidates = [
        Path.cwd() / ".env",
        Path(__file__).resolve().parent.parent / ".env",
    ]
    dedup = []  # type: List[Path]
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

High
Confidence
99% confidence
Finding
Batch rename mode can process many local images and, depending on configuration, either upload their contents directly or publish copies to a public URL for remote retrieval, all without explicit disclosure during execution. In batch mode this is more dangerous because the privacy exposure scales from a single file to an entire directory of personal images.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill declares capabilities that imply file reads/writes, environment access, and network use, but does not constrain them with an explicit tool scope or permissions model. In a skill that can rename local files and send image content to a remote vision API, missing scope increases the chance of overreach, unintended file modification, or data exposure beyond what the user expects.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill instructs default direct execution of file renaming unless the user explicitly asks for a preview, but does not pair that behavior with a prominent warning or confirmation step. For a local file-modifying skill, defaulting to immediate writes raises the chance of accidental data changes, user surprise, and operational mistakes, even if rollback exists.

Vague Triggers

Medium
Confidence
87% confidence
Finding
The skill mandates use for loosely related requests and states that content-classification-style requests should still map to renaming first, even when the user may have asked for a different operation such as sorting or moving files. Ambiguous routing in a state-changing skill is risky because it biases the system toward destructive or privacy-sensitive actions without clearly bounded scope.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The default natural-language prompt explicitly instructs the model in Chinese and requires output such as titles in Chinese characters. This imposes a language/locale choice by default without offering the user an explicit opt-in or alternative locale selection.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The code can copy local images into a publicly served outbound directory and return a public URL for remote model access, which contradicts the apparent 'local rename' user expectation. This creates an unintended confidentiality risk because private local photos may be exposed over the internet in addition to being sent to a third-party vision API.

External Transmission

Medium
Category
Data Exfiltration
Content
"temperature": 0,
    }

    resp = requests.post(
        endpoint,
        headers={
            "Authorization": f"Bearer {api_key}",
Confidence
91% confidence
Finding
The HTTP POST itself is not inherently malicious, but here it is the mechanism used to send local image content and authorization headers to an external endpoint. In the context of a nominally local file-renaming skill, this external transmission is security-relevant because it exposes private image data to a remote service and depends on trustworthy endpoint configuration.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The describe flow transmits image content to a remote vision API after loading API credentials, but there is no explicit user-facing warning at the point of use that local image data leaves the machine. For a skill presented as image renaming/organization, this omission materially increases privacy risk and may violate user expectations around local-only handling.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding
The dependency is range-pinned rather than fixed to a single known-safe version, so builds may resolve to different requests releases over time, including versions with known advisories. Because this skill appears to process local files but may still rely on network access or URL handling through requests, leaving the exact resolved version unverifiable creates a real supply-chain and patch-management risk.

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding
For Python 3.7+ the manifest allows any requests version >=2.31.0, which prevents determining from the manifest alone whether the installed version is patched against all applicable advisories. Unpinned upperless ranges reduce reproducibility and can silently introduce vulnerable or behavior-changing releases into the environment.

Unverifiable Dependency: Pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
96% confidence
Finding
Pillow<9 permits a broad set of old Pillow releases, many of which have published vulnerabilities including image parsing issues and resource-consumption flaws. In the context of an image-renaming skill that processes local pictures, this is more dangerous than average because Pillow is directly exposed to attacker-controlled image inputs, making denial of service or potentially worse parser exploitation more plausible.

Intent-Code Divergence

Low
Confidence
71% confidence
Finding
The module docstring presents the available use cases as describing images, generating filename titles, and applying renames with a rollback file. However, the script also implements an active rollback mode that renames files back, which is a meaningful file-modification behavior omitted from the top-level documentation and therefore understates what the tool does.

Context-Inappropriate Capability

Low
Confidence
89% confidence
Finding
The stated purpose is batch renaming local image files based on image content, but the script exposes a separate describe mode that analyzes a single image and prints metadata without renaming anything. That capability may be related, but it is not justified by or declared in the manifest's rename-only workflow.

Static analysis

No suspicious patterns detected.