T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/tensorslab_image.py:76
- Finding
- Unvalidated API-Provided URLs Permit Arbitrary Network Requests and Unbounded Downloads<![CDATA[ ## Vulnerability Details **File Location**: `scripts/tensorslab_image.py:76-99` and `scripts/tensorslab_image.py:267-275` **Vulnerability Type**: Unvalidated remote URL retrieval / client-side SSRF **Risk Level**: Medium ### Vulnerable Code ```python def download_image(url: str, output_path: Path) -> Path: """Download an image from URL to local path.""" try: response = _SESSION.get(url, timeout=30) response.raise_for_status() content_type = response.headers.get('content-type', '').split(';')[0].strip() ext = mimetypes.guess_extension(content_type) if ext == '.jpe': ext = '.jpg' if not ext: ext = Path(urlparse(url).path).suffix if not ext or len(ext) > 6: ext = '.png' final_path = output_path.with_suffix(ext) with open(final_path, 'wb') as f: f.write(response.content) return final_path except Exception as e: logger.warning(f"Warning: Failed to download image from {url}: {e}") return None ``` The URL is obtained from API-controlled task data and passed directly to the download function: ```python for i, url in enumerate(urls): filename = f"{task_id}_{i}" output_path = output_dir / filename logger.info(f"📥 Downloading image {i+1}/{len(urls)}") final_path = download_image(url, output_path) if final_path: downloaded_files.append(str(final_path)) ``` ### Technical Analysis The application trusts image URLs returned by the remote task-status API. It does not validate: - The URL scheme. - The destination hostname or resolved IP address. - Whether the destination belongs to an approved image-storage domain. - Redirect destinations. - Whether the resolved address is loopback, private, link-local, or otherwise internal. - The response body size. - Whether the response is actually a valid image. The `Content-Type` header is used only to determine the output extension. I ...[truncated 1637 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Permit only `https` URLs. 2. Maintain an explicit allowlist of documented image-storage hostnames. 3. Resolve the hostname before connecting and reject loopback, private, link-local, multicast, reserved, and unspecified addresses. 4. Disable automatic redirects or validate the scheme, hostname, and resolved address of every redirect target. 5. Download with `stream=True` and enforce a strict maximum response size. 6. Require an expected image MIME type and verify the downloaded bytes with an image parser or file-signature check. 7. Apply connection and read timeouts separately. 8. Delete partial files when validation or download fails. Example defensive structure: ```python response = _SESSION.get( validated_url, stream=True, allow_redirects=False, timeout=(5, 30), ) response.raise_for_status() if response.headers.get("Content-Type", "").split(";")[0] not in ALLOWED_IMAGE_TYPES: raise TensorsLabAPIError("Unexpected download content type") total = 0 with open(final_path, "xb") as output: for chunk in response.iter_content(chunk_size=64 * 1024): total += len(chunk) if total > MAX_IMAGE_BYTES: raise TensorsLabAPIError("Downloaded image exceeds size limit") output.write(chunk) ``` ]]>
