Back to skill

Security audit

Ernie Image Radeon

Security checks for vulnerabilities and agentic risk

Overview

This image-generation skill is coherent and disclosed, but it defaults to sending prompts over unauthenticated HTTP and includes unsafe optional download and installer guidance that users should review carefully.

Install only if you are comfortable sending non-sensitive prompts to the default AMD Radeon Cloud HTTP endpoint. Prefer configuring a trusted HTTPS `ERNIE_BASE_URL`, avoid `--format url` unless you trust the provider response path, and do not run the documented `curl | sh` installer without separately verifying the installer source.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T03 · Remote Payload Retrieval and Execution

Error
Location
references/api-guide.md:182
Finding
Unverified Remote Installer Executed Directly by a Shell<![CDATA[ ## Vulnerability Details **File Location**: `references/api-guide.md:182` **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: Critical ### Vulnerable Code ```text | `uv run` fails | uv not installed or Python < 3.11 | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` | ``` ### Technical Analysis The troubleshooting documentation instructs users to download a remote installation script and pipe it directly into `sh`. The fetched payload is executed immediately without: - Pinning it to an immutable version. - Verifying a checksum or cryptographic signature. - Saving and inspecting the script before execution. - Constraining the permissions available to the installer. Although the referenced domain appears associated with the legitimate `uv` project, the effective payload remains mutable after the Skill has been reviewed. Security therefore depends on the continued integrity of the domain, DNS resolution, TLS infrastructure, hosting environment, and upstream release process. Installing `uv` is a prerequisite rather than part of image generation itself. Blind execution of a remote script is not the minimum privilege or minimum trust necessary to satisfy that prerequisite. ### Attack Path 1. A user encounters the documented `uv run` troubleshooting entry. 2. The user copies and executes the recommended command. 3. The shell retrieves the current contents of the remote URL. 4. A compromised hosting account, upstream release process, DNS path, or TLS trust chain supplies modified shell code. 5. The pipe passes that code directly to `sh` without inspection or integrity verification. 6. The malicious code executes with all permissions held by the invoking user. ### Impact Assessment A malicious installer could execute arbitrary commands with the invoking user's privileges. Depending on that user's access, it could: - Read, alter, or delete user-accessible files. - Access environment variables, credentials, A ...[truncated 435 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Remove the `curl | sh` installation instruction. Prefer one of the following approaches: 1. Recommend installation through a trusted operating-system package manager where available. 2. Direct users to the official installation documentation rather than embedding an immediately executable pipeline. 3. If a standalone installer is necessary: - Pin an explicit installer or release version. - Download it as a separate file. - Obtain the expected checksum or signature through an authenticated channel. - Verify the checksum or signature before execution. - Allow the user to inspect the downloaded file. - Run it without elevated privileges unless elevation is demonstrably required. 4. Document the files and directories the installer is expected to modify. 5. For reproducible environments, provide a version-pinned dependency setup with integrity hashes. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate.py:31
Finding
Image Prompts and API Responses Use an Unauthenticated Plaintext HTTP Channel<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py:31, 205-245` **Vulnerability Type**: Plaintext transmission of user-provided content **Risk Level**: High ### Vulnerable Code ```python RADEON_BASE_URL = "http://134.199.132.159/ocr/v1" ``` ```python def generate_image(args: argparse.Namespace) -> object: base_url = os.environ.get("ERNIE_BASE_URL", "").strip() or RADEON_BASE_URL api_key = os.environ.get("AI_STUDIO_API_KEY", "").strip() # Suppress API key for plaintext HTTP endpoints to avoid credential leakage. # Key is only forwarded when the endpoint uses HTTPS (a trusted endpoint). if api_key and base_url.startswith("http://"): print( "Warning: AI_STUDIO_API_KEY is set but the endpoint uses HTTP. " "Key will NOT be sent over plaintext. Use an HTTPS endpoint " "(ERNIE_BASE_URL) to enable authentication.", file=sys.stderr, ) api_key = "" if not api_key: api_key = "radeon-cloud" timeout = OPENAI_TIMEOUT_SECONDS timeout_str = os.environ.get("ERNIE_TIMEOUT", "").strip() if timeout_str: try: timeout = float(timeout_str) except ValueError: pass client = OpenAI( api_key=api_key, base_url=base_url, timeout=timeout, ) extra_body = build_extra_body(args) try: return client.images.generate( model=args.model, prompt=args.prompt, n=args.n, size=args.size, response_format=args.response_format, extra_body=extra_body if extra_body else None, ) ``` ### Technical Analysis The default API endpoint uses HTTP rather than HTTPS and is identified by a raw IP address. Consequently, the connection provides neither transport confidentiality nor authenticated server identity. The code correctly suppresses `AI_STUDIO_API_KEY` when the endpoint begins with `http://`, which ...[truncated 2057 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the default endpoint with a verified HTTPS endpoint using a valid hostname and certificate. 2. Reject plaintext HTTP endpoints by default, including custom `ERNIE_BASE_URL` values. 3. If legacy HTTP support is unavoidable, require an explicit insecure-transport flag and display a prominent warning before sending data. 4. Validate custom endpoint URLs with `urllib.parse` rather than relying only on string-prefix checks. 5. Permit API credentials only for validated HTTPS endpoints, preserving the existing credential-suppression safeguard. 6. Clearly identify the service operator and publish a privacy and data-retention policy. 7. Treat all API responses as untrusted and validate image data before saving or exposing it to downstream clients. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate.py:265
Finding
Unrestricted and Unbounded Download of API-Provided URLs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py:265-275` **Vulnerability Type**: Server-side request forgery and resource exhaustion through untrusted URLs **Risk Level**: High ### Vulnerable Code ```python def write_image_from_url(url: str, filepath: Path) -> None: """Download an image from *url* to *filepath* with timeout checks.""" parsed = urlparse(url) if parsed.scheme not in ("http", "https"): raise ValueError(f"Image URL must use http or https, got {parsed.scheme}") request = urllib.request.Request( url, headers={"User-Agent": "ernie-image-radeon/1.0"}, ) try: with urllib.request.urlopen(request, timeout=DOWNLOAD_TIMEOUT_SECONDS) as response: filepath.write_bytes(response.read()) except URLError as exc: raise RuntimeError(f"Could not download image URL: {exc}") from exc ``` ### Technical Analysis When `--format url` is selected, the URL returned by the remote generation service is passed to `urllib.request.urlopen`. Validation only confirms that the initial scheme is HTTP or HTTPS. The function does not: - Restrict downloads to trusted hosts. - Require HTTPS. - Reject loopback, private, link-local, multicast, or reserved IP ranges. - Revalidate the destination after DNS resolution. - Validate each redirect target. - Limit the maximum response size. - Stream the response with a bounded byte count. - Validate the HTTP status, content type, or decoded image format. The timeout limits elapsed waiting behavior but does not impose a content-size limit. `response.read()` loads the complete response into memory before `write_bytes` writes it to disk. Because the API controls the URL, compromise of the service can exploit this behavior. The plaintext default API channel also allows a network-positioned attacker to potentially substitute an arbitrary URL in the response. ### Attack Path #### Internal Network Request Path 1. The user invokes the sc ...[truncated 1862 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Retain `b64_json` as the default and consider removing URL mode unless it is required. 2. Require HTTPS for downloaded images. 3. Allowlist the exact image-delivery hosts expected from the trusted provider. 4. Resolve hostnames before connecting and reject all loopback, private, link-local, multicast, unspecified, and reserved addresses for both IPv4 and IPv6. 5. Repeat destination validation after every redirect, or disable redirects and handle approved redirects manually. 6. Protect against DNS rebinding by connecting only to the validated resolved address while preserving correct TLS hostname verification. 7. Stream downloads in fixed-size chunks instead of using `response.read()` without a limit. 8. Enforce a conservative maximum image size and abort if `Content-Length` or streamed bytes exceed it. 9. Require a successful HTTP status and an expected image content type. 10. Decode and verify the resulting image with a trusted image parser before retaining it. 11. Write first to a safely created temporary file and atomically rename it only after all validation succeeds. 12. Delete partial files after errors or size-limit violations. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill explicitly instructs the agent to access environment variables, write local files, and make network requests, but it does not declare any tool scope or allowed-tools boundary. This creates a permission mismatch: an agent platform may expose more capabilities than users expect, increasing the risk of silent data exfiltration or unintended file writes, especially since the default endpoint uses unencrypted HTTP.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The trigger list includes very broad phrases like "draw" and "generate image," and the skill says to "generate immediately with defaults." In a conversational agent, this can cause accidental invocation from ordinary user speech, leading to unintended network transmission of user text to a third-party service and local file creation without clear confirmation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The script defaults to a plaintext HTTP endpoint (`RADEON_BASE_URL = "http://134.199.132.159/ocr/v1"`) and sends user prompts to it during image generation. This exposes prompt contents to interception or modification in transit by network attackers, which is especially relevant because prompts may contain sensitive user data and the endpoint could be tampered with via man-in-the-middle attacks.

Natural-Language Policy Violations

Low
Confidence
76% confidence
Finding
The description says "Chinese prompts excel" and that the skill "specializes in Chinese" styles, which introduces a language preference in the natural-language guidance. While not a hard prohibition, this can steer behavior toward a specific language without stating that language choice remains up to the user.

Natural-Language Policy Violations

Low
Confidence
81% confidence
Finding
The section titled 'Chinese Prompt Tips' states that Chinese prompts often produce better results and then recommends Chinese-specific subjects and themes. This creates a language preference in the guidance without offering an explicit user choice or documenting a necessary region-specific constraint.

External Script Fetching

Low
Category
Supply Chain
Content
| Empty response | Temporary API issue | Retry after a few seconds |
| Invalid size | Size not in allowed list | Use one of the 7 supported sizes |
| Invalid prefix | Unsafe filename prefix | Use letters, numbers, `_`, `-`, or `.` |
| `uv run` fails | uv not installed or Python < 3.11 | Install uv: `curl -LsSf https://astral.sh/uv/install.sh \| sh` |

---
Confidence
97% confidence
Finding
The guide instructs users to pipe a remote script directly into a shell via curl | sh, which is a classic supply-chain and remote-code-execution risk. If the hosting site, transport, DNS, or published installer is compromised, users could execute attacker-controlled code immediately on their system.

Static analysis

No suspicious patterns detected.