T09 · Insecure Skill Coding Practices
- Location
scripts/ollama.py:14- Finding
Configurable plaintext Ollama endpoint can disclose sensitive prompt data
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
scripts/ollama.py:14Configurable plaintext Ollama endpoint can disclose sensitive prompt data
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
)
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)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
)
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)
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.
method="POST"
)
with urllib.request.urlopen(req, timeout=120) as resp:
result = json.loads(resp.read())
return result
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.
method="POST"
)
with urllib.request.urlopen(req, timeout=120) as resp:
result = json.loads(resp.read())
msg = result.get("message", {})
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.
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.
Without declared permissions the skill's intent is opaque and cannot be validated.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Chat
curl $OLLAMA_HOST/api/chat -d '{
"model": "qwen3:4b",
"messages": [{"role": "user", "content": "Hello"}],
"stream": false
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.
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.
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.
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.
No suspicious patterns detected.