T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/generate.py:41
- Finding
- Unvalidated API-Supplied Image URL in Python Implementation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate.py`, lines 41-49 and 127-131 **Vulnerability Type**: Server-Side Request Forgery and Unbounded Response Download **Risk Level**: Medium ### Vulnerable Code ```python def _download(url: str, out_file: str, timeout_s: int = 120): req = urllib.request.Request(url, method="GET", headers={"User-Agent": _UA}) try: with urllib.request.urlopen(req, timeout=timeout_s) as resp: content = resp.read() except Exception as e: raise RuntimeError(f"Failed to download result: {e}") from None with open(out_file, "wb") as f: f.write(content) ``` The API-provided URL is passed directly to the download function: ```python url = results[0] ext = _ext_from_url(url) if not args.out: out_file = os.path.abspath(_default_out_file(ext)) _download(url, out_file=out_file) ``` ### Technical Analysis The image result URL is obtained from the Evolink task response and passed directly to `urllib.request.urlopen` without validating its scheme, hostname, port, resolved address, or redirect destination. Although the initial API endpoint is fixed to `https://api.evolink.ai/v1`, the API response controls the destination of the subsequent request. If the service, account, or response channel is compromised, a crafted result URL could cause the process to request an internal service, loopback address, link-local endpoint, or another destination accessible from the host. Python's URL handler may also follow redirects. Validating only the initial URL would therefore be insufficient unless every redirect target is independently checked. The response is read in full through `resp.read()` before being written. No `Content-Length`, content type, image signature, or maximum-byte limit is enforced. A large or endless response could cause excessive memory consumption, followed by disk consumption when saved. ### Attack Path 1. A user invokes the Skill to generate an image. ...[truncated 1146 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Accept only `https` result URLs. 2. Maintain an explicit allowlist of documented Evolink image CDN hostnames. 3. Reject URLs containing embedded credentials, unexpected ports, malformed hostnames, or unsupported schemes. 4. Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified addresses for both IPv4 and IPv6. 5. Disable automatic redirects or validate the scheme, hostname, port, and resolved address of every redirect target. 6. Stream the response in bounded chunks instead of calling `resp.read()` without a limit. 7. Enforce a strict maximum image size and reject responses whose declared or observed size exceeds it. 8. Require an approved image content type and validate the downloaded file's image signature before retaining it. 9. Delete partial output files when validation or downloading fails. ]]>
