Back to skill

Security audit

Ollama Local

Security checks for vulnerabilities and agentic risk

Overview

This skill is a straightforward Ollama helper, but users should only point it at trusted Ollama servers because prompts are sent to the configured host.

Install only if you are comfortable running the included Python helper scripts. Keep OLLAMA_HOST on localhost for private work, or use only a trusted remote Ollama server with appropriate transport protections. Avoid sending secrets in prompts or embedding text, and verify the model name before using rm because it deletes a local Ollama model.

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/ollama.py:14
Finding
Configurable plaintext Ollama endpoint can disclose sensitive prompt data<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/ollama.py:14-49` - `scripts/ollama.py:110-166` - `scripts/ollama_tools.py:18` - `scripts/ollama_tools.py:82-102` - `scripts/ollama_tools.py:126-160` - `SKILL.md:13-19` **Vulnerability Type**: Transmission of potentially sensitive data to an unrestricted, potentially plaintext endpoint **Risk Level**: Medium ### Vulnerable Code From `scripts/ollama.py:14-49`: ```python OLLAMA_HOST = os.environ.get("OLLAMA_HOST", "http://localhost:11434") def api_request(endpoint, method="GET", data=None): """Make request to Ollama API (non-streaming).""" url = f"{OLLAMA_HOST}{endpoint}" headers = {"Content-Type": "application/json"} if data else {} req = urllib.request.Request( url, data=json.dumps(data).encode() if data else None, headers=headers, method=method ) try: with urllib.request.urlopen(req, timeout=300) as resp: return json.loads(resp.read()) except urllib.error.URLError as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) def api_stream(endpoint, data): """Make streaming request to Ollama API.""" url = f"{OLLAMA_HOST}{endpoint}" req = urllib.request.Request( url, data=json.dumps(data).encode(), headers={"Content-Type": "application/json"}, method="POST" ) try: with urllib.request.urlopen(req, timeout=300) as resp: for line in resp: if line.strip(): yield json.loads(line) ``` Sensitive prompt construction in `scripts/ollama.py:110-166`: ```python def chat(model_name, message, system=None, stream=True): """Chat with a model.""" messages = [] if system: messages.append({"role": "system", "content": system}) messages.append({"role": "user", "content": message}) data = { "model": model_name, "messages": messages, "stream": ...[truncated 5323 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Restrict the default trust boundary** - Allow loopback destinations such as `localhost`, `127.0.0.1`, and `::1` by default. - Require an explicit option such as `--allow-remote-host` before connecting to a non-loopback address. 2. **Require secure transport remotely** - Reject `http://` whenever the destination is not loopback. - Require `https://` for remote Ollama servers. - Preserve normal TLS certificate and hostname verification; do not introduce an unverified SSL context. 3. **Validate the endpoint** - Parse `OLLAMA_HOST` with `urllib.parse.urlparse`. - Permit only supported schemes, preferably `http` for loopback and `https` for remote hosts. - Reject missing hostnames, embedded credentials, fragments, and unexpected schemes. - Normalize the base URL before appending API paths. 4. **Make disclosure visible** - Display the resolved remote destination before transmitting prompts. - Require interactive confirmation for remote use unless the user supplies an explicit noninteractive consent flag. - Warn that prompts, system instructions, embedding text, tool arguments, and tool results will leave the local machine. 5. **Support authenticated remote deployments** - Add an optional authorization mechanism appropriate for the Ollama proxy or gateway. - Read credentials from a protected credential source rather than command-line arguments or hardcoded values. - Never log authorization values. 6. **Reduce transmitted data** - Avoid retaining or retransmitting conversation and tool history beyond what is required. - Provide a redaction or confirmation mechanism for likely credentials and sensitive content. - Document that embedding input is transmitted just like chat prompts. 7. **Apply the same centralized validation to both scripts** - Implement one shared endpoint-validation and request-construction helper. - Use it in `ollama.py` and `ollama_tools.py` so secu ...[truncated 33 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

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

Critical
Category
Data Flow
Content
)
    
    try:
        with urllib.request.urlopen(req, timeout=300) as resp:
            return json.loads(resp.read())
    except urllib.error.URLError as e:
        print(f"Error: {e}", file=sys.stderr)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
)
    
    try:
        with urllib.request.urlopen(req, timeout=300) as resp:
            return json.loads(resp.read())
    except urllib.error.URLError as e:
        print(f"Error: {e}", file=sys.stderr)
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
method="POST"
    )
    
    with urllib.request.urlopen(req, timeout=120) as resp:
        result = json.loads(resp.read())
    
    return result
Confidence
95% confidence
Finding
The request URL is built from the OLLAMA_HOST environment variable and sent directly to urllib.request.urlopen without validation. In an agent/skill context, this can enable server-side request forgery or exfiltration to an attacker-controlled host if the environment is influenced, especially because the code sends user prompts, system prompts, and tool metadata over the network.

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

Critical
Category
Data Flow
Content
method="POST"
        )
        
        with urllib.request.urlopen(req, timeout=120) as resp:
            result = json.loads(resp.read())
        
        msg = result.get("message", {})
Confidence
95% confidence
Finding
This is the same unvalidated network sink in the tool loop path: OLLAMA_HOST from the environment controls where chat messages and tool interaction data are posted. If an attacker can set or influence that variable, they can redirect sensitive prompts and responses to an arbitrary service and potentially manipulate returned tool-call content.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
The code substantially matches the core declared purpose around local Ollama model management and usage: listing models, pulling/removing models, showing model details, chat/completions, and embeddings are all present. However, the description also explicitly claims support for tool-use, OpenClaw sub-agent integration, and model selection guidance, none of which appear in this code chunk. There are no undeclared dangerous capabilities beyond making HTTP requests to the configured Ollama host, which is consistent with the stated purpose. This is therefore a description-to-behavior mismatch due to overclaiming unsupported features.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description is significantly broader than what this code chunk implements. The code only interacts with the Ollama /api/chat endpoint and is specifically a tool-use helper for local models. It defines example tools, sends tool-enabled chat requests, and simulates executing requested tools in a loop. It does not implement model management, embeddings, model pulling/removal/listing, OpenClaw integration, or guidance logic. While tool-use with local Ollama models is accurately represented, the supplied code chunk does not substantiate most of the declared capabilities, so the description does not accurately represent the actual behavior of this chunk.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# Chat
curl $OLLAMA_HOST/api/chat -d '{
  "model": "qwen3:4b",
  "messages": [{"role": "user", "content": "Hello"}],
  "stream": false
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The rm command deletes a local model immediately with no confirmation, dry-run, or safety interlock. In an agent or automation context, a mistaken invocation, prompt-induced tool call, or wrong model name can cause irreversible local data loss and service disruption more easily than an interactive human-operated CLI would.

Missing User Warnings

Low
Confidence
92% confidence
Finding
The guide documents a destructive removal command (`rm modelname`) without warning that it deletes local models or suggesting confirmation/backups. In an operational setting, users may copy-paste it and unintentionally remove required models, causing availability loss or workflow disruption.

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
The example tool schema declares a `run_code` function with the description `Execute Python code and return the result`, which implies real code execution capability. However, `execute_tool_call` later returns a fixed simulated response for `run_code` rather than executing anything, so the inline documentation overstates what the code does.

Intent-Code Divergence

Low
Confidence
79% confidence
Finding
The docstring for `execute_tool_call` says this is a mock implementation, but the exposed example tool catalog includes realistic capabilities like `search_web` and `run_code` without marking them as mock/demo-only in those tool definitions. This creates an intent mismatch between the helper's documentation and what it advertises to the model as callable tools.

Static analysis

No suspicious patterns detected.