T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/edit.py:124
- Finding
- Unvalidated Provider-Controlled URL Retrieval<![CDATA[ ## Vulnerability Details **File Location**: `scripts/edit.py:124-129`, `scripts/edit.py:255-264`, `scripts/generate.py:88-93`, and `scripts/generate.py:207-213` **Vulnerability Type**: Server-Side Request Forgery and Unrestricted Resource Retrieval **Risk Level**: Medium ### Vulnerable Code From `scripts/edit.py:124-129`: ```python b64 = result.get("data", [{}])[0].get("b64_json") if not b64: url = result.get("data", [{}])[0].get("url") if url: with urllib.request.urlopen(url) as img_resp: return img_resp.read() ``` From `scripts/edit.py:255-264`: ```python img_url = images[0].get("url", "") # Handle data URI (sync_mode) or HTTP URL if img_url.startswith("data:"): b64_data = img_url.split(",", 1)[1] return base64.b64decode(b64_data) else: with urllib.request.urlopen(img_url) as img_resp: return img_resp.read() ``` From `scripts/generate.py:88-93`: ```python if not b64: # Try URL fallback url = body.get("data", [{}])[0].get("url") if url: with urllib.request.urlopen(url) as img_resp: return img_resp.read() ``` From `scripts/generate.py:207-213`: ```python img_url = images[0].get("url") if not img_url: raise RuntimeError("No image URL in fal response") with urllib.request.urlopen(img_url) as img_resp: return img_resp.read() ``` ### Technical Analysis The scripts extract image URLs from OpenAI or fal.ai responses and pass them directly to `urllib.request.urlopen`. They do not validate: - The URL scheme, such as restricting retrieval to HTTPS. - The destination hostname against an approved provider CDN allowlist. - Whether DNS resolution points to loopback, private, link-local, multicast, or reserved addresses. - Redirect destinations. - The response content type or maximum response size. - Download timeouts for these image retrieval requests. This create ...[truncated 1947 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse every returned URL with `urllib.parse.urlsplit` and permit only the `https` scheme. 2. Maintain an allowlist of documented provider image-delivery domains. Do not permit arbitrary hosts solely because the initial API host is trusted. 3. Resolve the destination hostname before connecting and reject all loopback, private, link-local, multicast, unspecified, and reserved IP addresses using Python's `ipaddress` module. 4. Apply the same validation to every redirect destination, or disable automatic redirects and process them manually. 5. Add explicit connection and read timeouts to all image downloads. 6. Stream responses in bounded chunks and reject downloads above a configured maximum size. 7. Verify that the response `Content-Type` is an expected image media type and validate the file signature before writing it. 8. For fal data URIs, require the expected `data:image/...;base64,` structure and enforce a decoded-size limit. 9. Prefer inline image data from authenticated API responses when the provider supports it, avoiding secondary URL retrieval where practical. ]]>
