Back to skill

Security audit

Rotate OpenRouter Key

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate key-rotation purpose, but its instructions and helper script can expose OpenRouter API keys through command lines, output, permissions, and plaintext backups.

Install only if you are comfortable letting an agent read and modify OpenClaw credential files. Use dry-run first, do not paste live keys into command lines or chat logs, redact any reported key values, review and remove old .bak files after confirming rollback is no longer needed, and check that ~/.openclaw config files remain owner-readable only. Treat gateway restart and any SSH-based remote update as separate user-approved maintenance actions.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/update-openrouter-key.py:156
Finding

API Key Exposed Through Command-Line Arguments

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/update-openrouter-key.py:84
Finding

Plaintext Backups Retain Historical API Keys Indefinitely

Content
View full analysis
str: ts = datetime.now().strftime("%Y%m%d_%H%M%S") backup = f"{filepath}.bak.{ts}" shutil.copy2(filepath, backup) return backup ``` Backups are created before both JSON and environment-file updates: ```python if changed and not dry_run: backup_file(filepath) ``` ### Technical Analysis Each credential rotation creates a timestamped plaintext copy of the complete configuration file. These copies contain the previous OpenRouter API key and potentially other sensitive configuration values. The implementation provides no: - Retention limit. - Automatic cleanup after successful rotation. - User confirmation or option to disable backups. - Explicit permission validation. - Encryption. - Inventory of created backup paths in the final output. `shutil.copy2()` generally preserves source metadata, which is preferable to creating an unrestricted copy, but the script does not verify that the source or resulting backup has an appropriately restrictive mode. Repeated rotations accumulate historical secrets under predictable filenames. Backups provide legitimate rollback value, but indefinite plaintext retention exceeds the minimum duration required for rollback and weakens the security objective of credential rotation. ### Attack Path 1. The user rotates an OpenRouter key using the helper script. 2. The script creates files such as `openclaw.json.bak.20260910_120000` or `.env.bak.20260910_120000`. 3. The old key remains stored in the backup after the live configuration is updated. 4. A local attacker, overly broad backup service, accidental archive, support bundle, or later account compromise o ...[truncated 570 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/update-openrouter-key.py:106
Finding

JSON Credential File Replacement Does Not Preserve Restrictive Permissions

Content
View full analysis
.tmp`) and is opened without exclusive-creation or symlink protections. The expected `~/.openclaw` directory should be private, which reduces exploitability, but unsafe directory permissions or an attacker with access to that directory could increase the risk. ### Attack Path 1. The user has an OpenClaw JSON configuration containing an API key and protected with restrictive permissions such as `0600`. 2. The script opens `.tmp` using ordinary write mode. 3. The operating-system umask allows the temporary file to be created with broader permissions, such as `0644`. 4. `os.replace()` makes that temporary file the live configuration without restoring the original mode. 5. Another local account reads the newly exposed JSON file and extracts the OpenRouter key. Where an untrusted party can write to the configuration directory, the predictable temporary filename may also permit interference with the update process, although ordinary OpenClaw deployments should not grant suc ...[truncated 395 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (23)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
| Priority | Source | Path |
|----------|--------|------|
| 1 (highest) | Environment / .env | `~/.openclaw/.env` |
| 2 | Agent config | `~/.openclaw/agents/<name>/agent/models.json` |
| 3 (lowest) | Global config | `~/.openclaw/openclaw.json` |

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/key-rotation-guide.md (reported line 195)May include surrounding context.

md
| Priority | Source | Path |
|----------|--------|------|
| 1 (highest) | Environment / .env | `~/.openclaw/.env` |
| 2 | Agent config | `~/.openclaw/agents/<name>/agent/models.json` |
| 3 (lowest) | Global config | `~/.openclaw/openclaw.json` |

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/key-rotation-guide.md (reported line 200)May include surrounding context.

md
| Priority | Source | Path |
|----------|--------|------|
| 1 (highest) | Environment / .env | `~/.openclaw/.env` |
| 2 | Agent config | `~/.openclaw/agents/<name>/agent/models.json` |
| 3 (lowest) | Global config | `~/.openclaw/openclaw.json` |

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/key-rotation-guide.md (reported line 204)May include surrounding context.

md
| Priority | Source | Path |
|----------|--------|------|
| 1 (highest) | Environment / .env | `~/.openclaw/.env` |
| 2 | Agent config | `~/.openclaw/agents/<name>/agent/models.json` |
| 3 (lowest) | Global config | `~/.openclaw/openclaw.json` |

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/key-rotation-guide.md (reported line 207)May include surrounding context.

md
| Priority | Source | Path |
|----------|--------|------|
| 1 (highest) | Environment / .env | `~/.openclaw/.env` |
| 2 | Agent config | `~/.openclaw/agents/<name>/agent/models.json` |
| 3 (lowest) | Global config | `~/.openclaw/openclaw.json` |

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/update-openrouter-key.py (reported line 61)May include surrounding context.

python
| Priority | Source | Path |
|----------|--------|------|
| 1 (highest) | Environment / .env | `~/.openclaw/.env` |
| 2 | Agent config | `~/.openclaw/agents/<name>/agent/models.json` |
| 3 (lowest) | Global config | `~/.openclaw/openclaw.json` |

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/update-openrouter-key.py (reported line 116)May include surrounding context.

python
| Priority | Source | Path |
|----------|--------|------|
| 1 (highest) | Environment / .env | `~/.openclaw/.env` |
| 2 | Agent config | `~/.openclaw/agents/<name>/agent/models.json` |
| 3 (lowest) | Global config | `~/.openclaw/openclaw.json` |

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code substantially matches the declared purpose of locating and updating OpenRouter API keys in an OpenClaw installation, including optional verification and safe update behavior via backups and dry-run support. However, the description says it 'restarts the gateway,' which is a concrete operational capability not implemented here. The script merely outputs 'Run: openclaw gateway restart' to stderr and never executes a restart command. That is a material description-to-behavior mismatch, though the rest of the description is accurate.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

bash
# Find every file containing an OpenRouter key
grep -r "sk-or-v1" ~/.openclaw/ --include="*.json" --include=".env" -l 2>/dev/null

# Also check for uncommented key in .env
grep -v '^#' ~/.openclaw/.env 2>/dev/null | grep OPENROUTER_API_KEY

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/key-rotation-guide.md (reported line 213)May include surrounding context.

bash
# Find every file containing an OpenRouter key
grep -r "sk-or-v1" ~/.openclaw/ --include="*.json" --include=".env" -l 2>/dev/null

# Also check for uncommented key in .env
grep -v '^#' ~/.openclaw/.env 2>/dev/null | grep OPENROUTER_API_KEY

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/update-openrouter-key.py (reported line 62)May include surrounding context.

python
```bash
# Find every file containing an OpenRouter key
grep -r "sk-or-v1" ~/.openclaw/ --include="*.json" --include=".env" -l 2>/dev/null

# Also check for uncommented key in .env
grep -v '^#' ~/.openclaw/.env 2>/dev/null | grep OPENROUTER_API_KEY

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

The command grep -v '^#' ~/.openclaw/.env | grep OPENROUTER_API_KEY will print the full environment line, likely exposing the active API key in logs or chat output. Because the skill explicitly says to report findings to the user before making changes, it creates a direct secret disclosure path during normal use.

Content

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

Find every file containing an OpenRouter key

grep -r "sk-or-v1" ~/.openclaw/ --include="*.json" --include=".env" -l 2>/dev/null

Also check for uncommented key in .env

grep -v '^#' ~/.openclaw/.env 2>/dev/null | grep OPENROUTER_API_KEY

text

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

The instruction to 'Report what you found to the user before making changes' follows discovery steps that can expose raw secret material from .env. In combination, the workflow encourages the agent/operator to disclose credential contents or exact secret-bearing lines, which is dangerous because API keys can be reused immediately if leaked.

Content

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

grep -r "sk-or-v1" ~/.openclaw/ --include="*.json" --include=".env" -l 2>/dev/null

Also check for uncommented key in .env

grep -v '^#' ~/.openclaw/.env 2>/dev/null | grep OPENROUTER_API_KEY

text

Report what you found to the user before making changes.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

If the user manages OpenClaw on other machines, repeat Steps 2-5 via SSH:

bash
ssh <host> "grep -r 'sk-or-v1' ~/.openclaw/ --include='*.json' --include='.env' -l"

Then run the update script remotely or copy it over.

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · references/key-rotation-guide.md (reported line 90)May include surrounding context.

bash
# Quick API test
curl -s https://openrouter.ai/api/v1/auth/key \
  -H "Authorization: Bearer sk-or-v1-YOUR-NEW-KEY" | python3 -m json.tool

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

The sed command embeds the full replacement API key directly in the command line while modifying .env, which can leak the secret via shell history, audit logs, and process inspection. In a credential-rotation context this is especially risky because the newly issued key is highly sensitive and often has broad access immediately after creation.

Content

Scanner excerpt · references/key-rotation-guide.md (reported line 201)May include surrounding context.

bash
# Check if .env has an active (uncommented) key
grep -v '^#' ~/.openclaw/.env 2>/dev/null | grep OPENROUTER

# If it shows a key, update it
sed -i 's|^OPENROUTER_API_KEY=.*|OPENROUTER_API_KEY=sk-or-v1-YOUR-NEW-KEY|' ~/.openclaw/.env

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This markdown file applies to SQP-2, and it describes file updates, service restart, and API verification that can affect user data, availability, and privacy. While the document explains what the skill does, it does not include an explicit warning or caution section alerting users that configs will be changed, backups created, the gateway restarted, and the key sent to OpenRouter for verification.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill instructs reading environment files, modifying config files, and verifying a key against an external API, which implies file read/write and network access, yet it declares no explicit tool scope or permissions. Missing scope boundaries increases the chance an agent executes sensitive operations without clear authorization controls or least-privilege limits.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
72% confidence
Finding

The skill specifies creating timestamped backups before writing secrets, which can leave old and new API keys persisted in additional files for an extended period. Although intended as a safety feature, retaining credential-bearing backups increases the attack surface if backup permissions, cleanup, or storage location are not controlled.

Content

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

md
The script:
- Finds all config files (`.env` + JSON) containing an openrouter key
- Creates timestamped backups before each write
- Updates only the key value (minimal change)
- Verifies the new key against the OpenRouter API
- Reports what it changed

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill directs a gateway restart without requiring an explicit warning about service interruption or obtaining confirmation immediately before the disruptive action. In operational environments, this can cause avoidable downtime or interrupt active workloads, especially if run automatically by an agent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The guide instructs users to place the new API key directly into shell commands and inline Python snippets, which can expose the credential through shell history, process listings, terminal scrollback, logs, and copied documentation. Although the purpose is administrative key rotation, the handling pattern is unsafe because it increases the chance of accidental credential disclosure during a sensitive operation.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest promises to "verify the new key works," which in context suggests end-to-end confirmation after rotation. The verify_key function only calls OpenRouter's auth endpoint to check that the credential is valid; it does not confirm OpenClaw is using the new key successfully after configuration changes.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest description says the skill "restarts the gateway," implying the rotation workflow includes actually performing that action. In the implementation, after updating files it only prints "Run: openclaw gateway restart" to stderr, so the advertised behavior is not carried out by code.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.