Back to skill

Security audit

OpenRouter Analytics

Security checks for vulnerabilities and agentic risk

Overview

The skill looks like a legitimate OpenRouter analytics helper, but it automatically reads broad local .env credential files and handles sensitive account/request data without enough scoping or warnings.

Install only if you are comfortable with the helper reading ~/.openclaw/.env and the current directory's .env automatically. Prefer running it from a controlled directory with least-privilege OpenRouter keys, avoid --raw unless needed, and treat CSV/report output as sensitive account data.

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 (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/openrouter_analytics.py:33
Finding

Overbroad Automatic Loading of Credential Files

Content
View full analysis

Vulnerability Details

File Location: scripts/openrouter_analytics.py:33-49, 503-506
Vulnerability Type: Overbroad credential-file access and environment pollution
Risk Level: Medium

Vulnerable Code

python
def load_dotenv(path: str) -> None:
    """Minimal .env loader (KEY=VALUE, ignores comments/blank lines)."""
    if not os.path.exists(path):
        return
    try:
        with open(path, "r", encoding="utf-8") as f:
            for line in f:
                s = line.strip()
                if not s or s.startswith("#") or "=" not in s:
                    continue
                k, v = s.split("=", 1)
                k = k.strip()
                v = v.strip().strip('"').strip("'")
                if k and k not in os.environ:
                    os.environ[k] = v
    except OSError:
        pass
python
def main() -> int:
    # Auto-load .env hints before parsing/executing commands
    load_dotenv(os.path.expanduser("~/.openclaw/.env"))
    load_dotenv(os.path.join(os.getcwd(), ".env"))

Technical Analysis

The Skill requires only OPENROUTER_MANAGEMENT_KEY and OPENROUTER_API_KEY, but its generic dotenv loader reads every entry from ~/.openclaw/.env and the current working directory’s .env. Every parsed variable that is not already set is copied into the process environment.

This exceeds the minimum credential access required for OpenRouter analytics. The files may contain unrelated database passwords, cloud credentials, access tokens, or other secrets. Those values become available to the entire Python process and any code subsequently imported or added to it.

Loading .env from os.getcwd() also makes credential selection dependent on the caller’s working directory. An unintended or attacker-controlled directory can therefore supply an OPENROUTER_MANAGEMENT_KEY or OPENROUTER_API_KEY when the corresponding variable is not already pres ...[truncated 1957 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove automatic loading of generic dotenv files where possible. Prefer credentials already supplied through the environment or an explicit user-selected credential file.
  2. If dotenv support is retained, parse and retain only OPENROUTER_MANAGEMENT_KEY and OPENROUTER_API_KEY; do not copy unrelated entries into os.environ.
  3. Avoid implicit current-working-directory discovery. Use an explicit --env-file option or a fixed, documented configuration path.
  4. Keep parsed credentials in narrowly scoped variables instead of mutating the global process environment.
  5. Validate explicitly selected credential files, including regular-file status and, where supported, ownership and restrictive permissions. Consider rejecting symlinks and group/world-readable files.
  6. Report which credential source was selected without ever printing the credential value.
  7. Update SKILL.md to describe the exact credential precedence and restricted variable allowlist.
  8. Add tests proving that unrelated dotenv entries are neither imported into os.environ nor included in outbound requests.

A safer loader should allowlist only the required variable:

python
ALLOWED_KEYS = {
    "OPENROUTER_MANAGEMENT_KEY",
    "OPENROUTER_API_KEY",
}

def load_openrouter_credentials(path: str) -> dict[str, str]:
    values: dict[str, str] = {}
    with open(path, "r", encoding="utf-8") as f:
        for line in f:
            s = line.strip()
            if not s or s.startswith("#") or "=" not in s:
                continue
            key, value = s.split("=", 1)
            key = key.strip()
            if key in ALLOWED_KEYS:
                values[key] = value.strip().strip('"').strip("'")
    return values
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (19)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/openrouter_analytics.py activity --limit 20

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/openrouter_analytics.py activity --limit 20

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/openrouter_analytics.py activity --limit 20

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/openrouter_analytics.py activity --limit 20

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/openrouter_analytics.py activity --limit 20

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/openrouter_analytics.py activity --limit 20

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/openrouter_analytics.py activity --limit 20

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
python3 scripts/openrouter_analytics.py activity --limit 20

Credential Access

High
Category
Privilege Escalation
Confidence
94% confidence
Finding

The documented behavior states that the tool will auto-load credential-bearing .env files, which is a form of local credential access. In the context of an analytics skill, automatically harvesting tokens from disk is more dangerous because the skill only needs an API key to function and should not broaden its authority by searching local secret stores.

Content

Scanner excerpt · scripts/openrouter_analytics.py (reported line 16)May include surrounding context.

python
- OPENROUTER_MANAGEMENT_KEY
- OPENROUTER_API_KEY

Also attempts to auto-load ~/.openclaw/.env if variables are not already set.
"""

from __future__ import annotations

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

The load_dotenv helper parses arbitrary local .env files and injects discovered key/value pairs into the process environment. That is credential acquisition behavior, and if reused or expanded it normalizes reading secrets from disk that the user did not explicitly authorize for this specific execution path.

Content

Scanner excerpt · scripts/openrouter_analytics.py (reported line 38)May include surrounding context.

python
def load_dotenv(path: str) -> None:
    """Minimal .env loader (KEY=VALUE, ignores comments/blank lines)."""
    if not os.path.exists(path):
        return
    try:

Credential Access

High
Category
Privilege Escalation
Confidence
98% confidence
Finding

The explicit call to load ~/.openclaw/.env causes the skill to access a user-home secret file automatically. Home-directory secret scraping is particularly sensitive because it reaches beyond the working project and may pull in privileged credentials unrelated to the immediate analytics task.

Content

Scanner excerpt · scripts/openrouter_analytics.py (reported line 504)May include surrounding context.

python
def main() -> int:
    # Auto-load .env hints before parsing/executing commands
    load_dotenv(os.path.expanduser("~/.openclaw/.env"))
    load_dotenv(os.path.join(os.getcwd(), ".env"))

Credential Access

High
Category
Privilege Escalation
Confidence
96% confidence
Finding

The automatic read of the current directory .env file accesses project-local secrets without an explicit user action. In practice, this can unintentionally ingest unrelated credentials from whatever directory the tool is run in, broadening secret exposure and surprising operators.

Content

Scanner excerpt · scripts/openrouter_analytics.py (reported line 505)May include surrounding context.

python
def main() -> int:
    # Auto-load .env hints before parsing/executing commands
    load_dotenv(os.path.expanduser("~/.openclaw/.env"))
    load_dotenv(os.path.join(os.getcwd(), ".env"))

    parser = build_parser()

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/openrouter_analytics.py (reported line 506)May include surrounding context.

python
def main() -> int:
    # Auto-load .env hints before parsing/executing commands
    load_dotenv(os.path.expanduser("~/.openclaw/.env"))
    load_dotenv(os.path.join(os.getcwd(), ".env"))

    parser = build_parser()
    args = parser.parse_args()

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill advertises operational use of environment variables, local files, network access, and CSV/report output, but it does not declare an explicit tool scope such as permissions or allowed-tools. That increases the chance an agent may invoke broader capabilities than the user expects, especially in a skill that handles management keys and usage data. The analytics context makes this more sensitive because the documented operations involve credentials and potentially billing or request metadata.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill encourages request-level troubleshooting and use of --raw to print full JSON, but it does not warn that these responses may contain sensitive operational data such as request IDs, model/provider routing details, token usage, cost metadata, prompts, or other user-linked information. In an analytics/troubleshooting skill, that omission is risky because operators may paste or store verbose output in logs, chats, or tickets without sanitization.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill states that it auto-loads ~/.openclaw/.env and the current-directory .env, which are common locations for API keys and other secrets, but provides no warning about secret exposure or unintended credential use. This is dangerous because an agent or operator could unknowingly load high-privilege credentials from the local environment, broadening access and increasing the blast radius of mistakes or misuse.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script advertises analytics/reporting functions, but it also proactively reads credential material from local .env files, including a home-directory path. That expands the skill's data-access scope beyond explicit user input and can cause credentials to be consumed from disk without the user's awareness, which is unnecessary and risky for a reporting helper.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The CSV export writes API-derived activity data to any path supplied by the user without confirmation, overwrite protection, or disclosure that sensitive usage metadata will be persisted locally. This can lead to accidental clobbering of files or unintended retention of potentially sensitive analytics data on disk.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

At startup the tool reads ~/.openclaw/.env and the current working directory .env before any command execution. This behavior silently pulls secrets from local storage into process environment state, creating credential-access capability outside the narrowly stated analytics purpose and increasing the chance of unintended secret use or exposure.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.