Back to skill

Security audit

Vision Helper — AI Image Analysis

Security checks for vulnerabilities and agentic risk

Overview

This image-analysis skill does what it claims, but it gives an agent broad local image/screenshot access and can transmit those contents to configurable or cloud vision endpoints without strong safeguards.

Review before installing. Use this skill only for images and screenshots you are comfortable sending to the configured Ollama endpoint, keep OLLAMA_API_URL on localhost unless you intentionally trust a remote service, and avoid using it on sensitive desktop screenshots, credentials, private documents, or paths outside the workspace.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

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]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Tainted flow: 'req' from os.environ.get (line 102, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
)

    try:
        resp = urllib.request.urlopen(req, timeout=timeout)
        result = json.loads(resp.read().decode())
        content = result.get("message", {}).get("content", "")
        if not content:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger phrases are broad terms like 'screenshot', 'OCR', and 'what's in this image', which are common in normal conversation and can cause the skill to activate unexpectedly. Because this skill can invoke exec-driven workflows and access arbitrary readable image paths, accidental activation increases the chance of unnecessary screenshot capture or sensitive image processing.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill promotes cloud vision models and longer timeouts but does not clearly warn that image contents may be sent to external services when those models are used. This creates a material privacy and data-handling risk because screenshots often contain credentials, personal data, internal documents, or other sensitive visual content.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The documentation explicitly advertises bypassing built-in file path restrictions and analyzing images from any readable directory. In an agent setting, that expands the skill from narrow image analysis into a data-access primitive that can be used to read and exfiltrate sensitive screenshots or image-based documents from the local system, especially when paired with cloud models.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
This script base64-encodes the entire supplied image and sends it to the configured vision model API, but the CLI does not clearly warn users that sensitive screenshots, documents, or UI contents may leave the local machine. That is especially important here because the examples explicitly mention cloud-based models and the endpoint is configurable, so confidential data could be disclosed to a remote service without meaningful user awareness.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The help text explicitly describes the default prompt as a 'general description in English', which sets a specific language expectation. The file does not indicate any language choice, opt-in, or justification for forcing English as the default locale behavior.

Static analysis

No suspicious patterns detected.