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]
