- Location
- scripts/minimax_image.py:76
- Finding
- Unvalidated response-directed downloads permit SSRF and unbounded file retrieval<![CDATA[
## Vulnerability Details
**File Location**: `scripts/minimax_image.py:76-81, 123-127`; equivalent response-directed downloads in `scripts/minimax_video.py:122-129` and `scripts/minimax_music.py:101-103, 145-149`
**Vulnerability Type**: Untrusted URL retrieval and unbounded download
**Risk Level**: Medium
### Vulnerable Code
```python
def download_and_save(url: str, output_path: str):
"""Download image from URL and save."""
resp = requests.get(url, timeout=60)
resp.raise_for_status()
with open(output_path, "wb") as f:
f.write(resp.content)
return len(resp.content)
```
```python
else:
urls = result.get("data", {}).get("image_urls", [])
for i, url in enumerate(urls):
path = args.output if len(urls) == 1 else _numbered_path(args.output, i)
size = download_and_save(url, path)
print(f"OK: {size} bytes -> {path}")
```
The video client contains the same pattern:
```python
download_url = data.get("file", {}).get("download_url", "")
if not download_url:
raise SystemExit(f"No download_url in response: {json.dumps(data, indent=2)}")
print(f" Downloading from {download_url[:80]}...")
video_resp = requests.get(download_url, timeout=300)
video_resp.raise_for_status()
os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
with open(output_path, "wb") as f:
f.write(video_resp.content)
```
### Technical Analysis
The scripts trust URLs returned in API responses and issue unrestricted `GET` requests to them. They do not validate the URL scheme, hostname, resolved IP address, redirect chain, response content type, or response size.
A malicious or compromised API endpoint can therefore make the client connect to loopback, private, link-local, or cloud metadata addresses. Because `requests` follows redirects by default, an apparently acceptable public URL could also redirect to an internal destination. This creates a server-side request forgery primitive from the machine running the Skill
...[truncated 1786 chars]
- Remediation
- <![CDATA[
## Remediation Suggestions
1. Parse every returned URL and require HTTPS.
2. Maintain an allowlist of documented MiniMax media-storage hostnames. If hosts are dynamic, validate them against a narrowly defined provider-owned suffix with correct DNS-boundary checks.
3. Resolve the hostname before connection and reject loopback, private, link-local, multicast, reserved, and cloud-metadata address ranges for both IPv4 and IPv6.
4. Disable redirects initially:
```python
requests.get(url, allow_redirects=False, stream=True, timeout=...)
```
Validate each redirect destination before following it.
5. Stream responses in bounded chunks and enforce a type-specific maximum file size using both `Content-Length` and a running byte count.
6. Require an expected media `Content-Type` and verify file signatures before accepting the file.
7. Write to a temporary file, validate it, and atomically rename it to the final output path.
8. Prefer base64 or authenticated first-party response modes where supported, reducing the need to follow arbitrary response-provided URLs.
]]>