Back to skill

Security audit

Prediction Stack Setup

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly purpose-aligned, but it handles trading and API credentials, persistent scheduled jobs, and includes unsafe troubleshooting steps that can expose secrets.

Install only if you are comfortable with ongoing market-scanning automation, iMessage delivery through BlueBubbles, and local storage of API credentials. Before running it, set ~/.openclaw to 0700 and ~/.openclaw/config.yaml plus private keys to 0600, avoid the troubleshooting steps that print keys or environment variables, review each cron job before adding it, and rotate any credential that has been displayed in a shared or logged terminal.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/validate_setup.py:92
Finding
Sensitive configuration file is created without restrictive permissions## Vulnerability Details **File Location**: `scripts/validate_setup.py:92-97` **Vulnerability Type**: Sensitive credential file created with permissions inherited from the process umask **Risk Level**: Medium ### Vulnerable Code ```python config_path = Path.home() / ".openclaw" / "config.yaml" if not config_path.exists(): # Generate template config on first run config_path.parent.mkdir(parents=True, exist_ok=True) config_path.write_text(_CONFIG_TEMPLATE) ``` ### Technical Analysis The validator creates `~/.openclaw/config.yaml` using `Path.write_text()` without explicitly assigning a restrictive file mode. The resulting permissions depend on the user's current umask. Under a permissive umask, the file may be readable by other local users. Although the initially generated template only contains placeholders, the instructions direct the user to place Anthropic and Polygon API keys, along with the Kalshi API key identifier and private-key path, into this file. The script does not subsequently verify or correct its permissions. This exceeds secure minimum-privilege handling for a credential-bearing configuration file because access is not explicitly limited to the owning user. ### Attack Path 1. A user runs `validate_setup.py` when `~/.openclaw/config.yaml` does not exist. 2. The script creates the configuration file with permissions derived from the current umask. 3. The user follows the setup instructions and inserts valid API credentials into the generated file. 4. Another local account or process with filesystem access reads the configuration file. 5. The exposed credentials are reused to make unauthorized API requests or incur usage charges. Exploitation requires local filesystem access or another process executing under an identity permitted to read the file. ### Impact Assessment An attacker may obtain: - The Anthropic API key, enabling unauthorized billable API requests. - The Polygon API key, enabling unauthorized use of the associ ...[truncated 359 chars]
Remediation
## Remediation Suggestions 1. Create the OpenClaw configuration directory with owner-only permissions: ```python config_path.parent.mkdir(parents=True, exist_ok=True, mode=0o700) os.chmod(config_path.parent, 0o700) ``` 2. Create the file atomically with mode `0600` rather than relying on the process umask: ```python fd = os.open( config_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600, ) with os.fdopen(fd, "w", encoding="utf-8") as config_file: config_file.write(_CONFIG_TEMPLATE) ``` 3. Check existing file permissions before loading credentials and warn or fail when group or world access is present: ```python mode = config_path.stat().st_mode & 0o777 if mode & 0o077: raise PermissionError( f"{config_path} has insecure permissions {oct(mode)}; use chmod 600" ) ``` 4. Prefer environment variables, an operating-system credential store, or a dedicated secrets manager for API secrets. 5. Document that `~/.openclaw/config.yaml` must use mode `0600` and `~/.openclaw` should use mode `0700`.

T09 · Insecure Skill Coding Practices

Warning
Location
references/validation-troubleshooting.md:48
Finding
Troubleshooting instructions disclose API and private-key material in terminal output## Vulnerability Details **File Location**: `references/validation-troubleshooting.md:48-87` **Vulnerability Type**: Sensitive information exposure through terminal output **Risk Level**: Medium ### Vulnerable Instructions ```markdown #### Error: "File not found: Check private_key_file path in config" **Diagnosis:** The path to your private key doesn't exist. **Fix steps:** 1. Check file exists: `ls -la ~/.openclaw/keys/kalshi-secret.pem` 2. If not found, download it from https://kalshi.com/settings/api again 3. Paste into file: ```bash nano ~/.openclaw/keys/kalshi-secret.pem # Paste your private key (should start with -----BEGIN PRIVATE KEY-----) # Press Ctrl+O, Enter, Ctrl+X to save ``` 4. Verify: `cat ~/.openclaw/keys/kalshi-secret.pem | head -5` 5. Check permissions: `chmod 600 ~/.openclaw/keys/kalshi-secret.pem` 6. Rerun: `python validate_setup.py --kalshi-only` --- ### Anthropic (Claude) API Validation Fails #### Error: "Invalid or expired API key" **Diagnosis:** Your API key is invalid, expired, or incorrectly formatted. **Fix steps:** 1. Go to https://console.anthropic.com/account/keys 2. Check if you have any active keys (not revoked or expired) 3. If not, click "Create New Key" 4. Copy the full key (looks like `sk-ant-...`) 5. Update `~/.openclaw/config.yaml`: ```yaml anthropic: api_key: "sk-ant-YOUR_NEW_KEY_HERE" ``` OR set as environment variable: ```bash export ANTHROPIC_API_KEY="sk-ant-YOUR_NEW_KEY_HERE" ``` 6. Verify: `echo $ANTHROPIC_API_KEY` (should show your key, not empty) 7. Rerun: `python validate_setup.py --verbose` ``` ### Technical Analysis The troubleshooting guide recommends printing sensitive credential material directly to the terminal: - `cat ~/.openclaw/keys/kalshi-secret.pem | head -5` displays part of the private key. - `echo $ANTHROPIC_API_KEY` displays the complete Anthropic API key. Terminal output may be retained in scrollback, captured by session-recording or monitoring s ...[truncated 1894 chars]
Remediation
## Remediation Suggestions 1. Replace private-key content display with existence, ownership, permission, and non-empty checks: ```bash test -s ~/.openclaw/keys/kalshi-secret.pem \ && echo "Private key file exists and is non-empty" \ || echo "Private key file is missing or empty" stat -f "%Sp %Su" ~/.openclaw/keys/kalshi-secret.pem 2>/dev/null \ || stat -c "%A %U" ~/.openclaw/keys/kalshi-secret.pem ``` 2. Validate the private-key format without printing its contents, for example by parsing it with a trusted cryptographic tool and displaying only a public-key fingerprint. 3. Replace `echo $ANTHROPIC_API_KEY` with a non-disclosing presence check: ```bash if [ -n "${ANTHROPIC_API_KEY:-}" ]; then echo "ANTHROPIC_API_KEY is configured" else echo "ANTHROPIC_API_KEY is not configured" fi ``` 4. If format diagnostics are necessary, show only a short, explicitly masked prefix and the total length. Never display the complete value. 5. Add guidance warning users not to paste credential values, private-key contents, verbose authentication errors, or unredacted configuration files into support tickets, chat sessions, screenshots, or logs. 6. Recommend immediate revocation and rotation if a credential has been displayed in a recorded or shared terminal session.
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
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (27)

Tainted flow: 'api_key' from os.getenv (line 270, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
import requests

        # Test with a simple ticker query
        response = requests.get(
            "https://api.polygon.io/v1/marketstatus",
            params={"apikey": api_key},
            timeout=5
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description portrays a broad interactive installer/reconfiguration wizard with orchestration features, scheduling, messaging tests, and system integration. The supplied code instead performs API/config validation only. It creates a default config template if missing, reads configuration from ~/.openclaw/config.yaml, and makes network calls to validate Kalshi, Anthropic, Polygon, Ollama, and Polymarket availability. There is no evidence of interactivity beyond CLI flags, no cron setup, no heartbeat management, no iMessage testing, and no skill discovery or stack wiring. This is a material mismatch in primary purpose and capabilities.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README advertises that the skill will create scheduled jobs, route alerts through iMessage/BlueBubbles, and collect API keys, but it does not clearly warn users that it will modify local system scheduling, handle sensitive credentials, and send data to external messaging infrastructure. In a setup wizard context, users may proceed with elevated trust and grant access without understanding persistence, data exposure, or where secrets are stored, increasing the chance of unintended system changes or credential leakage.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill instructs use of capabilities that touch local files, credentials, environment configuration, and external network services, but it does not declare any tool scope or permission boundaries. In a setup skill that edits config, stores secrets, validates APIs, and schedules recurring jobs, the absence of explicit allowed-tools makes the blast radius unclear and increases the chance of over-privileged execution or unintended access to sensitive data.

Session Persistence

Medium
Category
Rogue Agent
Content
### Phase 2: API Key Configuration

Create or update `~/.openclaw/config.yaml`:

```yaml
# === REQUIRED ===
Confidence
90% confidence
Finding
The skill instructs users to persist long-lived credentials in `~/.openclaw/config.yaml` and a private key file, creating durable local access to external trading and messaging-related services. Persistent secrets are a common target because they survive sessions, may be copied into backups or synced storage, and can be reused by other local processes or future jobs created by the stack.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The skill directs users to place API credentials and private-key material into persistent local configuration and files without an explicit warning about secret handling risks, backup exposure, shell history leakage, multi-user systems, or repository sync. Because this setup also validates credentials against live services and wires recurring jobs, compromise of these stored secrets could enable account access, message abuse, or ongoing unauthorized automation.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
kalshi:
  enabled: true
  api_key_id: "YOUR_KALSHI_KEY_ID"                    # Settings → API Keys on kalshi.com
  private_key_file: "~/.openclaw/keys/kalshi-secret.pem"  # chmod 600 this file

anthropic:
  api_key: "YOUR_ANTHROPIC_API_KEY"                  # Required for the reference Kalshalyst path
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
3. If not, generate a new one
4. Download the private key file
5. Save it to `~/.openclaw/keys/kalshi-secret.pem`
6. Verify permissions: `chmod 600 ~/.openclaw/keys/kalshi-secret.pem`
7. Update `~/.openclaw/config.yaml`:
   ```yaml
   kalshi:
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
3. If not, generate a new one
4. Download the private key file
5. Save it to `~/.openclaw/keys/kalshi-secret.pem`
6. Verify permissions: `chmod 600 ~/.openclaw/keys/kalshi-secret.pem`
7. Update `~/.openclaw/config.yaml`:
   ```yaml
   kalshi:
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
3. If not, generate a new one
4. Download the private key file
5. Save it to `~/.openclaw/keys/kalshi-secret.pem`
6. Verify permissions: `chmod 600 ~/.openclaw/keys/kalshi-secret.pem`
7. Update `~/.openclaw/config.yaml`:
   ```yaml
   kalshi:
Confidence
80% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The guide instructs users to paste a private key into a local file and then verify it with `cat ... | head -5`, which unnecessarily displays sensitive key material in plaintext. Even partial private key exposure can leak into terminal logs, recordings, clipboard history, remote support sessions, or shell auditing systems, making this especially risky in an interactive setup workflow.

File System Enumeration

Medium
Category
Data Exfiltration
Content
**Diagnosis:** The path to your private key doesn't exist.

**Fix steps:**
1. Check file exists: `ls -la ~/.openclaw/keys/kalshi-secret.pem`
2. If not found, download it from https://kalshi.com/settings/api again
3. Paste into file:
   ```bash
Confidence
60% confidence
Finding
Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The troubleshooting guide tells users to place a live Anthropic API key directly into config files or export it into the shell, but it does not warn about secret handling risks such as shell history exposure, screen sharing, terminal logging, or committing config files to source control. In a setup skill focused on automation and scheduling, this increases the chance that long-lived credentials are stored insecurely and later exfiltrated.

Session Persistence

Medium
Category
Rogue Agent
Content
**Fix steps:**
1. Go to https://console.anthropic.com/account/keys
2. Check if you have any active keys (not revoked or expired)
3. If not, click "Create New Key"
4. Copy the full key (looks like `sk-ant-...`)
5. Update `~/.openclaw/config.yaml`:
   ```yaml
Confidence
88% confidence
Finding
The guide encourages creating and copying a long-lived API key into configuration or environment variables without warning about persistence risks. In a stack that also creates cron jobs and automation, persistent secrets may be inherited by processes, stored in dotfiles, backed up, or exposed through shell history and operational tooling.

External Transmission

Medium
Category
Data Exfiltration
Content
### Check network connectivity:
```bash
# Kalshi
curl -I https://api.kalshi.com/

# Anthropic
curl -I https://api.anthropic.com/
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
curl -I https://api.kalshi.com/

# Anthropic
curl -I https://api.anthropic.com/

# Polygon.io
curl -I https://api.polygon.io/
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Known Vulnerable Dependency: requests==2.32.5 — 2 advisory(ies): CVE-2026-25645 (Requests has Insecure Temp File Reuse in its extract_zipped_paths() utility func); CVE-2026-25645 (Requests is a HTTP library. Prior to version 2.33.0, the `requests.utils.extract)

Medium
Category
Supply Chain
Confidence
98% confidence
Finding
The dependency is pinned to requests==2.32.5, and the finding indicates this version is affected by a known vulnerability fixed in 2.33.0 involving insecure temporary file reuse in extract_zipped_paths(). Even if that utility is not obviously used from this requirements file alone, shipping a known-vulnerable pinned version in an automation-focused stack increases exposure because downstream code or transitive usage may invoke the affected functionality.

External Transmission

Medium
Category
Data Exfiltration
Content
except ImportError:
            from kalshi_python import Configuration, KalshiClient

        config_obj = Configuration(host="https://api.elections.kalshi.com/trade-api/v2")
        with open(key_path, 'r') as f:
            config_obj.private_key_pem = f.read()
        config_obj.api_key_id = key_id
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script performs real outbound validation calls using configured credentials and prompts without an explicit up-front warning or consent step, which can surprise users and cause unintended credential use, billing, or metadata disclosure. In a setup skill that may be run interactively, hidden network/probing behavior is more sensitive because users may expect passive validation rather than active API invocation.

External Transmission

Medium
Category
Data Exfiltration
Content
# Test with a simple ticker query
        response = requests.get(
            "https://api.polygon.io/v1/marketstatus",
            params={"apikey": api_key},
            timeout=5
        )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
# Test with a simple ticker query
        response = requests.get(
            "https://api.polygon.io/v1/marketstatus",
            params={"apikey": api_key},
            timeout=5
        )
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
import requests

        # Test 1: Check if Ollama server is running
        response = requests.get(
            "http://localhost:11434/api/tags",
            timeout=3
        )
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

External Transmission

Medium
Category
Data Exfiltration
Content
return result

        # Test 3: Try a simple inference
        response = requests.post(
            "http://localhost:11434/api/generate",
            json={
                "model": model_name,
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
return result

        # Test 3: Try a simple inference
        response = requests.post(
            "http://localhost:11434/api/generate",
            json={
                "model": model_name,
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
return result

        # Test 3: Try a simple inference
        response = requests.post(
            "http://localhost:11434/api/generate",
            json={
                "model": model_name,
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Static analysis

No suspicious patterns detected.