T09 · Insecure Skill Coding Practices
- Location
scripts/stats.py:27- Finding
Plaintext API Credential File Lacks Access-Control and Repository Protections
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This is a disclosed OpenRouter usage-reporting skill, with the main caution that its optional local credentials file can expose API keys if handled carelessly.
Install only if you are comfortable giving the skill OpenRouter account keys for usage reporting. Use environment variables or a managed secret mechanism where possible; if you use credentials.env, keep it out of source control and shared archives, restrict its permissions, and rotate keys if exposure is suspected.
scripts/stats.py:27Plaintext API Credential File Lacks Access-Control and Repository Protections
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Set environment variables (recommended) or create a credentials.env file:
export OPENROUTER_API_KEY=your_key_here
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Set environment variables (recommended) or create a credentials.env file:
export OPENROUTER_API_KEY=your_key_here
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Set environment variables (recommended) or create a credentials.env file:
export OPENROUTER_API_KEY=your_key_here
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
---
## How to run (recommended)
Set environment variables (recommended) or create a `credentials.env` file:
```bash
export OPENROUTER_API_KEY=your_key_here
The script makes outbound network requests to OpenRouter APIs, but network capability is reportedly not covered by declared permissions. Undeclared network access is a real security concern because any code with HTTP capability can exfiltrate data or access external systems outside reviewer expectations.
The script makes outbound network requests to OpenRouter APIs, but network capability is reportedly not covered by declared permissions. Undeclared network access is a real security concern because any code with HTTP capability can exfiltrate data or access external systems outside reviewer expectations.
The documentation advertises loading credentials from a local credentials.env fallback, signaling that the skill is designed to read secrets from a file within the skill directory. File-based secret loading increases exposure because credentials can be accidentally committed, copied, or read by other components with filesystem access.
Credentials are loaded from:
1. Environment variables (recommended for security)
2. credentials.env file (fallback)
Required:
OPENROUTER_API_KEY - for real-time totals and balance
The code constructs a path to credentials.env in the skill directory, explicitly enabling local secret-file access. In an agent-skill context, embedding secret lookup in the package directory is risky because it normalizes storing long-lived API keys alongside executable code and increases accidental disclosure risk.
# Credentials file in skill directory (same level as scripts/)
SCRIPT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
CRED_FILE = os.path.join(SCRIPT_DIR, "credentials.env")
def load_creds():
"""Load credentials from environment variables first, then fallback to credentials.env file."""
The function directly reads OPENROUTER_API_KEY and OPENROUTER_MGMT_KEY from process environment state. Accessing secrets is expected for this skill's purpose, but it remains a sensitive capability that can become dangerous if permissions are overbroad or if the skill later gains unintended output or exfiltration paths.
CRED_FILE = os.path.join(SCRIPT_DIR, "credentials.env")
def load_creds():
"""Load credentials from environment variables first, then fallback to credentials.env file."""
creds = {
"OPENROUTER_API_KEY": os.environ.get("OPENROUTER_API_KEY", ""),
"OPENROUTER_MGMT_KEY": os.environ.get("OPENROUTER_MGMT_KEY", "")
The fallback branch opens and parses credentials.env, meaning the running code will consume secrets from local disk if present. This materially increases the chance of secret leakage through source control mistakes, backups, artifact packaging, or broader filesystem access than intended.
"OPENROUTER_MGMT_KEY": os.environ.get("OPENROUTER_MGMT_KEY", "")
}
# If not in environment, try credentials.env file
if not creds["OPENROUTER_API_KEY"] or not creds["OPENROUTER_MGMT_KEY"]:
if os.path.exists(CRED_FILE):
with open(CRED_FILE) as f:
The docstring states OPENROUTER_API_KEY is required for both real-time totals and balance, implying balance is a defined guaranteed function. However, the implementation wraps balance retrieval in a broad exception handler and proceeds with a zero balance when the credits endpoint is inaccessible, which contradicts that stated behavior.
The manifest describes fetching real-time usage totals and historical per-model spend, but this code also calls a separate credits endpoint and computes account balance from total credits minus usage. Balance/account credits are adjacent to spend reporting, but they are not explicitly claimed in the manifest description.
No suspicious patterns detected.