Back to skill

Security audit

Ai Model Router

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a disclosed model router, but its privacy mode can select a cloud-configured primary model and it can store message snippets locally without clear safeguards.

Review before installing. Use this only if you are comfortable with automatic routing decisions, verify that primary_model in ~/.model-router/models.json is truly local before entering secrets, and treat context tracking as local plaintext storage of message snippets. Prefer a pinned installer version and avoid running the install command with elevated privileges.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
skill/context.py:34
Finding

Conversation Content Is Persisted in Plaintext Without Restrictive File Permissions

Content
View full analysis

Vulnerability Details

File Location: skill/context.py:34-36, 65-73
Vulnerability Type: Plaintext storage of potentially sensitive conversation data
Risk Level: Medium

Vulnerable Code

python
def _save(self):
    """Save contexts to disk"""
    with open(self.contexts_file, "w") as f:
        json.dump(self.contexts, f, indent=2)
python
ctx["last_model"] = model_used
ctx["messages"].append({
    "role": role,
    "content": content[:200],  # Truncate for storage
    "model": model_used,
    "type": model_type,
    "time": datetime.now().isoformat(),
})
self._save()

Technical Analysis

The context manager records the first 200 characters of each conversation message and writes the resulting data to ~/.model-router/contexts.json as plaintext JSON. The implementation does not redact credentials or personal information before persistence, encrypt the stored content, request consent for retention, or explicitly enforce a restrictive file mode such as 0600.

Truncation does not provide meaningful protection because API keys, passwords, access tokens, email addresses, and other sensitive values commonly occur within the first 200 characters. Opening the file with mode "w" also preserves the permissions of an existing file, including permissions that may allow access by other local users.

Attack Path

  1. A caller creates a conversation and submits a message containing a credential, personal information, or proprietary content.
  2. The caller invokes RouterCore.record_message(), which forwards the message to ContextManager.add_message().
  3. add_message() stores the first 200 characters of the message without sensitive-data filtering.
  4. _save() serializes the conversation history to ~/.model-router/contexts.json in plaintext.
  5. A local process or user able to read that file obtains the retained conversation content.

Impact Assessment

Success ...[truncated 443 chars]

Remediation
View remediation

Remediation Suggestions

  • Disable conversation persistence by default and require explicit user consent before storing message content.
  • Run sensitive-data detection and redaction before adding messages to the persistent context.
  • Avoid storing raw message text unless it is necessary; prefer non-sensitive summaries or metadata.
  • Create the context file atomically with owner-only permissions (0600) and ensure the containing directory uses restrictive permissions such as 0700.
  • Validate and repair permissions when opening an existing contexts file.
  • Consider authenticated encryption when conversation content must persist.
  • Define and enforce retention limits, deletion controls, and maximum storage bounds.

T09 · Insecure Skill Coding Practices

Warning
Location
skill/router.py:225
Finding

Privacy Routing Can Select a Cloud Model Instead of a Local Model

Content
View full analysis

Vulnerability Details

File Location: skill/router.py:117-120, 225-237
Vulnerability Type: Fail-open privacy control caused by trusting the configured primary model
Risk Level: Medium

Vulnerable Code

python
if os.path.exists(self.config_path):
    with open(self.config_path) as f:
        config = json.load(f)
        self.primary_id = config.get("primary_model", {}).get("id")
        self.secondary_id = config.get("secondary_model", {}).get("id")
        self.models = [self._dict_to_model(m) for m in config.get("models", [])]
python
# Privacy check - always routes to primary (usually local)
has_privacy, privacy_detected = self.check_privacy(task)
if has_privacy:
    primary = self.get_model(self.primary_id)
    if primary:
        return RouteResult(
            model_id=primary.id,
            model_name=primary.name,
            model_type="primary",
            reason=f"privacy_detected:{len(privacy_detected)}_patterns",
            confidence=1.0,
            privacy_detected=privacy_detected,
        )

Technical Analysis

The privacy control assumes that the primary model is local, but this invariant is not enforced. Configuration data can designate any model as primary_model, including an entry whose type is "cloud". When sensitive content is detected, the router retrieves that configured model and returns it without verifying that it is local.

The result is marked as "primary" rather than using the model's actual local or cloud type, and the decision receives a confidence value of 1.0. A downstream model executor that trusts this result may therefore transmit sensitive prompt content to a remote provider while believing privacy protection has been applied.

The project itself only returns a routing decision and does not perform the network transmission. Exploitation occurs when the result is consumed by an integration that sends the task to ...[truncated 1121 chars]

Remediation
View remediation

Remediation Suggestions

  • When privacy is detected, select only a model whose trusted metadata explicitly identifies it as local.
  • Validate model configuration at load time and reject a cloud model where a privacy-safe local model is required.
  • Fail closed if no verified local model is available. Return an explicit error or refusal rather than silently selecting a cloud or generic primary model.
  • Report the selected model's actual type instead of labeling every configured primary model as "primary".
  • Separate routing roles from transport properties by tracking fields such as route_role and execution_location.
  • Add tests covering a cloud primary model, missing local models, malformed configuration, and privacy-pattern matches.
  • Clearly communicate to downstream callers whether remote transmission is permitted.

T08 · Insecure Dependencies

Note
Location
SKILL.md:16
Finding

Installation Instructions Execute an Unpinned Third-Party Package

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:16
Vulnerability Type: Mutable third-party dependency execution
Risk Level: Low

Vulnerable Code

bash
npx clawhub@latest install ai-model-router

Technical Analysis

The documented installation command directs npx to download and execute the mutable latest release of the clawhub npm package. No exact version, lockfile, integrity hash, or other reproducibility control is specified.

Consequently, the code executed by users following the instructions may differ from the code available when this project was audited. A compromised npm account, malicious future release, or upstream supply-chain incident could turn the installation command into an arbitrary-code execution path.

No evidence was found that the currently reviewed project retrieves or executes a remote payload itself. The risk is specifically introduced by the documented use of an unpinned executable dependency.

Attack Path

  1. An attacker compromises the upstream package, its publishing account, or a future release associated with the latest tag.
  2. A user follows the Quick Start instructions.
  3. npx resolves clawhub@latest to the attacker-controlled or compromised release.
  4. npm downloads the package and executes its command-line entry point.
  5. The malicious package executes with the filesystem, network, and account permissions of the user running the installation command.

Impact Assessment

Impact depends on the privileges of the user executing npx. Under a normal user account, a compromised dependency could read or modify user-accessible files, steal credentials, access the network, or establish user-level persistence. If the command is run with elevated privileges, the impact could extend to system-wide compromise. This audit does not establish that the current upstream package is malicious.

Remediation
View remediation

Remediation Suggestions

  • Replace the mutable @latest tag with an exact, reviewed package version.
  • Where supported, verify package integrity using a lockfile, checksum, signature, or trusted provenance information.
  • Review the selected package version and its transitive dependencies before recommending execution.
  • Advise users not to run the installer with elevated privileges.
  • Update the pinned version only after reviewing and testing the new release.
  • Prefer a reproducible installation mechanism that does not implicitly execute newly resolved package code.
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill description defines broad trigger conditions such as requests to switch models, configuration requests, sensitive-data mentions, and complex-task keywords. Overbroad activation can cause the skill to run in contexts the user did not explicitly intend, which may alter model-selection behavior or intercept sensitive workflows unexpectedly.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding

The install command uses npx clawhub@latest, which pulls and executes the newest published package without pinning a specific version. This creates a supply-chain risk: if the upstream package is compromised or a breaking/malicious release is published, users of the skill may execute unintended code during installation.

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 86)May include surrounding context.

Configuration (Optional)

Create ~/.model-router/models.json:

json
{

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code persists conversation context, including truncated message content and model metadata, to a local JSON file without any visible consent, disclosure, retention control, or access protection. Even though the storage is local and messages are truncated to 200 characters, sensitive prompts, secrets, or personal data may still be written to disk and later exposed to other local users, backups, logs, or compromise of the host.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The file states that mentions of API keys/passwords "trigger privacy mode" and that sensitive data detection "forces local" automatically. While this is a privacy-oriented behavior, it removes user choice in handling requests and imposes a fixed routing policy without documenting opt-in or override conditions in the description.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The class docstring states the detector 'Only reads from' Ollama config files and environment variables, implying its behavior is limited to those read-only sources. In reality, no environment-variable access occurs anywhere in the file, and the class also exposes a hardcoded cloud registry via get_cloud_registry, so the documentation does not accurately describe what the code does.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The phrase 'Detect all available models' suggests the function discovers actual availability. However, the method simply returns detected local models plus a predefined list of cloud models without checking whether those cloud models are actually available to the user or configured in the environment.

Content

No source excerpt is available for this finding.