T09 · Insecure Skill Coding Practices
Warning
- Location
- scripts/analyze_image.py:37
- Finding
- Arbitrary Readable File Disclosure Through Symlink Following and Configurable Vision Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/analyze_image.py:24, 37-72, 90-107` **Vulnerability Type**: Arbitrary local file disclosure / unsafe outbound data transmission **Risk Level**: Medium ### Technical Analysis The image-path validation checks only the user-supplied filename extension, existence, and size. It does not: - Restrict files to approved directories. - Resolve and validate the canonical path with `realpath()`. - Reject symbolic links. - Verify that the file content is actually an image. - Require confirmation before transmitting file contents to a cloud or remotely configured service. The destination is controlled through the `OLLAMA_API_URL` environment variable. Consequently, a symbolic link with an allowed image extension can point to any file readable by the process, and the target file's raw bytes will be Base64-encoded and sent to the configured endpoint. Relevant code: ```python # Default Ollama API endpoint (override with OLLAMA_API_URL env var) OLLAMA_API = os.environ.get("OLLAMA_API_URL", "http://localhost:11434/api/chat") ``` ```python def validate_image_path(image_path: str) -> str: """Validate and resolve image path. Returns resolved path or raises ValueError.""" # Resolve ~ and normalize resolved = os.path.abspath(os.path.expanduser(image_path)) # Check for path traversal if '..' in image_path: raise ValueError(f"Path traversal not allowed: {image_path}") # Check file extension _, ext = os.path.splitext(resolved) if ext.lower() not in ALLOWED_EXTS: raise ValueError( f"Unsupported image format: {ext}. " f"Allowed: {', '.join(sorted(ALLOWED_EXTS))}" ) # Check file exists if not os.path.exists(resolved): raise ValueError(f"Image file not found: {image_path}") # Check file size file_size = os.path.getsize(resolved) if file_size > MAX_FILE_SIZE: raise ValueError( f"Image too larg ...[truncated 3621 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. **Restrict access to approved image directories** - Define one or more explicit trusted roots. - Resolve both the requested file and trusted roots with `os.path.realpath()`. - Reject the request unless the canonical file path remains beneath an approved root using `os.path.commonpath()`. 2. **Reject symbolic links** - Check each relevant path component for symbolic links. - Open files with `os.open()` and `O_NOFOLLOW` where supported. - Use `os.fstat()` on the opened descriptor to validate the actual opened object and its size, reducing time-of-check/time-of-use risks. 3. **Validate actual image content** - Do not rely on the extension. - Decode the file with a maintained image library and reject data that cannot be parsed as a supported image. - Consider excluding SVG unless required, because it is an active document format rather than a conventional raster image. 4. **Constrain outbound destinations** - Default to a fixed loopback endpoint and reject non-loopback destinations unless remote access is explicitly enabled. - Parse the URL and enforce an allowlist of schemes, hosts, and ports. - Require HTTPS for approved remote endpoints. - Disable or strictly validate HTTP redirects so image data cannot be redirected to an unexpected host. 5. **Require explicit consent for cloud processing** - Clearly indicate whether the selected model is local or cloud-backed. - Require user confirmation before transmitting image data outside the local machine. - Document that image contents may contain sensitive information. 6. **Use a safer file-handling sequence** - Open the file securely first. - Validate the opened descriptor's type and size with `fstat()`. - Decode and validate the image. - Only then construct and send the request. 7. **Add security tests** - Test symlinks with allowed extensions pointing to non-image files. - Test absolute paths outside trusted ro ...[truncated 174 chars]
