Back to skill

Security audit

Super Router

Security checks for vulnerabilities and agentic risk

Overview

This is a real model-routing skill, but it uses broad local execution, environment secrets, and agentic provider tools in ways that need careful review before installation.

Install only if you are comfortable running a local router that may send task text and project context to configured model providers. Use a dedicated environment with minimal secrets, avoid putting unrelated credentials in ~/.openclaw/.env, keep LangSmith prompt/output tracing off for sensitive work, prefer local-only Ollama when needed, and do not route untrusted task text until the shell-invocation examples and Gemini auto-approval behavior are hardened.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (6)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:21
Finding

Command Injection Through Mandatory Task Interpolation

Content
View full analysis
'` ``` The same unsafe construction is demonstrated later: ```python exec( command="/opt/homebrew/Caskroom/miniforge/base/bin/python /Users/fanyadan/.openclaw/skills/super-router/scripts/router.py 'Analyze K8s YAML errors and rewrite config'" ) ``` ### Technical Analysis The mandatory invocation workflow instructs the agent to interpolate user-controlled task text between single quotes inside a shell command string. Single quotes do not provide protection when the input itself can contain a single quote. An attacker can close the quoted task argument, append shell syntax, and comment out the remainder. Because the resulting value is supplied to an `exec` interface as a command string, it may be parsed by a shell rather than passed as a structured argument vector. This is especially serious because the unsafe form is described as mandatory. The documentation contains a safer environment-variable invocation elsewhere, but it does not require that form for ordinary tasks. ### Attack Path 1. An attacker submits a router task containing a single quote and shell metacharacters, conceptually: ```text benign task'; ATTACKER_COMMAND; # ``` 2. The agent follows the mandatory instruction and substitutes the task into: ```text router.py '' ``` 3. The injected quote terminates the task argument. 4. The shell interprets the appended text as an additional command. 5. The injected command executes with the operating-system privileges and filesystem acces ...[truncated 378 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/router.py:40
Finding

Unrestricted Secret Loading and Environment Propagation to Provider Processes

Content
View full analysis
None: """Load KEY=VALUE pairs from OpenClaw's .env without overriding explicit env.""" configured_path = (env_file or os.environ.get("OPENCLAW_ENV_FILE", "")).strip() if configured_path: env_path = os.path.expanduser(configured_path) else: openclaw_home = os.path.expanduser(os.environ.get("OPENCLAW_HOME", "~/.openclaw")) env_path = os.path.join(openclaw_home, ".env") if not env_path or not os.path.exists(env_path): return try: with open(env_path, "r", encoding="utf-8") as env_handle: lines = env_handle.readlines() except OSError: return for raw_line in lines: line = raw_line.strip() if not line or line.startswith("#"): continue if line.startswith("export "): line = line[len("export ") :].strip() if "=" not in line: continue key, value = line.split("=", 1) key = key.strip() if not key or key in os.environ: continue value = value.strip() if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}: value = value[1:-1] os.environ[key] = value ``` Provider processes then inherit the complete environment. For example, Gemini uses: ```python env = dict(os.environ) env["NO_COLOR"] = "1" env["NO_BROWSER"] = "true" ... result = subprocess.run( command, capture_output=True, text=True, timeout=timeout, env=env, check=False, stdin=subprocess.DEVNULL, ) ...[truncated 1777 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/router.py:3026
Finding

Gemini Tool Calls Are Automatically Approved Without a Router-Level Sandbox

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/router.py:2825
Finding

Router Reads the Complete OpenClaw Authentication Profile Store

Content
View full analysis
str: agent_dir = os.environ.get("OPENCLAW_AGENT_DIR", "").strip() if agent_dir: return os.path.join(os.path.expanduser(agent_dir), "auth-profiles.json") openclaw_home = os.path.expanduser(os.environ.get("OPENCLAW_HOME", "~/.openclaw")) return os.path.join(openclaw_home, "agents", "main", "agent", "auth-profiles.json") def detect_openclaw_gemini_auth_issue(now_ms: int | None = None) -> str: if env_flag_enabled("ROUTER_IGNORE_OPENCLAW_GEMINI_AUTH", False): return "" auth_path = openclaw_auth_profiles_path() if not os.path.exists(auth_path): return "" try: with open(auth_path, "r", encoding="utf-8") as auth_file: auth_data = json.load(auth_file) except (OSError, json.JSONDecodeError): return "" profiles = auth_data.get("profiles") if not isinstance(profiles, dict): return "" gemini_profiles = [ profile for profile in profiles.values() if isinstance(profile, dict) and profile.get("provider") == "google-gemini-cli" ] ``` The function then inspects expiry and account email fields: ```python for profile in gemini_profiles: expires = profile.get("expires") or profile.get("expiresAt") try: expires_ms = int(expires) except (TypeError, ValueError): continue if expires_ms <= now + 60_000: email = str(profile.get("email") or "configured account") return ( "OpenClaw Gemini OAuth profile for " f"{email} is expired. Run `openclaw models auth login --provider google-gemini-cli`." ) ``` ### Technical Analysis The router opens and parses the complete authentication-profile file to provide an expiry preflight message. ...[truncated 1429 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:104
Finding

Unpinned Third-Party Runtime Dependencies

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
templates/source-html-background-wrapper.sh:43
Finding

Shell Injection Risk in Copyable Background Wrapper

Content
View full analysis
&1 | tee "$LOG" ROUTER_EXIT=${PIPESTATUS[0]} set -e ``` ### Technical Analysis The wrapper builds a new shell program inside a double-quoted string and inserts `PROMPT`, `PY`, and `ROUTER` using embedded single-quote delimiters. If any inserted path contains a single quote, command substitution syntax, or carefully formed shell content, it can break out of the intended quoting context when `zsh -lic` reparses the string. The nested login shell is unnecessary. The current shell can read the prompt into an environment variable and invoke Python using normal quoted arguments without constructing another shell program. The prompt file's contents are protected by command substitution into a double-quoted assignment, so the principal injection boundary is the interpolated path values rather than ordinary prompt contents. ### Attack Path 1. An attacker influences a copied wrapper placeholder or an environment-derived path, such as `RUN_DIR`, so that the resulting prompt path contains a quote and shell syntax. 2. The path is interpolated into the `zsh -lic` command string. 3. The nested Z shell reparses the generated string. 4. The crafted quote escapes the intended path literal. 5. Appended shell syntax executes with the wrapper user's privileges. ### Impact Assessment Successful exploitation provides command execution under the account running the background wrapper. The attacker can access the wrapper's environment, generated context and prompt files, router logs, and other files available to that user. ]]>
Remediation
View remediation
&1 | tee "$LOG" ROUTER_EXIT=${PIPESTATUS[0]} ``` Additional hardening steps: 1. Validate that `RUN_DIR`, `PROMPT`, `PY`, and `ROUTER` resolve to expected absolute paths. 2. Use `--` before untrusted path operands where supported. 3. Reject newline and control characters in configured paths. 4. Avoid `eval`, `sh -c`, `zsh -c`, and similar reparsing constructs. 5. Add regression tests using paths containing spaces, quotes, dollar signs, semicolons, and newlines. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (49)

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

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The router sends prompts to a network endpoint derived from ROUTER_OLLAMA_URL, and those prompts can contain sensitive user tasks, subtasks, and prior outputs. In this skill context, the router handles arbitrary task content and may process confidential operational or security material, so exfiltration to an attacker-controlled endpoint is a real risk.

Content

Scanner excerpt · scripts/router.py (reported line 2936)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(request, timeout=timeout) as response:
            data = json.loads(response.read().decode("utf-8"))
    except urllib.error.HTTPError as exc:
        body = exc.read().decode("utf-8", errors="replace")

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description is for an operational LangGraph router/orchestrator. The supplied code chunk is instead a documentation/visualization utility. Its primary function is to inspect router.py for add_node calls in build_router_graph, compare them against a hardcoded expected node set, and render a diagram image with PIL. It does not perform task planning, dependency-aware subtask execution, model routing, failure handling, or audit logging beyond validating diagram freshness. Therefore the code's actual behavior is materially different from the declared purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description centers on a LangGraph task router with dependency-aware subtask decomposition, branch routing to different model classes (PRO/FLASH), retry/escalation handling, and token-usage auditing. This code chunk does not implement those behaviors directly. Instead, it is a shell wrapper that orchestrates a local analysis pipeline: collect context from a source tree, build a router prompt, invoke an existing router script with --stream, generate HTML, and verify the resulting artifact. Its primary purpose is producing and validating a self-contained HTML guide/report in a background job. While it does invoke a router script, the advertised routing/decomposition, retry/escalation, and token-audit capabilities are not present in this chunk, and the HTML-report generation capability is undeclared. Therefore the description does not accurately represent this code chunk.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 123)May include surrounding context.

md
claw/.env`. OpenClaw normally exposes this file to `exec` child processes, and `router.py` also loads it directly before reading `os.environ`, so the same comma

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 139)May include surrounding context.

md
claw/.env`. OpenClaw normally exposes this file to `exec` child processes, and `router.py` also loads it directly before reading `os.environ`, so the same comma

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 201)May include surrounding context.

md
claw/.env`. OpenClaw normally exposes this file to `exec` child processes, and `router.py` also loads it directly before reading `os.environ`, so the same comma

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 406)May include surrounding context.

md
claw/.env`. OpenClaw normally exposes this file to `exec` child processes, and `router.py` also loads it directly before reading `os.environ`, so the same comma

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 407)May include surrounding context.

md
| `SKILL.md` | This documentation |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 410)May include surrounding context.

md
| `references/source-html-background-wrapper.md` | Concrete pattern for source-tree-to-HTML hierarchy/explainer jobs: source JSON collector, compact router prom

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

Automatically loading an OpenClaw .env file imports credentials and sensitive configuration into process environment at startup. In a skill that later spawns multiple external CLIs and supports networked providers, this increases the chance those secrets are propagated or misused beyond the original need.

Content

Scanner excerpt · scripts/router.py (reported line 40)May include surrounding context.

python
def load_openclaw_env_file(env_file: str | None = None) -> None:
    """Load KEY=VALUE pairs from OpenClaw's .env without overriding explicit env."""
    configured_path = (env_file or os.environ.get("OPENCLAW_ENV_FILE", "")).strip()
    if configured_path:
        env_path = os.path.expanduser(configured_path)

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

Defaulting to ~/.openclaw/.env means the skill silently consumes user-local secret material without explicit notice. Combined with subprocess environment inheritance and external provider calls, this makes the skill context more dangerous than a standalone config helper.

Content

Scanner excerpt · scripts/router.py (reported line 46)May include surrounding context.

python
env_path = os.path.expanduser(configured_path)
    else:
        openclaw_home = os.path.expanduser(os.environ.get("OPENCLAW_HOME", "~/.openclaw"))
        env_path = os.path.join(openclaw_home, ".env")
    if not env_path or not os.path.exists(env_path):
        return

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
91% confidence
Finding

Copying the full process environment into the Gemini CLI subprocess can expose secrets such as API keys, proxy credentials, and internal tokens to an external tool. In this skill context, multiple third-party CLIs are invoked, so wholesale environment inheritance unnecessarily broadens credential exposure.

Content

Scanner excerpt · scripts/router.py (reported line 3049)May include surrounding context.

python
if not GEMINI_CLI_PATH or not os.path.exists(GEMINI_CLI_PATH):
        raise RuntimeError("Gemini CLI executable was not found. Set ROUTER_GEMINI_CLI or install `gemini`.")

    env = dict(os.environ)
    env["NO_COLOR"] = "1"
    env["NO_BROWSER"] = "true"
    # Force inject ripgrep + common tool paths (fixes "Ripgrep is not available" warning)

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The router includes Codex CLI execution with configurable sandbox and working directory, which is substantially more powerful than simple task routing. In context, this turns the skill into a launcher for external agent behavior that may inspect repository contents or invoke tools, increasing risk of unauthorized access or unintended actions.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
95% confidence
Finding

The Codex CLI subprocess inherits the full environment, which may include credentials and internal configuration. Because Codex is an agentic tool with configurable sandbox/cwd, inherited secrets are especially risky and could be exposed through tool behavior or downstream provider interactions.

Content

Scanner excerpt · scripts/router.py (reported line 3244)May include surrounding context.

python
if os.path.sep in CODEX_CLI_PATH and not os.path.exists(CODEX_CLI_PATH):
        raise RuntimeError("Codex CLI executable was not found. Set ROUTER_CODEX_CLI or install `codex`.")

    env = dict(os.environ)
    env["NO_COLOR"] = "1"
    sandbox = os.environ.get(ROUTER_CODEX_SANDBOX_ENV_VAR, "read-only").strip() or "read-only"
    command = [

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
90% confidence
Finding

Claude CLI also inherits the entire environment, exposing secrets to a child process that may communicate with external services. This is unnecessary for routing and increases the blast radius if the CLI is compromised or behaves unexpectedly.

Content

Scanner excerpt · scripts/router.py (reported line 3420)May include surrounding context.

python
if os.path.sep in CLAUDE_CLI_PATH and not os.path.exists(CLAUDE_CLI_PATH):
        raise RuntimeError("Claude CLI executable was not found. Set ROUTER_CLAUDE_CLI or install `claude`.")

    env = dict(os.environ)
    env["NO_COLOR"] = "1"
    command = [CLAUDE_CLI_PATH, "--model", normalized_model, "--output-format", "json", "-p", prompt]

Context Leakage

High
Category
Data Exfiltration
Confidence
79% confidence
Finding

The wrapper exports source-path and output locations, then invokes a context collection step that is specifically designed to extract information from the local source tree for later router use. Given the skill's purpose as a task router for model execution, this increases the likelihood that repository contents are aggregated into prompts and written to logs/artifacts, causing context leakage to external models or durable local files.

Content

Scanner excerpt · templates/source-html-background-wrapper.sh (reported line 26)May include surrounding context.

sh
export SOURCE_SRC="$SRC" CONTEXT_OUT="$CTX" ROUTER_PROMPT="$PROMPT" ROUTER_LOG="$LOG" HTML_OUT="$OUT" RUN_DIR

printf '[step] collect context\n' | tee -a "$STATUS"
python3 "$RUN_DIR/collect_context.py" 2>&1 | tee -a "$STATUS"
COLLECT_EXIT=${PIPESTATUS[0]}
if [ "$COLLECT_EXIT" -ne 0 ]; then

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README prominently describes provider dispatch to Codex, Gemini, Claude, Ollama, plus LangSmith tracing and token ledgers, but does not place an upfront warning that task content may be transmitted to third-party services or persisted in telemetry/logs. Users may invoke the skill assuming local-only processing, causing inadvertent disclosure of sensitive prompts, outputs, metadata, or operational details.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · README.md (reported line 106)May include surrounding context.

Typical local install:

bash
mkdir -p ~/.openclaw/skills
cp -R . ~/.openclaw/skills/super-router

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The README recommends broad trigger phrases like 'use super-router' without clearly constraining when the skill should or should not be invoked. In an agent ecosystem, ambiguous activation language can cause accidental routing of sensitive or unrelated tasks into a skill that performs external model calls, retries, telemetry, and subprocess execution, increasing the chance of unintended data exposure or autonomous action.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill instructs the agent to invoke a local Python script, read environment configuration, access files, and potentially contact external model providers, but it declares no explicit tool scope or permission boundaries. That creates an avoidable over-privilege condition where consumers cannot easily see or constrain the skill's shell, file, env, and network access expectations.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · SKILL.md (reported line 75)May include surrounding context.

md
`dependency_reason` fields. Independent subtasks should use `depends_on: []`.
- The dependency judge verifies and corrects only dependency edges and reasons;
  it must keep the planner's existing subtask IDs and must not add, remove,
  merge, split, or rewrite subtasks.
- The dependency validator rejects duplicate IDs, unknown dependency IDs,
  self-dependencies, and cycles. If validation fails, the router falls back to a
  conservative serial dependency chain instead of crashing the graph.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger condition includes broad activation language such as asking for router analysis, which can cause the skill to auto-run shell execution in contexts where the user did not clearly consent to invoking this specific local workflow. Overly broad triggers increase the chance of unintended command execution and make prompt-based invocation easier to abuse.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
79% confidence
Finding

The skill explicitly contemplates creating and verifying persistent artifacts such as reports, JSON logs, database ingests, and generated files. Persistent outputs can retain sensitive task content, model outputs, or operational metadata beyond the active session, and the skill does not present retention limits, access controls, or sanitization guidance in the same section.

Content

Scanner excerpt · SKILL.md (reported line 191)May include surrounding context.

md
### Post-Completion Artifact Verification

For router runs that create or modify artifacts (reports, JSON logs, database ingests, generated files), do not trust the router final stdout alone. Before reporting success to the user:

1. Inspect the declared output artifacts directly (`stat`, read the JSON/Markdown headers, count expected sections/cards when relevant).
2. Compare the router final summary against the saved artifacts and machine-readable logs.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill documents optional LangSmith telemetry upload and prompt/output tracing, including potentially sensitive task content and model outputs, without a strong user-facing privacy warning or consent requirement. In a routing skill that may process proprietary prompts, logs, code, or investigation artifacts, silent or poorly disclosed external telemetry materially increases data exposure risk.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
71% confidence
Finding

The guidance explicitly instructs creation of a timestamped directory under ~/.openclaw/logs/ and storing run artifacts there, which introduces session persistence on disk. In an agent setting, persistent logs can retain source-tree metadata, provenance, and possibly sensitive project details beyond the immediate session, increasing data exposure if the host is shared or later compromised.

Content

Scanner excerpt · references/linux-mm-source-html-example.md (reported line 18)May include surrounding context.

md
Use the generic `source-html-background-wrapper` pattern, but for a very large source tree add a deterministic local source inventory stage before invoking the router:

1. Create a run directory under `~/.openclaw/logs/<topic>_<timestamp>/`.
2. Write `collect_context.py` that scans source files and emits `context.json` with:
   - source path, file count, total lines/bytes;
   - Makefile object samples and Kconfig option samples;

Static analysis

No suspicious patterns detected.