Back to skill

Security audit

magic-image2video

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real remote image-to-video client, but it can fetch arbitrary URLs and upload any readable local file when given as an image path.

Install only if you are comfortable sending selected images to the MagicLight remote service. Do not provide sensitive local paths, private documents, credential files, internal URLs, localhost URLs, or cloud metadata URLs as the image input; prefer a known image file in a dedicated folder or a trusted public HTTPS image URL. Use MAGIC_API_KEY from a protected environment variable rather than the --api-key command-line option.

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
scripts/media_gen_client.py:119
Finding
Unrestricted User-Controlled URL Fetch Enables Blind SSRF<![CDATA[ ## Vulnerability Details **File Location**: `scripts/media_gen_client.py`, lines 119–121 **Vulnerability Type**: Server-Side Request Forgery (SSRF) **Risk Level**: High ### Vulnerable Code ```python if args.image.startswith("http"): image_content = urllib.request.urlopen(args.image, context=_get_ssl_context()).read() image_url = args.image if image_content else None ``` ### Technical Analysis The `--image` argument is supplied by the user and is fetched directly with `urllib.request.urlopen`. The only validation is a case-sensitive string-prefix check for `http`; there is no URL parsing, destination allowlist, resolved-IP validation, redirect validation, response-size limit, or explicit timeout. Fetching a user-provided image URL is related to the Skill's declared functionality, but downloading the resource locally is not necessary merely to pass its URL to the video service. This preliminary request therefore grants more network access than the minimum required. An attacker can cause the process to issue GET requests to destinations reachable from the execution environment, including loopback interfaces, private networks, link-local services, and cloud metadata endpoints. Redirects can also undermine checks performed only on the original URL. Although the response body is not printed, request timing, errors, and success behavior provide a blind SSRF oracle. GET endpoints with side effects may also be triggered. Calling `.read()` without a size limit additionally permits memory exhaustion from a very large or endless response. ### Attack Path 1. An attacker supplies an image argument such as `http://127.0.0.1:8080/admin/action`, a private-network host, or a link-local metadata address. 2. The value passes `startswith("http")`. 3. `urllib.request.urlopen` sends a GET request from the Skill execution environment. 4. The attacker observes differences in completion time or error behavior to infer service availability. 5. If an internal GET ...[truncated 783 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the local prefetch entirely when the remote service can accept the original image URL. - If validation is required, parse the URL with `urllib.parse.urlsplit` and allow only `https`. - Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified IP ranges for both IPv4 and IPv6. - Disable redirects or validate every redirect destination after DNS resolution. - Apply an explicit short connection/read timeout. - Read only a small bounded amount of data needed for format validation rather than calling unbounded `.read()`. - Enforce an image content-type allowlist and validate image magic bytes. - Prefer a trusted image-host allowlist where operationally possible. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/media_gen_client.py:122
Finding
Arbitrary Local File Read and Upload Through the Image Path Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/media_gen_client.py`, lines 122–135 **Vulnerability Type**: Arbitrary Local File Disclosure **Risk Level**: High ### Vulnerable Code ```python else: image_content = open(args.image, "rb").read() image_put_url_resp = image_put_url(api_key=api_key) # _print_json(image_put_url_resp) image_key = image_put_url_resp.get("data", {}).get("key") put_url = image_put_url_resp.get("data", {}).get("put_url") # put_url是临时上传地址,需要上传图片到put_url req = urllib.request.Request(put_url, data=image_content, method="PUT") with urllib.request.urlopen(req, context=_get_ssl_context()) as resp: status = resp.getcode() if status != 200: raise Exception(f"Failed to upload image to put_url, status: {status}") image_get_url_resp = image_get_url(api_key=api_key, key=image_key) ``` ### Technical Analysis Any `--image` value that does not begin with `http` is treated as a local filesystem path. The process opens that path with its own privileges, reads the entire file, obtains a remote upload URL, and transmits the bytes to that URL. Supporting user-selected local images is part of the declared functionality. However, the implementation does not establish that the selected path is an intended image, is located in a user-approved directory, or has a supported image format. It therefore exceeds the minimum file access necessary for image-to-video generation by permitting any readable regular file to be uploaded. There are also no file-size limits. A special file or very large file may block execution or exhaust memory because the entire content is loaded with `.read()` before upload. ### Attack Path 1. An attacker persuades the agent to invoke the Skill with a sensitive local path as `--image`, for example a configuration file, credential file, SSH key, or process-readable system file. 2. Because the path does not start with `http`, execution enters the local-file br ...[truncated 941 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require local images to reside under an explicitly approved upload directory. - Canonicalize the path with `realpath` and verify that it remains inside the approved directory. - Reject symbolic links, device files, FIFOs, sockets, and all other non-regular files. - Validate the file extension, MIME type, and image magic bytes before uploading. - Enforce a conservative maximum file size before reading or transmitting the file. - Stream bounded uploads rather than loading the entire file into memory. - Require explicit user confirmation showing the canonical local path before network transmission. - Document clearly that local file contents are transferred to the named external service. - Run the client under a restricted account or sandbox with access only to approved media files. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/media_gen_client.py:178
Finding
API Key Accepted Through a Command-Line Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/media_gen_client.py`, lines 178–180 **Vulnerability Type**: Sensitive Credential Exposure Through Process Arguments **Risk Level**: Medium ### Vulnerable Code ```python p = argparse.ArgumentParser(description="OpenClaw Media Gen - image & video generation") p.add_argument("--api-key", help="Override MAGIC_API_KEY") ``` The argument is consumed as follows at lines 29–33: ```python def _get_api_key(explicit: Optional[str] = None) -> str: api_key = explicit or os.environ.get("MAGIC_API_KEY") if not api_key: raise ValueError("MAGIC_API_KEY is required (env or --api-key).") return api_key ``` ### Technical Analysis The client permits the bearer credential to be provided as `--api-key`. Command-line arguments commonly remain visible in shell history, process listings, monitoring systems, audit logs, crash reports, and orchestration metadata. The Skill metadata already declares `MAGIC_API_KEY` as its required and primary environment variable. Consequently, accepting the secret through a command-line option is unnecessary for the declared workflow and expands the channels through which the credential may leak. The credential is transmitted to the fixed HTTPS API endpoint in an `Authorization: Bearer` header, which is necessary for the service operation. The issue is not that authenticated HTTPS transmission occurs; it is the avoidable command-line secret input channel. ### Attack Path 1. A user or automated invocation supplies `--api-key SECRET`. 2. The complete command is recorded in shell history or exposed in the process command line while the client is running. 3. Another local user, monitoring agent, diagnostic tool, or log consumer obtains the argument. 4. The exposed bearer key is reused against the MagicLight API until it is revoked or expires. ### Impact Assessment An attacker who obtains the key may act with the API privileges associated with the victim's MagicLight ...[truncated 301 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the `--api-key` command-line option and accept the key only through the declared `MAGIC_API_KEY` environment integration or a protected secret manager. - If interactive use must be supported, read the key through a non-echoing prompt or inherited file descriptor. - Ensure the key is never included in logs, exception messages, URLs, or serialized output. - Configure CI/CD and agent runtimes to inject the credential through their native secret facilities. - Rotate any key that may previously have been supplied on a command line. - Apply API-side least privilege, quotas, expiration, and revocation controls. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Tainted flow: 'req' from os.environ.get (line 124, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, data=data, headers=all_headers, method=method.upper())
    try:
        with urllib.request.urlopen(req, timeout=timeout_s, context=_get_ssl_context()) as resp:
            raw = resp.read().decode("utf-8")
            return json.loads(raw) if raw else {}
    except urllib.error.HTTPError as e:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 124, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
put_url = image_put_url_resp.get("data", {}).get("put_url")
            # put_url是临时上传地址,需要上传图片到put_url
            req = urllib.request.Request(put_url, data=image_content, method="PUT")
            with urllib.request.urlopen(req, context=_get_ssl_context()) as resp:
                status = resp.getcode()
                if status != 200:
                    raise Exception(f"Failed to upload image to put_url, status: {status}")
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill invokes a Python client that requires environment access to read `MAGIC_API_KEY` and performs network requests to a remote video service, but the manifest does not declare any explicit tool scope such as `permissions` or `allowed-tools`. This weakens least-privilege enforcement and reduces review visibility into what the skill is allowed to access, making unintended credential exposure or unauthorized external communication harder to control.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The script directly fetches arbitrary user-supplied URLs before submitting the task, which gives the skill a general-purpose outbound URL retrieval capability. In an agent setting, this can be abused for SSRF-style access to internal services, cloud metadata endpoints, or other network locations reachable from the runtime environment.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
When given a local file path, the script reads the file and uploads its contents to a remote temporary storage endpoint. That creates remote file-transfer behavior that can expose sensitive local files if an attacker can influence the path or trick a user/agent into supplying one, especially since the manifest frames the skill as media generation rather than generic file upload.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The code silently transmits local image data to a remote endpoint without a clear user-facing warning at the moment of upload. In an agent context, that lack of disclosure increases the chance of accidental data exfiltration because users may believe they are only referencing a local file for processing rather than sending it off-host.

Static analysis

No suspicious patterns detected.