T09 · Insecure Skill Coding Practices
- Location
scripts/comfy_gen.py:151- Finding
Arbitrary File Write Through Server-Controlled Image Filename
- Content
View full analysis
Vulnerability Details
File Location:
scripts/comfy_gen.py, lines 151–164 and 326–334
Vulnerability Type: Path traversal leading to arbitrary file creation or overwrite
Risk Level: HighVulnerable Code
python def download_image(server: str, filename: str, subfolder: str, folder_type: str) -> Path: """Download a generated image from ComfyUI.""" OUTPUT_DIR.mkdir(parents=True, exist_ok=True) params = urllib.parse.urlencode({ "filename": filename, "subfolder": subfolder, "type": folder_type, }) url = f"{server}/view?{params}" image_path = OUTPUT_DIR / filename try: urllib.request.urlretrieve(url, str(image_path))The untrusted filename reaches this function from the ComfyUI server response:
python for node_id, node_output in outputs.items(): if "images" in node_output: for image_data in node_output["images"]: image_path = download_image( SERVER_ADDRESS, image_data["filename"], image_data.get("subfolder", ""), image_data.get("type", "output"), )Technical Analysis
The
filenamevalue is obtained from the remote ComfyUI history response and used directly as the local destination path:python image_path = OUTPUT_DIR / filenameNo validation rejects absolute paths, directory separators, or traversal components such as
... Joining a base directory with a value such as../../target-filedoes not guarantee that the resulting path remains underOUTPUT_DIR.The subsequent
urlretrievecall writes server-supplied response content to that unchecked path. Therefore, a compromised or malicious configured ComfyUI server can control both the destination filename and the bytes written there.This behavior exceeds the minimum privileges required for image generation. The Skil ...[truncated 1785 chars]
- Remediation
View remediation
Remediation Suggestions
-
Treat every filename returned by the ComfyUI server as untrusted.
-
Reject absolute paths,
..components, and both POSIX and Windows path separators. -
Resolve the destination path and enforce containment with
Path.is_relative_to():python safe_name = Path(filename).name if safe_name != filename or not safe_name: raise ValueError("Invalid server-provided filename") output_root = OUTPUT_DIR.resolve() image_path = (output_root / safe_name).resolve() if not image_path.is_relative_to(output_root): raise ValueError("Image path escapes the output directory") -
Prefer generating a local random filename rather than trusting the server-provided name:
python import uuid image_path = OUTPUT_DIR / f"{uuid.uuid4().hex}.png" -
Download to a temporary file inside
OUTPUT_DIR, validate the maximum size and expected image format, and then atomically rename it. -
Avoid following existing symbolic links when creating the destination file. Use exclusive or no-follow file creation mechanisms where supported.
-
Apply an explicit response-size limit to prevent disk exhaustion.
-
Add regression tests covering absolute paths,
../traversal, nested paths, Windows separators, symlink destinations, and malformed filenames.
-
