T09 · Insecure Skill Coding Practices
- Location
scripts/image_assets.py:50- Finding
Unrestricted Remote Image Fetching Enables Server-Side Request Forgery and Resource Exhaustion
- Content
View full analysis
None: destination.parent.mkdir(parents=True, exist_ok=True) with urllib.request.urlopen(url) as response, destination.open("wb") as handle: shutil.copyfileobj(response, handle) ``` The function is reached using imported image metadata: ```python if remote_target: filename = Path(urllib.parse.urlparse(remote_target).path).name or "image" if not Path(filename).suffix: filename = f"{Path(filename).stem or 'image'}.png" destination = _unique_path(asset_dir / filename) try: destination.parent.mkdir(parents=True, exist_ok=True) (download_image or download_remote_image)(remote_target, destination) ``` ### Technical Analysis Remote image URLs supplied through an imported draft are passed directly to `urllib.request.urlopen()` without validating the destination host. The URL classification logic accepts both HTTP and HTTPS URLs, while the downloader does not: - Reject loopback, private, link-local, reserved, or multicast addresses. - Revalidate destinations after DNS resolution. - Validate redirect targets. - Require HTTPS. - Set a network timeout. - Restrict response size. - Validate the response content type. Because `urlopen()` follows HTTP redirects, an initially public URL may redirect to a private or link-local resource. The lack of a response-size limit also permits an attacker-controlled server to stream excessive data into the vault. ### Attack Path 1. An attacker supplies an article or structured draft containing an image such as `images[].src`. 2. The image URL points directly to an internal service, localhost, a cloud metadata address, or a public redirector that redirects to such an address. 3. `render_obsidian_note()` calls ...[truncated 916 chars]- Remediation
View remediation
