Back to skill

Security audit

Model Failover Guard

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to do what it claims, but it can automatically change OpenClaw model routing and restart the gateway, so it should be reviewed before use.

Install only from a reviewed immutable commit or release, confirm every configured fallback model/provider is acceptable for your data, set excludedProviders before running, and avoid daemon/systemd mode unless automatic model switching and gateway restarts are acceptable for your OpenClaw deployment.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T08 · Insecure Dependencies

Warning
Location
README.md:44
Finding

Unpinned Remote Skill Installation Creates a Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: README.md:44, README.md:157, and SKILL.md:21
Vulnerability Type: Unpinned third-party dependency installation
Risk Level: Medium

Vulnerable Code Snippets

README.md:44 and README.md:157:

bash
npx skills add https://github.com/BovmantH/openclaw-model-failover-guard.git --skill model-failover-guard

SKILL.md:21:

bash
npx skills add BovmantH/openclaw-model-failover-guard --skill model-failover-guard

Technical Analysis

The documented installation commands invoke npx and retrieve the Skill from a mutable third-party source without pinning it to an immutable commit hash or verified release artifact. No checksum, signature, lock file, or integrity-verification procedure is provided.

Consequently, the code installed when a user follows these instructions may differ from the code reviewed during this audit. Changes to the repository's default branch, compromise of the repository owner, compromise of the package used through npx, or transfer of the upstream project could introduce malicious code after review.

This is a supply-chain weakness rather than evidence that the currently audited scripts are malicious. The reviewed failover.py did not contain an embedded remote payload or intentionally malicious behavior.

Attack Path

  1. An attacker compromises the upstream repository, its maintainer account, or another component involved in the npx installation process.
  2. The attacker modifies the mutable repository branch or distributed package to include malicious Skill instructions or executable scripts.
  3. A user follows the documented unpinned installation command.
  4. npx retrieves the current upstream content rather than the specific content covered by this audit.
  5. The altered Skill is installed and may subsequently execute with the privileges of the user running OpenClaw or the Skill.
  6. Depending on the injected paylo ...[truncated 894 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin the Skill source to a reviewed, immutable Git commit hash rather than a mutable default branch.

  2. Pin the skills installer package to an exact trusted version instead of allowing npx to resolve a changing version.

  3. Publish versioned release archives and provide SHA-256 or stronger checksums.

  4. Sign releases and document how users must verify the signature before installation.

  5. Prefer an installation process equivalent to:

    bash
    npx skills@<exact-trusted-version> add \
      https://github.com/BovmantH/openclaw-model-failover-guard.git#<reviewed-commit-hash> \
      --skill model-failover-guard
    
  6. Add the reviewed commit identifier and expected integrity digest to both README.md and SKILL.md.

  7. Establish a release-review process so that each published version is audited and mapped to its immutable source commit.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (15)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

This markdown file states that the guard will switch to a fallback model automatically and 'Switch + Restart Gateway' as part of failover behavior, which can affect running sessions, availability, or system behavior. The description presents the automation as a feature but does not include a clear warning or caution about these potentially disruptive actions.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding

The README instructs users to install the skill directly from a GitHub URL via npx skills add without any pinned commit, tag, or integrity control. This creates a supply-chain risk because the referenced repository can change over time, causing future installations to pull unreviewed or malicious content.

Content

No source excerpt is available for this finding.

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

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

md
| Path | Purpose |
|---|---|
| `skills/model-failover-guard/SKILL.md` | Skill definition |
| `skills/model-failover-guard/config.example.json` | Config template |
| `skills/model-failover-guard/scripts/failover.py` | Runtime guard script |

Skill Enumeration

Medium
Category
Agent Snooping
Confidence
80% confidence
Finding

Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

Content

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

md
| Path | Purpose |
|---|---|
| `skills/model-failover-guard/SKILL.md` | Skill definition |
| `skills/model-failover-guard/config.example.json` | Config template |
| `skills/model-failover-guard/scripts/failover.py` | Runtime guard script |

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

Install service

bash
mkdir -p ~/.config/systemd/user
cp skills/model-failover-guard/skills/model-failover-guard/openclaw-model-failover.service ~/.config/systemd/user/
systemctl --user daemon-reload

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% 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 90)May include surrounding context.

Enable & Start

bash
systemctl --user enable --now openclaw-model-failover

Logs

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% 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 198)May include surrounding context.

Enable & Start

bash
systemctl --user enable --now openclaw-model-failover

Logs

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding

The Chinese installation section repeats the same unpinned npx skills add GitHub installation flow, so users are exposed to the same mutable-source supply-chain risk. A later repository compromise or force-push could silently change what gets installed.

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

安装服务

bash
mkdir -p ~/.config/systemd/user
cp skills/model-failover-guard/skills/model-failover-guard/openclaw-model-failover.service ~/.config/systemd/user/
systemctl --user daemon-reload

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill description explains automated failover and daemon mode but does not clearly warn that running it can automatically modify the active model configuration. In context, this is security-relevant because an always-running guard that switches among all configured models can redirect traffic, change trust boundaries, and affect data handling without the operator fully understanding that behavior.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding

The install instruction uses npx skills without pinning a specific version, so users may fetch and execute whatever version is current at install time. That creates a supply-chain risk: a compromised, typosquatted, or newly malicious package version could run arbitrary code during installation or skill management.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest description and operational instructions are written in Chinese, with no indication that other languages are supported or that the user can opt into this locale. Under the language/locale policy, a skill should not impose a specific language by default unless it is justified or optional.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The subprocess test sends the fixed message '只回复OK', which imposes a Chinese-language instruction in the skill's natural-language behavior. This can violate language/locale policy because the skill does not offer any user choice or document a justified locale constraint.

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 · skills/model-failover-guard/scripts/failover.py (reported line 132)May include surrounding context.

python
'--message', '只回复OK', '--json', '--timeout', str(R['TEST_TIMEOUT_SEC'])
    ]
    try:
        p = subprocess.run(cmd, capture_output=True, text=True, timeout=R['TEST_TIMEOUT_SEC'] + 20)
        out = (p.stdout or '') + '\n' + (p.stderr or '')
        ok = (p.returncode == 0) and ('OK' in out)
        if ok:

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 · skills/model-failover-guard/scripts/failover.py (reported line 147)May include surrounding context.

python
def apply_primary(cfg, model_id: str):
    set_primary_model(cfg, model_id)
    save_openclaw_config(cfg)
    subprocess.run(['openclaw', 'gateway', 'restart'], capture_output=True, text=True)


def try_switch_and_test(cfg, candidate_model):