Back to skill

Security audit

Smart Router for Ollama

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its Ollama routing purpose, but it needs Review because it can send, store, and enrich user prompts in ways that are under-disclosed.

Before installing, use this only with Ollama endpoints you control, prefer local-only or HTTPS-protected remote endpoints, disable or explicitly configure search, and avoid sending secrets unless you are comfortable with prompt text being logged locally and possibly stored in a SQLite conversation database.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T01 · Skill Instruction Hijacking

Error
Location
scripts/search_integration.py:124
Finding

Untrusted Web Search Content Is Injected Directly into Model Prompts

Content
View full analysis
"] context_parts.append(f"Search results for: {task}\n") for i, result in enumerate(search_results, 1): title = result.get("title", "No title") content = result.get("content", "")[:200] context_parts.append(f"[{i}] {title}") if content: context_parts.append(f" {content}") context_parts.append("") context_parts.append("\n") return "\n".join(context_parts) ``` ```python # scripts/route.py:237-245 if SEARCH_AVAILABLE and search_enabled and is_search_query(task): yield "\n[Detected web search query, fetching results...]\n" search_context = get_search_context( task, limit=config.get("search", {}).get("limit", 5) ) if search_context: search_used = True yield "[Web search results added to context]\n\n" ``` ``` ...[truncated 2936 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/execute.py:43
Finding

Unvalidated Ollama Endpoint URLs Permit Server-Side Request Forgery and Plaintext Prompt Transmission

Content
View full analysis
list[str]: """List available models from Ollama instance.""" try: response = requests.get(f"{base_url}/api/tags", timeout=10) ``` ```python # scripts/health_check.py:37-43 version_resp = requests.get(f"{base_url}/api/version", timeout=timeout) version_resp.raise_for_status() result["version"] = version_resp.json().get("version", "unknown") # Check models endpoint models_resp = requests.get(f"{base_url}/api/tags", timeout=timeout) models_resp.raise_for_status() ``` ```yaml # config/router.yaml:12-16 cloud: model: "qwen2.5:14b" base_url: "http://your-server:11434" cost_per_1k_tokens: 0.0 max_tokens: 8192 ``` ### Technical Analysis The router accepts endpoint URLs from configuration and uses them directly in outbound HTTP requests. It does not enforce: - An allowed URL scheme. - An endpoint al ...[truncated 2820 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/route.py:79
Finding

User Prompts Are Persisted in Plaintext Logs and Conversation Databases

Content
View full analysis
50 else ''}' -> {decision} | model: {model} ({url_hint}) | latency: {latency:.2f}s{conv_hint}{search_hint}\n" with open(log_path, "a") as f: f.write(log_line) ``` ```python # scripts/conversation.py:41-67 def __init__(self, db_path: str = "cache/conversations.db"): """Initialize conversation memory.""" self.db_path = Path(db_path) self.db_path.parent.mkdir(parents=True, exist_ok=True) self._init_db() def _init_db(self): """Initialize database tables.""" with sqlite3.connect(self.db_path) as conn: conn.execute(""" CREATE TABLE IF NOT EXISTS conversations ( conversation_id TEXT PRIMARY KEY, created_at REAL, last_updated REAL ) """) conn.execute(""" CREATE TABLE IF NOT EXISTS turns ( id INTEGER PRIMARY KEY AUTOINCREMENT, conversation_id TEXT, user_message TEXT, classification_score INTEGER, routed_to TEXT, ...[truncated 3650 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (39)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

If the skill actually performs external web search and augments prompts with remote content while presenting itself as an Ollama router, that is a meaningful undeclared data-flow change. Hidden outbound search can leak user prompts or sensitive context to third-party infrastructure and changes the trust boundary in a way users would not expect.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

If the skill actually performs external web search and augments prompts with remote content while presenting itself as an Ollama router, that is a meaningful undeclared data-flow change. Hidden outbound search can leak user prompts or sensitive context to third-party infrastructure and changes the trust boundary in a way users would not expect.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

If the skill actually performs external web search and augments prompts with remote content while presenting itself as an Ollama router, that is a meaningful undeclared data-flow change. Hidden outbound search can leak user prompts or sensitive context to third-party infrastructure and changes the trust boundary in a way users would not expect.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

If the skill actually performs external web search and augments prompts with remote content while presenting itself as an Ollama router, that is a meaningful undeclared data-flow change. Hidden outbound search can leak user prompts or sensitive context to third-party infrastructure and changes the trust boundary in a way users would not expect.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

If the skill actually performs external web search and augments prompts with remote content while presenting itself as an Ollama router, that is a meaningful undeclared data-flow change. Hidden outbound search can leak user prompts or sensitive context to third-party infrastructure and changes the trust boundary in a way users would not expect.

Content

No source excerpt is available for this finding.

Memory Manipulation

High
Category
Memory Poisoning
Confidence
80% confidence
Finding

Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Content

Scanner excerpt · scripts/conversation.py (reported line 227)May include surrounding context.

python
for conv_id in old_ids:
                conn.execute("DELETE FROM turns WHERE conversation_id = ?", (conv_id,))
            
            # Delete conversations
            conn.execute("DELETE FROM conversations WHERE last_updated < ?", (cutoff,))
            conn.commit()

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill documents capabilities that imply shell, file write, and network access, but it does not declare any explicit tool scope or permission boundaries. In a skill that profiles hardware, checks endpoints, reads/writes config, and contacts remote Ollama servers, this omission increases the chance of over-broad execution and silent data flow to local or remote resources.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The invocation guidance is so broad that the skill could activate for many ordinary requests and route them through logic involving networked model endpoints. Over-broad triggering increases the chance that unrelated or sensitive user requests are processed by this skill without clear user intent or awareness, especially given its remote-routing design.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation does not clearly warn users that their requests may be transmitted to a remote/cloud Ollama instance. In a routing skill whose core behavior is deciding between local and cloud execution, omission of that warning can cause unintended disclosure of prompts, code, or confidential data to another host.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Claiming that no data leaves the user's infrastructure conflicts with documented remote/cloud Ollama endpoints, which can cause users to submit sensitive prompts under a false privacy assumption. In this skill context, routing prompts to a remote server is central behavior, so misleading privacy claims materially increase the risk of unintended disclosure.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This is a manifest file, so vague trigger review applies. Triggers such as "refactor", "optimize", "research", and "comprehensive analysis" are broad natural-language phrases without scope limits or exclusion conditions, making it unclear when these specialist routes should or should not activate.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The function contract says classify_task returns three values, but the long-input branches return only two while other paths return three. This inconsistency can cause runtime unpacking errors or incorrect control flow, creating a denial-of-service condition where certain user inputs reliably crash the router instead of being classified. In a smart-routing skill, input-dependent crashes are security-relevant because an attacker can trigger failure simply by sending long prompts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The early returns for long inputs omit the third return value, so input length alone can trigger a ValueError when callers expect the documented tuple shape. Because this skill routes arbitrary user tasks, an attacker can submit intentionally long requests to crash classification and potentially disrupt fallback routing or service availability. The skill context makes this more dangerous because the classifier sits on the routing path for every request.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code stores raw user_message content and routing metadata in a persistent SQLite database on disk without any disclosure, consent flow, minimization, encryption, or access controls visible in this file. In the context of an LLM router, user prompts can contain sensitive personal, business, or credential-like data, so silent persistence increases privacy and data exposure risk if the host is shared, backed up, or compromised.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/execute.py (reported line 55)May include surrounding context.

python
if system:
                payload["system"] = system
            
            response = requests.post(url, json=payload, stream=stream, timeout=120)
            response.raise_for_status()
            
            if stream:

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/execute.py (reported line 135)May include surrounding context.

python
if system:
                payload["system"] = system
            
            response = requests.post(url, json=payload, stream=stream, timeout=120)
            response.raise_for_status()
            
            if stream:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The logger writes raw task content to disk, which can capture secrets, personal data, proprietary prompts, or credentials entered by the user. In this skill context, routing requests may contain highly sensitive model inputs, so silent prompt logging materially increases confidentiality risk.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The router performs web-search augmentation even though the skill is described as a model-routing component, expanding its behavior to external data retrieval and outbound network access. That mismatch increases data-exposure risk because user prompts may be sent to search infrastructure or enriched with untrusted remote content without the user clearly invoking a search feature.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

Including a search capability not justified by the stated routing purpose violates least privilege and broadens the trust boundary of the skill. In a routing tool, unexpected external lookup is more dangerous because users may provide sensitive prompts expecting only local/cloud model selection, not third-party data access or prompt enrichment from the web.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill fetches web search context based on task content without explicit user warning, causing potential leakage of sensitive user prompts to external services and introducing untrusted content into the model prompt. Because the skill is presented as a smart router, users are less likely to expect outbound search behavior, making this more dangerous in context.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/route.py (reported line 348)May include surrounding context.

python
# Run profiler if requested
    if args.profile:
        profiler_script = Path(__file__).parent / "system_profiler.py"
        subprocess.run([sys.executable, str(profiler_script)])
        print("\n" + "=" * 50)
        print("Now routing your task...")
        print("=" * 50 + "\n")

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill manifest describes intelligent routing between local and cloud Ollama models, but this code introduces separate external web-search behavior via SearXNG. That expands the trust boundary and data flow beyond the stated purpose, which can surprise users and operators, especially because user prompts may be sent to an external endpoint and remote content is then streamed back into the interaction.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code sends the full user task to an external SearXNG endpoint whenever a query is classified as requiring search, without warning, consent, or redaction. User prompts can contain secrets, personal data, internal project details, or regulated information, so silently transmitting them off-box creates a real privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

get_search_context() also performs an external search using the raw task text, but unlike the streaming path it provides no visible disclosure at all. Because this helper is likely to be used to silently enrich prompts, it can exfiltrate sensitive user input without the user realizing that external network access occurred.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The class docstring says it 'adds web search capability' and 'augments with web results,' but in route() the code only streams search output to the caller and then calls self.router.route(task, conversation_id) with the original task unchanged. The inline note at L197 acknowledges that prompt injection of search results is not implemented, directly contradicting the documented behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.