Back to skill

Security audit

Wuli Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill is for Wuli image and video generation, but it has review-worthy network, file-opening, and undeclared cookie behavior.

Review before installing. Use only with non-sensitive media and trusted URLs, run it in a network-restricted environment if possible, unset WULI_EXTRA_COOKIE unless explicitly needed, and be aware that generated files may be saved locally and opened automatically.

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

T09 · Insecure Skill Coding Practices

Error
Location
skill.py:215
Finding
Unrestricted Remote Media Fetching Enables SSRF and External Data Relay<![CDATA[ ## Vulnerability Details **File Location**: `skill.py:215-262` **Vulnerability Type**: Server-Side Request Forgery (SSRF), unrestricted network access, and unbounded data transfer **Risk Level**: High ### Vulnerable Code ```python def upload_url_media(media_url, token): """Download a remote media file and re-upload it to OSS.""" print(f"Downloading remote media: {media_url} ...") req = urllib.request.Request(media_url) with urllib.request.urlopen(req, timeout=60) as resp: media_data = resp.read() ct = resp.headers.get("Content-Type", "") ext = ".jpg" for suffix, mime in CONTENT_TYPES.items(): if mime in ct: ext = suffix break url_path = urllib.parse.urlparse(media_url).path if "." in url_path.split("/")[-1]: ext = "." + url_path.split("/")[-1].rsplit(".", 1)[-1].lower() filename = f"upload{ext}" encoded_filename = urllib.parse.quote(filename) print(f"Re-uploading to OSS as {filename} ...") resp = api_request("GET", f"{API_BASE}/image/getUploadUrl?filename={encoded_filename}", token) if not resp.get("success"): print(f"Error: Failed to get upload URL: {json.dumps(resp, ensure_ascii=False)}", file=sys.stderr) sys.exit(1) upload_url = resp["data"]["uploadUrl"] # Build public URL from upload_url (strip query params) parsed = urllib.parse.urlparse(upload_url) public_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}" put_req = urllib.request.Request(upload_url, data=media_data, method="PUT") put_req.add_header("Content-Type", "application/octet-stream") with urllib.request.urlopen(put_req, timeout=120) as _: pass width, height = get_image_size(media_data) print(f"Upload complete: {public_url}") return public_url, width, height ``` ### Technical Analysis The `media_url` value originates from the command-line options `--image_url`, `--end_image_url`, and `--video_url`. It ...[truncated 2525 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only `https` URLs. Reject `file`, `ftp`, `data`, and all other schemes. 2. Resolve the hostname before connecting and reject every address in loopback, private, link-local, multicast, unspecified, documentation, and reserved ranges for both IPv4 and IPv6. 3. Disable automatic redirects or implement a redirect handler that validates every destination before following it. 4. Protect against DNS rebinding by ensuring the validated address is the address used for the connection. 5. Introduce an explicit allowlist of trusted media hosts where feasible. 6. Enforce a strict maximum download size using `Content-Length` where available and a bounded streaming loop regardless of that header. 7. Validate the response MIME type and verify the file signature before uploading it. 8. Reject non-image content for image parameters and non-video content for video parameters. 9. Stream validated content to bounded temporary storage rather than loading the entire response into memory. 10. Require clear user confirmation before fetching a URL outside an approved domain set. 11. Apply outbound network controls at the sandbox or container level to block access to internal and metadata networks. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
skill.py:154
Finding
Undeclared Environment Cookie Is Silently Transmitted to Wuli API<![CDATA[ ## Vulnerability Details **File Location**: `skill.py:154-161` **Vulnerability Type**: Undeclared secret access and sensitive-data transmission **Risk Level**: Medium ### Vulnerable Code ```python def api_request(method, url, token, data=None, content_type="application/json"): headers = { "Authorization": f"Bearer {token}", "Accept": "application/json", } extra_cookie = os.environ.get("WULI_EXTRA_COOKIE") if extra_cookie: headers["Cookie"] = extra_cookie ``` ### Technical Analysis The Skill manifest declares only `WULI_API_TOKEN` as a required secret. However, the implementation also reads `WULI_EXTRA_COOKIE` from the process environment and attaches its value to every request made through `api_request()`. This behavior is not declared in `manifest.json` or in the Skill's setup requirements. The documentation describes Bearer-token authentication and does not explain why an additional cookie is needed. Environment variables commonly contain authentication material. Reading and transmitting an undeclared variable violates least-privilege and informed-consent principles. Although the current API endpoints are constructed from the fixed HTTPS base `https://platform.wuli.art/api/v1/platform`, the cookie is still disclosed to an external service without an explicit permission declaration. ### Attack Path 1. The host process or execution environment contains a `WULI_EXTRA_COOKIE` variable, potentially left by another integration or prior session. 2. The user invokes any Skill action. 3. `api_request()` reads the variable without notifying the user. 4. The value is added as a `Cookie` header to API requests sent to `platform.wuli.art`. 5. The external service receives and can process or log the additional authentication material. No attacker-controlled URL is required for this path because the transmission is built into every API request. ### Impact Assessment The exposed scope is limited to the contents an ...[truncated 462 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `WULI_EXTRA_COOKIE` handling if Bearer-token authentication is sufficient. 2. If the cookie is operationally required, declare it in `manifest.json` as an optional secret and document its exact purpose. 3. Require an explicit command-line option or other affirmative opt-in before using the cookie. 4. Send it only to the specific endpoint that requires it rather than attaching it to every API request. 5. Avoid inheriting unnecessary environment variables by running the Skill with a minimal environment. 6. Ensure cookies have the narrowest possible scope, short expiration, and least privilege. 7. Never print the cookie or include it in exception messages, request diagnostics, or telemetry. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
skill.py:287
Finding
Network-Downloaded Results Are Automatically Opened Without Content Validation<![CDATA[ ## Vulnerability Details **File Location**: `skill.py:287-303` and `skill.py:499-523` **Vulnerability Type**: Unsafe automatic file handling and unbounded download **Risk Level**: Medium ### Vulnerable Code ```python def download_file(url, filename): req = urllib.request.Request(url) with urllib.request.urlopen(req, timeout=120) as resp: Path(filename).write_bytes(resp.read()) def open_file(filepath): """Open a local file with the OS default viewer after download.""" system = platform.system() try: if system == "Darwin": subprocess.Popen(["open", filepath]) elif system == "Windows": os.startfile(filepath) elif system == "Linux": subprocess.Popen(["xdg-open", filepath]) except Exception: pass ``` The downloaded files are opened automatically after the generation result is received: ```python if media_type == "IMAGE": downloaded = [] for i, item in enumerate(results, 1): task_id = item.get("taskId") url = nw_urls.get(task_id) or item.get("imageUrl") if url: filename = f"wuli_image_{timestamp}_{i}.png" src = "no-watermark" if task_id in nw_urls else "watermarked" print(f"Downloading ({src}): {filename}") download_file(url, filename) downloaded.append(filename) print(f"\nDownloaded {len(downloaded)} image(s) to current directory") for f in downloaded: open_file(f) else: task_id = results[0].get("taskId") if results else None url = nw_urls.get(task_id) or (results[0].get("imageUrl") if results else None) if url: filename = f"wuli_video_{timestamp}.mp4" src = "no-watermark" if task_id in nw_urls else "watermarked" print(f"Downloading ({src}): {filename}") download_file(url, filename) print("Video downloaded to current directory") open_file(filename) ``` ### Technical Analysis Result UR ...[truncated 2428 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove automatic opening from the default workflow. 2. Add an explicit `--open` option that requires user consent. 3. Restrict result downloads to HTTPS URLs on an allowlist of expected Wuli or CDN hosts. 4. Validate every redirect destination using the same host and IP restrictions. 5. Enforce conservative maximum sizes for images and videos using bounded streaming reads. 6. Verify MIME types and file signatures before saving or opening files. 7. Reject HTML, scripts, executables, archives, and content that does not match the expected output format. 8. Save downloads using exclusive file creation or safely generated paths to avoid overwriting existing files. 9. Consider scanning downloaded files before opening them. 10. Surface download and viewer errors instead of silently suppressing all exceptions, while ensuring error messages do not disclose secrets. ]]>
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 (21)

Tainted flow: 'put_req' from pathlib.Path.read_bytes (line 205, file read) → urllib.request.urlopen (network output)

High
Category
Data Flow
Content
file_data = path.read_bytes()
    put_req = urllib.request.Request(upload_url, data=file_data, method="PUT")
    put_req.add_header("Content-Type", "application/octet-stream")
    with urllib.request.urlopen(put_req, timeout=120) as _:
        pass

    width, height = get_image_size(file_data)
Confidence
80% confidence
Finding
File contents flow to a network sink. This may indicate data exfiltration of sensitive files.

Tainted flow: 'put_req' from pathlib.Path.read_bytes (line 205, file read) → urllib.request.urlopen (network output)

High
Category
Data Flow
Content
put_req = urllib.request.Request(upload_url, data=media_data, method="PUT")
    put_req.add_header("Content-Type", "application/octet-stream")
    with urllib.request.urlopen(put_req, timeout=120) as _:
        pass

    width, height = get_image_size(media_data)
Confidence
91% confidence
Finding
The skill downloads arbitrary remote content from user-supplied URLs and then re-uploads it to Wuli storage, effectively acting as a generic fetch-and-forward network relay. In an agent environment, this expands capability beyond image/video generation and can be abused to access internal resources or transfer sensitive content via SSRF-style retrieval.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill advertises and documents capabilities that involve environment access, local file reads/writes, network operations, and shell execution, but it does not declare any explicit tool scope or permissions boundary. This increases the chance that an agent runtime may invoke the skill with broader capabilities than users expect, enabling uploads, downloads, and local execution effects without clear consent or confinement.

Shadow Command Trigger

Medium
Category
Trigger Abuse
Confidence
84% confidence
Finding
The trigger phrase 'edit image' overlaps with a common built-in verb pattern and can shadow or hijack ordinary user requests that are not meant for this external skill. If auto-selected, the skill could route user images and prompts to a third-party API, causing unexpected disclosure, charges, and file operations.

Shadow Command Trigger

Medium
Category
Trigger Abuse
Confidence
83% confidence
Finding
The trigger phrase 'create artwork' is highly generic and conflicts with common creation intents, making accidental invocation plausible. In this skill's context, accidental invocation is more dangerous because the skill performs external network requests, may upload media, and can auto-download/open outputs.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger list includes broad generic phrases like 'generate image', 'generate video', 'edit image', and 'create artwork', which can match many unrelated user requests. Over-broad triggers can cause the agent to select this skill unexpectedly, leading to unintended API calls, media uploads, costs, or disclosure of user-provided files to a third-party service.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation states that local files may be auto-uploaded, remote URLs may be fetched and re-uploaded, results are auto-downloaded, and outputs may be auto-opened, but it does not prominently warn about privacy, bandwidth, storage, or local system side effects. Users may unknowingly send sensitive media to an external platform or trigger local file creation/opening behavior they did not intend.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
"default": "true"
    },
    "no_optimize": {
      "description": "Disable prompt optimization when you need the raw prompt sent as-is.",
      "default": "false"
    }
  }
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
"default": "true"
    },
    "no_optimize": {
      "description": "Disable prompt optimization when you need the raw prompt sent as-is.",
      "default": "false"
    }
  }
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
"default": "true"
    },
    "no_optimize": {
      "description": "Disable prompt optimization when you need the raw prompt sent as-is.",
      "default": "false"
    }
  }
Confidence
85% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The documentation shows a bearer token directly in example headers and code without an explicit warning to use placeholders only and never paste real secrets into shared logs, screenshots, or source files. Even if the token string appears illustrative, this pattern normalizes unsafe secret handling and can lead users or downstream tool builders to hardcode or expose live API credentials.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The upload examples instruct users to download third-party media and re-upload local or remote files to Wuli OSS, but do not clearly warn that this transmits potentially sensitive content to an external service. In a skill context, this can cause accidental disclosure of private images, videos, or copyrighted material because the examples encourage transfer as a normal preprocessing step.

External Transmission

Medium
Category
Data Exfiltration
Content
UPLOAD_URL=$(echo $UPLOAD_RESP | jq -r '.data.uploadUrl')

# 2. PUT 上传文件(Content-Type 必须为 application/octet-stream)
curl -X PUT \
  -H "Content-Type: application/octet-stream" \
  --data-binary @photo.jpg \
  "$UPLOAD_URL"
Confidence
86% confidence
Finding
This example performs an explicit external upload of a local file to a presigned URL, which is a real data egress action. In this skill's domain, external transmission is expected functionality, but it is still security-relevant because local user content may be uploaded off-device without adequate privacy notice, validation, or user confirmation.

External Transmission

Medium
Category
Data Exfiltration
Content
UPLOAD_URL=$(echo $UPLOAD_RESP | jq -r '.data.uploadUrl')

curl -X PUT \
  -H "Content-Type: application/octet-stream" \
  --data-binary @clip.mp4 \
  "$UPLOAD_URL"
Confidence
86% confidence
Finding
This example uploads a local video file to an external presigned URL, creating a direct path for outbound transfer of potentially sensitive media. While consistent with the API's intended use, the lack of adjacent warnings makes accidental disclosure more likely, especially for agent users who may not realize local assets are being copied to third-party infrastructure.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
Accepting arbitrary remote URLs and fetching them directly gives the skill broad URL-retrieval behavior unrelated to the narrow intent of generation. In a local or enterprise agent context, this can be abused for SSRF-like access to internal services, metadata endpoints, or as a blind content transfer mechanism.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill performs significant side effects—fetching remote URLs, writing files locally, and opening them—without an explicit user warning or separate consent step. In agent settings, hidden side effects reduce user awareness and can turn normal media prompts into unexpected network access and local file/application activity.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
Automatically opening downloaded files is unrelated to the stated purpose of generating media and introduces a local side effect that can activate external applications on untrusted content. That increases the attack surface substantially, especially on systems where handlers may execute scripts, render active content, or expose parser vulnerabilities.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
system = platform.system()
    try:
        if system == "Darwin":
            subprocess.Popen(["open", filepath])
        elif system == "Windows":
            os.startfile(filepath)
        elif system == "Linux":
Confidence
88% confidence
Finding
The skill automatically launches downloaded files with the OS default handler via `open`, which creates an unintended local code/content execution surface. Although the call is not shell-invoked, opening attacker-controlled downloaded content can trigger risky applications, browser handlers, or vulnerable previewers without explicit user consent.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
elif system == "Windows":
            os.startfile(filepath)
        elif system == "Linux":
            subprocess.Popen(["xdg-open", filepath])
    except Exception:
        pass
Confidence
88% confidence
Finding
The Linux `xdg-open` invocation automatically hands downloaded content to the desktop's default application, which is outside the core image/video generation function. Even without shell injection, this can expose users to malicious files, browser-based handlers, or unsafe helper applications processing untrusted output.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
The file’s user-facing text and link descriptions are entirely in Chinese, which can amount to forcing a specific language for users without opt-in. The policy allows locale constraints when they are explicitly justified, but this README does not state that the skill is intended only for Chinese-speaking or region-specific users.

Natural-Language Policy Violations

Low
Confidence
97% confidence
Finding
The token setup error message contains the Chinese-only phrase "左下角 -> API 开放平台" embedded in otherwise English guidance. This imposes a locale-specific instruction without giving the user a language choice or documenting that the skill is intentionally region/language-specific.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
references/【呜哩Wuli】开放平台 API 文档.md:37