Back to skill

Security audit

ollama-vision

Security checks for vulnerabilities and agentic risk

Overview

This skill performs local image analysis with Ollama as advertised, with some implementation and disclosure caveats but no evidence of malicious behavior.

Install only if you are comfortable with Chinese-only prompts, a possible first-run qwen3-vl:4b model download of about 2-3GB, and sending selected images to your local Ollama service. Avoid using it on sensitive images in multi-user environments until the temporary-file handling is changed to use unique private temp files.

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
analyze_image.py:111
Finding
Predictable Shared Temporary File Allows File Overwrite and Race Conditions<![CDATA[ ## Vulnerability Details **File Location**: `analyze_image.py`, lines 111–140 and 244–247 **Vulnerability Type**: Predictable temporary file creation and unsafe shared-file handling **Risk Level**: Medium ### Vulnerable Code ```python # Create temporary file temp_dir = tempfile.gettempdir() temp_path = os.path.join(temp_dir, f"compressed_{os.path.basename(image_path)}") # Try different compression strategies # Strategy 1: Reduce quality quality = 95 while quality >= 30: img.save(temp_path, 'JPEG', quality=quality, optimize=True) if os.path.getsize(temp_path) <= max_size_bytes: compressed_size = os.path.getsize(temp_path) print(f"Compression complete: {original_size / 1024 / 1024:.2f}MB → {compressed_size / 1024 / 1024:.2f}MB (quality: {quality}%)") return temp_path, True quality -= 10 # Strategy 2: Reduce dimensions if lowering quality is insufficient scale = 0.9 while scale > 0.3: new_width = int(width * scale) new_height = int(height * scale) resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS) resized_img.save(temp_path, 'JPEG', quality=85, optimize=True) if os.path.getsize(temp_path) <= max_size_bytes: compressed_size = os.path.getsize(temp_path) print(f"Compression complete: {original_size / 1024 / 1024:.2f}MB → {compressed_size / 1024 / 1024:.2f}MB (dimensions: {scale*100:.0f}%)") return temp_path, True scale -= 0.1 ``` The corresponding cleanup code is: ```python finally: # Clean up temporary file if temp_file and os.path.exists(temp_file): try: os.remove(temp_file) except: pass ``` ### Technical Analysis The compressed image path is constructed deterministically from the basename of the source image: ```python compressed_<source basename> ``` The file is placed in the operating system's shared temporary directory. The application does not use an atomic, exclusive file-creation op ...[truncated 3268 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace the predictable path with a securely and uniquely created temporary file: ```python fd, temp_path = tempfile.mkstemp(prefix="ollama_image_", suffix=".jpg") os.close(fd) ``` Alternatively, use `tempfile.NamedTemporaryFile(delete=False, suffix=".jpg")` and retain the generated path. 2. Ensure the temporary file is created atomically by the application rather than generating a name and opening it later. 3. Use a private temporary directory with restrictive permissions when processing potentially sensitive images: ```python temp_dir = tempfile.mkdtemp(prefix="ollama_vision_") temp_path = os.path.join(temp_dir, "compressed.jpg") ``` 4. Track the exact file and directory created by the current invocation. Remove only those resources during cleanup, preferably with `shutil.rmtree(temp_dir)` after validating that `temp_dir` is the private directory returned by `mkdtemp()`. 5. Minimize time-of-check/time-of-use windows. Avoid repeatedly resolving a shared pathname across save, size-check, encoding, and deletion operations where practical. 6. Add concurrency tests that analyze multiple images with identical basenames and verify that each invocation receives a unique temporary path. 7. Add platform-specific tests for symbolic links, junctions, and other path-redirection mechanisms supported by the deployment environment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (11)

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file's natural-language interface and outputs are written in Chinese, including the module description, prompts, errors, and CLI usage strings. There is no indication that the skill is region-specific or that users can opt into another language, which makes this a language-policy issue under the rule.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
return False
    
    try:
        result = subprocess.run(
            [ollama_path, "--version"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
return False
    
    try:
        result = subprocess.run(
            [ollama_path, "list"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
try:
        print(f"正在下载模型 {model_name},请耐心等待...")
        result = subprocess.run(
            [ollama_path, "pull", model_name],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

External Transmission

Medium
Category
Data Exfiltration
Content
}
        
        # 发送请求
        response = requests.post(api_url, json=payload, timeout=120)
        
        if response.status_code == 200:
            result = response.json()
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The file’s docstring, function docstring, status messages, and startup output are all hard-coded in Chinese, which imposes a specific language on users. Under the policy, locale or language constraints should be optional or clearly justified; this file provides neither opt-in nor documentation that the skill is intended only for Chinese-speaking users.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 1. 检查 Ollama 安装
    try:
        result = subprocess.run(
            ["ollama", "--version"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# 2. 检查可用模型
    try:
        result = subprocess.run(
            ["ollama", "list"],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The file's docstrings, status messages, and user instructions are all presented in Chinese, including the runtime output shown to users. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is explicitly justified, which is not present here.

Missing User Warnings

Low
Confidence
90% confidence
Finding
The skill documentation does not clearly warn users that image data will be transmitted to a local Ollama service for processing, nor does it prominently disclose that first use may trigger an automatic multi-gigabyte model download. This creates a transparency and consent issue: users may expose sensitive local images to another service/process on the machine and incur unexpected bandwidth, storage, or operational costs.

Missing User Warnings

Low
Confidence
83% confidence
Finding
This code sends a base64-encoded copy of the user-provided image to an HTTP API endpoint via requests.post. Although the module docstring says it analyzes images with Ollama, there is no user-facing warning at the point of transmission about sending image contents to the local service, which is a data-handling operation covered by the warning rule.

Static analysis

No suspicious patterns detected.