T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/generate_image.py:184
- Finding
- Unrestricted Local File Upload to External Service<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_image.py`, lines 184–218 **Vulnerability Type**: Missing file-type, size, and path validation before external upload **Risk Level**: Medium ### Vulnerable Code ```python def upload_file(file_path): if not os.path.exists(file_path): print(f"Error: File not found: {file_path}", file=sys.stderr) sys.exit(1) url = f"{API_BASE}/model/uploadMedia" filename = os.path.basename(file_path) boundary = f"----AtlasCloudBoundary{int(time.time() * 1000)}" with open(file_path, "rb") as f: file_data = f.read() body = b"" body += f"--{boundary}\r\n".encode() body += f'Content-Disposition: form-data; name="file"; filename="{filename}"\r\n'.encode() body += b"Content-Type: application/octet-stream\r\n\r\n" body += file_data body += f"\r\n--{boundary}--\r\n".encode() headers = { "Authorization": f"Bearer {get_api_key()}", "Content-Type": f"multipart/form-data; boundary={boundary}", "User-Agent": "AtlasCloud-Skill/1.0", } req = urllib.request.Request(url, data=body, headers=headers, method="POST") try: with urllib.request.urlopen(req, timeout=120) as resp: result = json.loads(resp.read().decode("utf-8")) ``` ### Technical Analysis The `upload` command is intended to upload a local image for image editing. However, `upload_file()` accepts any existing filesystem path and does not verify that the path refers to a regular image file. The implementation lacks: - Image file-signature validation. - An allowlist of supported image formats. - A maximum upload size. - Rejection of symbolic links or non-regular files. - Restriction to an approved working directory. - Explicit confirmation identifying the file and external destination. - Streaming upload behavior. The supplied file is read completely into memory and transmitted to `https://api.atlascloud.ai/api/v1/model/uploadMedia`. La ...[truncated 1626 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Resolve the supplied path to a canonical path and require it to be inside an explicitly approved input directory. 2. Require a regular file and reject directories, devices, FIFOs, and symbolic links: ```python resolved = os.path.realpath(file_path) if not os.path.isfile(resolved) or os.path.islink(file_path): raise ValueError("Only regular, non-symbolic-link image files are allowed") ``` 3. Enforce a conservative maximum file size before reading or uploading the file. 4. Validate supported image formats using file signatures rather than trusting extensions. 5. Decode the image with a maintained image parser where available to ensure that it is structurally valid. 6. Use the detected image MIME type instead of unconditional `application/octet-stream`. 7. Display the canonical path, file size, and destination and require explicit user approval before transmission. 8. Stream the multipart body in bounded chunks instead of loading the entire file into memory. 9. Document the provider's retention and access behavior for uploaded image bytes and filenames. ]]>
