T09 · Insecure Skill Coding Practices
- Location
scripts/glm_image_cli.py:131- Finding
Unvalidated Server-Provided Image URL Enables Client-Side SSRF and Unbounded Downloads
- Content
View full analysis
Vulnerability Details
File Location:
scripts/glm_image_cli.py, lines 131–139 and 310–316
Vulnerability Type: Unrestricted URL retrieval, client-side SSRF, and unbounded response buffering
Risk Level: MediumVulnerable Code
python def download_image(url: str, save_path: str) -> bool: """Download image from URL to local file.""" try: with urllib.request.urlopen(url, timeout=60) as response: with open(save_path, "wb") as f: f.write(response.read()) return True except Exception: return FalseThe function is invoked with a URL obtained directly from the remote API response:
python # Download image if --save specified saved_file = None if result["ok"] and args.save and result.get("image_url"): if download_image(result["image_url"], args.save): saved_file = os.path.abspath(args.save) else: result["ok"] = False result["error"] = { "code": "DOWNLOAD_FAILED", "message": f"Failed to download image to {args.save}", }Technical Analysis
The generated image URL is extracted from the remote API response and passed directly to
urllib.request.urlopen(). The implementation does not validate:- The URL scheme or destination hostname.
- Whether the resolved address is loopback, link-local, private, or reserved.
- Whether redirects lead to a different or prohibited destination.
- The response content type or whether the content is actually an image.
- The response size before calling
response.read(). - Whether an existing destination file may be overwritten.
As a result, a compromised API response, compromised upstream service, or malicious redirect could cause the client to initiate requests to unintended network locations. Because
urllibfollows redirects, validating only the initial API endpoint would not protect the subsequent ...[truncated 2460 chars]- Remediation
View remediation
Remediation Suggestions
- Permit only
httpsimage URLs and reject URLs containing embedded credentials or unsupported schemes. - Maintain an explicit allowlist of trusted image-storage hostnames used by the ZhiPu service.
- Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IP ranges for both IPv4 and IPv6.
- Disable automatic redirects or validate the scheme, hostname, resolved address, and port after every redirect.
- Verify that the response has an expected image content type, such as
image/png,image/jpeg, or another explicitly supported format. - Stream the response in bounded chunks instead of using an unrestricted
response.read(). - Enforce a conservative maximum download size using both
Content-Lengthand an actual byte counter while streaming. - Download into a temporary file in the destination directory, validate the image format, and atomically rename it after successful validation.
- Refuse to overwrite existing files by default or require an explicit overwrite option.
- Preserve and report specific download errors rather than suppressing every exception, while ensuring sensitive values are not included in error output.
- Permit only
