Back to skill

Security audit

Adaptive Routing

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a local/cloud routing helper, but it can send sensitive prompts to cloud paths without clear consent and includes an unsafe remote installer command.

Review this skill before installing if you plan to use it for secrets, private business data, health data, or source code. Do not allow automatic cloud fallback for sensitive prompts unless you explicitly approve the destination provider, and avoid the documented curl-to-sh installer; use a verified package-manager or vendor install path instead.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T03 · Remote Payload Retrieval and Execution

Error
Location
references/local-providers.md:7
Finding
Unverified Remote Installation Script Is Piped Directly Into a Shell<![CDATA[ ## Vulnerability Details **File Location**: `references/local-providers.md:7-13` **Vulnerability Type**: `T03: Remote Payload Retrieval and Execution` **Risk Level**: High ### Vulnerable Code ```bash **Install** ```bash # macOS brew install ollama # Linux curl -fsSL https://ollama.ai/install.sh | sh ``` ``` The security-sensitive command is: ```bash curl -fsSL https://ollama.ai/install.sh | sh ``` ### Technical Analysis The Linux installation instructions download mutable content from an external URL and immediately execute it with `sh`. The user is not given an opportunity to inspect the script, and the command does not pin a release, verify a cryptographic signature, or compare a published checksum. HTTPS protects the connection in ordinary circumstances but does not protect against compromise of the provider, its hosting infrastructure, DNS or certificate ecosystem, or the remote installation script itself. Because the effective payload can change after the Skill has been audited, the package's reviewed source does not fully define the code users are instructed to execute. This behavior is unnecessary for the Skill's core routing functionality. The Skill metadata only declares `python3` as required, while Ollama is one optional local provider among several. Recommending immediate remote execution therefore increases the supply-chain and code-execution scope beyond the minimum privileges needed by the Skill. ### Attack Path 1. An attacker compromises the installation endpoint, its deployment pipeline, or another component capable of modifying the script returned by `https://ollama.ai/install.sh`. 2. The attacker inserts commands into the remotely served script. 3. A user follows the documented Linux installation command. 4. `curl` retrieves the attacker's current payload and streams it directly to `sh`. 5. The payload executes with all privileges available to the invoking user. 6. The payload can read, modify, or delete data accessible to ...[truncated 789 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the direct `curl | sh` instruction. 2. Prefer an official, version-pinned operating-system package or repository with normal package-signing verification. 3. If a script-based installation must be documented: - Download the script to a local file. - Pin an immutable release URL where available. - Obtain the expected SHA-256 checksum or signature through a separately authenticated channel. - Verify the checksum or signature before execution. - Instruct the user to inspect the downloaded script. - Execute it only after explicit user approval. 4. Clearly state what files, services, users, groups, and network listeners the installer creates. 5. Avoid recommending elevated execution unless a particular operation strictly requires it, and document that operation separately. 6. Present optional providers as optional dependencies rather than prerequisites for the Python routing utilities. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/validate_result.py:64
Finding
Sensitive Prompts Can Be Automatically Escalated to a Cloud Provider<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/route_request.py:119-130` - `scripts/validate_result.py:64-72` - `references/routing-logic.md:23-31` - `references/routing-logic.md:60-67` **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: High ### Vulnerable Code The pre-flight router initially forces detected sensitive prompts to remain local: ```python if not args.local_available: decision = "cloud" reason = "No local LLM provider is running" elif sensitive: decision = "local" reason = "Prompt contains sensitive data — routing locally for privacy" elif complexity >= threshold: decision = "cloud" reason = f"High complexity score ({complexity}) — routing to cloud for best results" else: decision = "local" reason = f"Simple/moderate request (complexity={complexity}) — local model sufficient" ``` However, the validation component unconditionally recommends cloud escalation after any failure: ```python score = max(0.0, min(1.0, score)) passed = score >= args.min_score and len(fail_reasons) == 0 print(json.dumps({ "passed": passed, "score": round(score, 2), "reason": "ok" if passed else ", ".join(fail_reasons), "should_escalate": not passed, "validation_mode": "heuristic", "min_score": args.min_score, })) ``` The documented workflow directs the caller to perform that escalation: ```text route_request.py → local ↓ Execute with local provider ↓ validate_result.py ├── passed=true → use local result → track_savings.py log --kind local_success └── passed=false → re-run with cloud → track_savings.py log --kind escalated ``` ### Technical Analysis The sensitivity decision is not propagated into `validate_result.py`. Consequently, `should_escalate` is calculated solely from execution and response signals and is always the inverse of `passed`. A prompt can therefore be classified as sensitive and routed locally for privacy, but then be sent to a cloud provi ...[truncated 2206 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Propagate the sensitivity decision through the entire workflow using a structured field rather than relying on documentation. 2. Add a sensitivity option to validation and make escalation fail closed: ```python should_escalate = (not passed) and (not args.sensitive) requires_user_consent = (not passed) and args.sensitive ``` 3. Change routing order so sensitivity is evaluated before availability: ```python if sensitive and not args.local_available: decision = "blocked" reason = "Sensitive request cannot be processed because no local provider is available" elif sensitive: decision = "local" ``` 4. Introduce an explicit result such as `blocked`, `local_retry`, or `consent_required` rather than overloading `local` and `cloud`. 5. Require explicit, informed user approval before transmitting sensitive content to any external service. 6. Show the destination provider, the categories of data detected, and the exact disclosure boundary when requesting approval. Do not display secret values themselves. 7. Apply robust secret and personal-data redaction only when redaction preserves the task's meaning; otherwise block cloud transmission. 8. Add integration tests covering: - Sensitive prompt with no local provider. - Sensitive prompt followed by timeout. - Sensitive prompt followed by provider error. - Sensitive prompt followed by empty output. - Explicit user consent and denial paths. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/validate_result.py:45
Finding
Claimed Quality Gate Accepts Every Non-Empty Successful Response<![CDATA[ ## Vulnerability Details **File Location**: `scripts/validate_result.py:45-72` **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: Medium ### Vulnerable Code ```python score = 1.0 fail_reasons: list[str] = [] # 1. Provider / process error if args.exit_code != 0: score -= 1.0 fail_reasons.append("provider_error") # 2. Timeout — treat as truncation, not hard failure if args.timed_out: score -= 0.3 fail_reasons.append("timed_out") # 3. Tool execution error if args.tool_error.strip(): score -= 0.6 fail_reasons.append(f"tool_error:{args.tool_error.strip()}") # 4. Empty assistant output if not args.response.strip(): score -= 0.4 fail_reasons.append("empty_assistant_output") score = max(0.0, min(1.0, score)) passed = score >= args.min_score and len(fail_reasons) == 0 print(json.dumps({ "passed": passed, "score": round(score, 2), "reason": "ok" if passed else ", ".join(fail_reasons), "should_escalate": not passed, "validation_mode": "heuristic", "min_score": args.min_score, })) ``` ### Technical Analysis The validator does not evaluate correctness, relevance, completeness, factuality, policy compliance, output structure, or correspondence to the original task. It only checks externally supplied process flags and whether the response contains at least one non-whitespace character. As a result, any non-empty response receives a score of `1.0` when the caller supplies an exit code of zero and no timeout or tool-error flag. Examples that pass include an irrelevant sentence, a fabricated answer, an incomplete result, malformed structured data, or adversarial instructions emitted by a compromised local provider. The `--min-score` option does not address this weakness because the score remains `1.0` for every non-empty response without reported process errors. The implementation is therefore an availability and transport-health check, not the advertised response-quality gat ...[truncated 1435 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Rename the current component to indicate that it performs transport or execution-health validation rather than quality validation, unless substantive quality checks are added. 2. Pass the original task and expected output constraints to the validator. 3. Add task-specific validation mechanisms, including: - JSON Schema validation for structured output. - Required-section and length checks for reports. - Compilation, linting, and isolated tests for generated code. - Citation or source verification for factual tasks. - Comparison against explicit acceptance criteria. 4. Treat local-model output as untrusted data. Do not execute commands or follow tool instructions from it without an independent policy and user authorization check. 5. For high-impact workflows, use an independent verifier with a distinct model or deterministic validation mechanism. 6. Return separate fields for process health and semantic quality instead of combining them into one misleading score. 7. Add adversarial tests demonstrating that irrelevant, malformed, incomplete, and instruction-injecting responses are rejected. 8. Preserve fail-safe behavior: if semantic quality cannot be established, return an `unverified` state rather than `passed: true`. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The code only performs service discovery for three local providers by querying localhost endpoints and listing models. While this is related to the declared ecosystem of supported local providers, it does not implement the skill's main described behavior: routing requests, validating local output quality, escalating to cloud on failure, or tracking outcomes in a persistent dashboard. This is a material description-behavior mismatch because the supplied code chunk represents only a small supporting check, not the declared primary purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description centers on an adaptive execution pipeline: send requests to a local model first, inspect the returned answer, and only then escalate to cloud if quality is insufficient, while persistently tracking outcomes in a dashboard. The supplied code does something much narrower: it is a standalone command-line decision helper that chooses either local or cloud upfront using heuristic complexity and sensitivity checks. It never invokes a model, never inspects a response, and never performs retry/escalation after a failed local attempt. It also does not store metrics, update any dashboard, or report savings. While local/cloud routing is related to the declared theme, the core promised behaviors—quality validation, fallback based on actual output, persistent tracking, and provider support beyond a label—are absent, making this a material description/behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description presents a full adaptive-routing system for LLM inference, including local provider support, quality validation, fallback to cloud, and dashboard tracking. The supplied code chunk only implements the tracking portion: a command-line script that records outcome kinds, token counts, estimated cloud cost savings, and persists them to ~/.openclaw/adaptive-routing/savings.json. While this partially aligns with the tracking/dashboard aspect, the primary advertised capabilities—routing, validation, escalation logic, and provider support—are absent from this code. Therefore the description materially overstates what this code chunk actually does.

External Model or Provider Selection

High
Category
Excessive Agency
Content
```bash
# Local success (no escalation needed)
python3 skills/adaptive-routing/scripts/track_savings.py log \
  --kind local_success --tokens 800 --model gpt-4o

# Escalated (local failed validation, used cloud)
python3 skills/adaptive-routing/scripts/track_savings.py log \
Confidence
90% confidence
Finding
Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

External Model or Provider Selection

High
Category
Excessive Agency
Content
# Escalated (local failed validation, used cloud)
python3 skills/adaptive-routing/scripts/track_savings.py log \
  --kind escalated --tokens 800 --model gpt-4o
```

### 6. Show the dashboard
Confidence
90% confidence
Finding
Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

External Script Fetching

High
Category
Supply Chain
Content
brew install ollama

# Linux
curl -fsSL https://ollama.ai/install.sh | sh
```

**Start server**
Confidence
98% confidence
Finding
`curl -fsSL https://ollama.ai/install.sh | sh` fetches a remote script and immediately executes it without prior inspection, integrity verification, or pinning. If the host, network path, or downloaded script is compromised, users can suffer arbitrary code execution on their machine during installation.

Chaining Abuse

High
Category
Tool Misuse
Content
brew install ollama

# Linux
curl -fsSL https://ollama.ai/install.sh | sh
```

**Start server**
Confidence
98% confidence
Finding
The `| sh` pipeline is dangerous because it turns remotely fetched content directly into shell commands, eliminating the user's opportunity to review what will run. In a setup guide, this normalizes a risky pattern that can lead to immediate system compromise if the fetched content is malicious or tampered with.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill documentation instructs use of shell commands, network calls, and persistent file writes, yet it declares no explicit tool scope or permissions boundary. This creates a trust and review gap: an agent may execute capabilities broader than a user expects, including writing routing state under the home directory and sending prompts to local HTTP services.

Session Persistence

Medium
Category
Rogue Agent
Content
## Configuration

Create `~/.openclaw/adaptive-routing/config.json` to tune thresholds:

```json
{
Confidence
88% confidence
Finding
The skill persists configuration and, elsewhere in the document, savings/outcome data under ~/.openclaw/adaptive-routing. Persistent storage can retain sensitive routing metadata, prompt-related statistics, and operational preferences across sessions, which may expose privacy-sensitive usage patterns on shared systems or through overly permissive file permissions.

External Transmission

Medium
Category
Data Exfiltration
Content
### Ollama

```bash
curl http://localhost:11434/api/generate \
  -d '{"model": "llama3.2", "prompt": "YOUR_PROMPT", "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.

External Transmission

Medium
Category
Data Exfiltration
Content
**Test**

```bash
curl http://localhost:11434/api/generate \
  -d '{"model":"llama3.2","prompt":"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.

Whitespace Padding

Medium
Category
Prompt Injection
Content
Each request is scored. Higher = more complex = prefer cloud.

| Factor                                                                                                                                                                                   | Score Change |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| Keyword match: `analyze`, `synthesize`, `compare`, `reason`, `architecture`, `code review`, `multi-step`, `evaluate`, `critique`, `refactor`, `design`, `implement`, `debug`, `strategy` | +2 per match |
| Keyword match: `summarize`, `translate`, `list`, `what is`, `define`, `explain briefly`, `convert`, `format`, `reformat`, `spell check`                                                  | −1 per match |
Confidence
70% confidence
Finding
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Whitespace Padding

Medium
Category
Prompt Injection
Content
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| Keyword match: `analyze`, `synthesize`, `compare`, `reason`, `architecture`, `code review`, `multi-step`, `evaluate`, `critique`, `refactor`, `design`, `implement`, `debug`, `strategy` | +2 per match |
| Keyword match: `summarize`, `translate`, `list`, `what is`, `define`, `explain briefly`, `convert`, `format`, `reformat`, `spell check`                                                  | −1 per match |
| Token count > `token_high_watermark` (default 4,000)                                                                                                                                     | +2           |
| Token count < `token_low_watermark` (default 500)                                                                                                                                        | −1           |

### Decision Tree
Confidence
70% confidence
Finding
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

Whitespace Padding

Medium
Category
Prompt Injection
Content
| Keyword match: `analyze`, `synthesize`, `compare`, `reason`, `architecture`, `code review`, `multi-step`, `evaluate`, `critique`, `refactor`, `design`, `implement`, `debug`, `strategy` | +2 per match |
| Keyword match: `summarize`, `translate`, `list`, `what is`, `define`, `explain briefly`, `convert`, `format`, `reformat`, `spell check`                                                  | −1 per match |
| Token count > `token_high_watermark` (default 4,000)                                                                                                                                     | +2           |
| Token count < `token_low_watermark` (default 500)                                                                                                                                        | −1           |

### Decision Tree
Confidence
70% confidence
Finding
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def run(args: list) -> dict:
    try:
        result = subprocess.run(
            [PY] + args, capture_output=True, text=True, timeout=5
        )
        return json.loads(result.stdout) if result.returncode == 0 and result.stdout.strip() else {}
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.