Back to skill

Security audit

Clawswap

Security checks for vulnerabilities and agentic risk

Overview

This trading skill is coherent overall, but it stores and can send sensitive trading credentials in ways that need review before installation.

Install only if you are comfortable reviewing a trading agent that sends bearer credentials to a configurable gateway, stores API/runtime credentials in local plaintext files, and runs custom strategy Python with full local privileges. Use paper mode first, avoid untrusted strategy files, keep .env/.runtime_token/.clawswap_api_key out of version control, restrict file permissions, and do not set custom gateway variables unless you fully trust the destination.

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

Error
Location
clawswap/runtime_client.py:237
Finding
Bearer credentials can be transmitted to an arbitrary or plaintext gateway<![CDATA[ ## Vulnerability Details **File Location**: `clawswap/runtime_client.py`, lines 237-248 and 848-870 **Vulnerability Type**: Unrestricted credential transmission endpoint **Risk Level**: High ### Vulnerable Code ```python def http_json(method, url, data=None, token=None, timeout=15): """Send an HTTP request and return (status_code, response_body_dict | None).""" headers = { "Content-Type": "application/json", "User-Agent": "ClawSwap-RuntimeClient/1.0", } if token: headers["Authorization"] = f"Bearer {token}" body = json.dumps(data).encode() if data else None req = Request(url, data=body, headers=headers, method=method) try: with urlopen(req, timeout=timeout) as resp: return resp.status, json.load(resp) ``` ```python env_files = [ os.path.join(SKILL_DIR, ".env"), os.path.join(SKILL_DIR, "..", "..", "gateway", "tests", ".env.e2e"), ] for env_file in env_files: if os.path.exists(env_file): with open(env_file) as f: for line in f: line = line.strip() if line and not line.startswith("#") and "=" in line: k, _, v = line.partition("=") if v and k.strip() not in os.environ: os.environ[k.strip()] = v.strip() saved = load_saved_token() or {} api_key = args.api_key or os.environ.get("CLAWSWAP_API_KEY") or load_api_key() agent_id = args.agent_id or os.environ.get("CLAWSWAP_AGENT_ID") or saved.get("agent_id") gateway_url = args.gateway or os.environ.get("CLAWSWAP_GATEWAY_URL") or os.environ.get("GATEWAY_URL") or saved.get("gateway_url", "https://api.clawswap.trade") bootstrap_token = args.bootstrap_token or os.environ.get("CLAWSWAP_BOOTSTRAP_TOKEN") runtime_token = args.runtime_token or os.environ.get("CLAWSWAP_RUNTIME_TOKEN") or saved.get("runtime_token") ``` ### Technical Analysis The shared HTTP function places API keys, bootstrap tokens, and runtime tokens in ...[truncated 2041 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require `https://` for every non-loopback gateway. 2. Default to an exact allowlist containing `https://api.clawswap.trade`. 3. Reject embedded URL credentials, unexpected schemes, fragments, and unapproved ports. 4. Remove the generic `GATEWAY_URL` fallback and retain only the Skill-specific `CLAWSWAP_GATEWAY_URL`. 5. Require an explicit development option such as `--allow-custom-gateway` before sending credentials to a non-production origin. 6. Display the normalized credential destination and obtain confirmation before first use of a custom gateway. 7. Never permit plaintext HTTP except for explicitly approved loopback test addresses. 8. Bind saved credentials to the origin for which they were issued. Refuse to reuse a saved token if the configured origin changes. 9. Add tests confirming rejection of HTTP, malformed URLs, credential-bearing URLs, and unauthorized domains. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
clawswap/runtime_client.py:805
Finding
API keys are automatically persisted without explicit user consent<![CDATA[ ## Vulnerability Details **File Location**: `clawswap/runtime_client.py`, lines 805-824 and 867-875 **Vulnerability Type**: Unexpected persistent storage of sensitive credentials **Risk Level**: Medium ### Vulnerable Code ```python def load_api_key(): """Load API key from file (~/.clawswap_api_key or local .clawswap_api_key).""" try: if os.path.exists(API_KEY_FILE): with open(API_KEY_FILE) as f: key = f.read().strip() if key: return key except Exception: pass return None def save_api_key(api_key): """Persist API key to file for future use.""" try: with open(API_KEY_FILE, "w") as f: f.write(api_key) os.chmod(API_KEY_FILE, 0o600) log.info(f"API key saved to {API_KEY_FILE}") except Exception as e: log.warning(f"Could not save API key: {e}") ``` ```python api_key = args.api_key or os.environ.get("CLAWSWAP_API_KEY") or load_api_key() agent_id = args.agent_id or os.environ.get("CLAWSWAP_AGENT_ID") or saved.get("agent_id") gateway_url = args.gateway or os.environ.get("CLAWSWAP_GATEWAY_URL") or os.environ.get("GATEWAY_URL") or saved.get("gateway_url", "https://api.clawswap.trade") bootstrap_token = args.bootstrap_token or os.environ.get("CLAWSWAP_BOOTSTRAP_TOKEN") runtime_token = args.runtime_token or os.environ.get("CLAWSWAP_RUNTIME_TOKEN") or saved.get("runtime_token") # Persist API key if provided via CLI/env for future runs if api_key and not load_api_key(): save_api_key(api_key) ``` ### Technical Analysis An API key supplied through a command-line argument, environment variable, or `.env` file is automatically copied into `.clawswap_api_key` on the first run. No explicit persistence option or confirmation is required. The file is placed in the Skill directory. Although the code applies mode `0600`, this only restricts access according to local operating-system ownership. It does not protect th ...[truncated 1441 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Stop persisting API keys by default. 2. Introduce an explicit `--save-api-key` option and require informed user consent. 3. Clearly document the storage path, permissions, retention period, and deletion procedure. 4. Prefer an operating-system credential store or keychain over a plaintext file. 5. Add a command such as `--clear-api-key` that securely removes stored credentials. 6. Ensure `.clawswap_api_key`, `.runtime_token`, and `.env` are excluded from version control and release packages. 7. Create sensitive files atomically with restrictive permissions at creation time rather than applying `chmod` only after writing. 8. Warn users that command-line secrets may be visible in process listings and recommend environment variables or secure prompting. 9. Consider retaining only a narrowly scoped runtime token and requiring the API key again for control-plane operations. ]]>

other

Note
Location
clawswap/runtime_client.py:257
Finding
Machine hostname and platform fingerprint are transmitted during bootstrap<![CDATA[ ## Vulnerability Details **File Location**: `clawswap/runtime_client.py`, lines 257-260 and 369-385 **Vulnerability Type**: Undisclosed host fingerprint transmission **Risk Level**: Low ### Vulnerable Code ```python def get_host_fingerprint(): """Generate a simple host fingerprint for the bootstrap exchange.""" return f"{platform.node()}/{platform.system()}/{platform.machine()}" ``` ```python def bootstrap_exchange(self): """Exchange bootstrap token for a runtime token via POST /runtime/v1/bootstrap.""" if not self.bootstrap_token: log.error("No bootstrap token available") return False log.info(f"Exchanging bootstrap token ({self.bootstrap_token[:16]}...)") status, resp = http_json( "POST", f"{self.gateway_url}/runtime/v1/bootstrap", data={ "agent_id": self.agent_id, "runtime_version": RUNTIME_VERSION, "host_fingerprint": get_host_fingerprint(), }, token=self.bootstrap_token, ) ``` ### Technical Analysis During every bootstrap exchange, the client sends a string containing `platform.node()`, `platform.system()`, and `platform.machine()`. `platform.node()` commonly exposes the machine hostname, which may contain an employee name, internal asset identifier, environment name, or infrastructure naming convention. The Skill documentation describes bootstrap as an exchange of credentials for a runtime token, but does not disclose collection and transmission of host-identifying metadata. A stable hostname is not evidently required to submit paper-trading intents. The data also becomes more exposed when combined with the unrestricted gateway behavior, because it can be sent to a non-ClawSwap destination. ### Attack Path 1. The user starts a newly registered agent or reconnects using a bootstrap token. 2. `get_host_fingerprint()` reads the local hostname, operating-system name, and machine architecture. 3. The bootstrap request trans ...[truncated 828 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the hostname from the bootstrap payload unless it is strictly required. 2. If a stable installation identifier is needed, generate a random identifier that does not encode machine information. 3. Make fingerprint transmission opt-in and explain its purpose before collection. 4. Minimize the payload to the least information required by the runtime protocol. 5. Document all transmitted fields, their purpose, retention period, and receiving endpoint. 6. Permit users to disable host identification without losing core paper-trading functionality. 7. Apply the gateway-origin validation described in the credential-transmission finding. ]]>
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
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (30)

Credential Access

High
Category
Privilege Escalation
Content
```bash
# 1. Copy and edit config
cp .env.example .env
# Create your own API key at https://clawswap.trade/settings (click "Generate Key")
# Then paste it into CLAWSWAP_API_KEY
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Memory Manipulation

High
Category
Memory Poisoning
Content
Returns:
            True  — reconnect succeeded, caller should continue running.
            False — reconnect failed permanently (401); caller should clear state and exit.
            None  — reconnect failed transiently; caller should exit but preserve state.
        """
        if not self.api_key:
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
Returns:
            True  — reconnect succeeded, caller should continue running.
            False — reconnect failed permanently (401); caller should clear state and exit.
            None  — reconnect failed transiently; caller should exit but preserve state.
        """
        if not self.api_key:
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Credential Access

High
Category
Privilege Escalation
Content
help="Seconds between strategy ticks (default: 30)")
    args = parser.parse_args()

    # Load .env files: local .env (skill root) then gateway/tests/.env.e2e
    # Later files do NOT override earlier ones or real env vars.
    env_files = [
        os.path.join(SKILL_DIR, ".env"),
Confidence
84% confidence
Finding
The client automatically reads environment values from local .env files, including a sibling test path, and then treats those values as active runtime configuration. In a skill context, silently ingesting secrets from repository-adjacent files can unintentionally load sensitive credentials into the process and broaden the attack surface if untrusted workspace contents or test fixtures are present.

Credential Access

High
Category
Privilege Escalation
Content
# Load .env files: local .env (skill root) then gateway/tests/.env.e2e
    # Later files do NOT override earlier ones or real env vars.
    env_files = [
        os.path.join(SKILL_DIR, ".env"),
        os.path.join(SKILL_DIR, "..", "..", "gateway", "tests", ".env.e2e"),
    ]
    for env_file in env_files:
Confidence
86% confidence
Finding
Reading ../../gateway/tests/.env.e2e is especially risky because it reaches outside the skill directory into a test fixture location that commonly contains real or semi-real credentials. In a shared repo or agent-skill environment, that behavior can unintentionally harvest secrets unrelated to this runtime and activate them without the operator realizing it.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
This code writes untrusted strategy code to a temporary Python file and executes it with spec.loader.exec_module(mod), giving attacker-supplied code full interpreter privileges. In the skill context, this is especially dangerous because the executed code can access the filesystem, environment variables, network, imported modules, and any credentials or data available to the hosting agent process.

Missing User Warnings

High
Confidence
96% confidence
Finding
The runner is explicitly designed to execute arbitrary user code, but the API and CLI provide no explicit warning, confirmation, or trust boundary messaging before doing so. In an agent skill context, that increases the chance that operators or upstream components pass untrusted code into a highly privileged execution path, leading to accidental remote code execution exposure.

Credential Access

High
Category
Privilege Escalation
Content
# Remove non-release files from staged tree
find "$STAGE_DIR" -type d -name '__pycache__' -prune -exec rm -rf {} +
find "$STAGE_DIR" -type f -name '*.pyc' -delete
find "$STAGE_DIR" -type f \( -name '.runtime_token' -o -name '.clawswap_api_key' -o -name '.env' \) -delete
rm -rf "$STAGE_DIR/clawswap/tests"

# Avoid nesting package scripts inside shipped tools
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
find "$STAGE_DIR" -type d -name '__pycache__' -prune -exec rm -rf {} +
find "$STAGE_DIR" -type f -name '*.pyc' -delete
find "$STAGE_DIR" -type f \( -name '.runtime_token' -o -name '.clawswap_api_key' -o -name '.env' \) -delete
rm -rf "$STAGE_DIR/clawswap/tests"

# Avoid nesting package scripts inside shipped tools
rm -f "$STAGE_DIR/clawswap/tools/package_skill.sh" "$STAGE_DIR/clawswap/tools/prepublish_check.sh"
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
rm -rf "$STAGE_DIR/clawswap/tests"

# Avoid nesting package scripts inside shipped tools
rm -f "$STAGE_DIR/clawswap/tools/package_skill.sh" "$STAGE_DIR/clawswap/tools/prepublish_check.sh"

(
  cd "$STAGE_DIR"
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Skill Enumeration

Medium
Category
Agent Snooping
Content
This directory contains the publishable OpenClaw skill package at `skill/clawswap/`.

Use `skill/clawswap/SKILL.md` as the canonical skill definition.
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.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The quick-start path immediately instructs users to run a strategy that auto-registers an agent and submits trades using real-time market data, but it does not present a prominent risk warning first. In a trading context, users may underestimate that this can trigger autonomous order placement and financial loss, especially if they later switch from paper to live runtime or misconfigure the environment.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The documentation tells users to store an API key in .env and later notes that agent_id and runtime_token are persisted to .runtime_token, but it does not clearly warn that these are sensitive credentials. Users may accidentally commit these files, expose them in shell history, or leave them with unsafe permissions, enabling unauthorized trading activity or account misuse.

External Transmission

Medium
Category
Data Exfiltration
Content
try:
            data = json.dumps({"type": "allMids"}).encode()
            req = Request(
                "https://api.hyperliquid.xyz/info",
                data=data,
                headers={
                    "Content-Type": "application/json",
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
92% confidence
Finding
The runtime token is written to disk in plaintext for automatic reconnection without any explicit notice. A stolen runtime token could let another local user, compromised process, or leaked workspace impersonate the runtime client until the token is revoked or expires.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The client automatically persists the API key to disk when supplied via CLI or environment, without explicit user consent or warning. Even though the file permissions are restricted to 0600, local plaintext storage increases the chance of credential exposure through backups, workspace sharing, container image capture, or accidental publication of the skill directory.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill metadata explicitly advertises runtime trading, backtesting, and telemetry, but it does not present any prominent warning that the runtime mode may trigger real financial actions or transmit operational data. In a trading-agent context, omission of these disclosures can mislead users into running the skill without understanding financial risk, live-market interaction, or outbound data flows.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The docstring states the strategy uses EMA crossover on aggregated 15m bars and enters on 1m pullbacks, implying a multi-timeframe design. However, the configuration sets `agg_period` to 1 with an explicit comment saying 'no aggregation to match Rust', so the implemented logic operates on 1-minute bars rather than a separate higher timeframe.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The strategy's exit logic references self.cfg.trailing_stop_pct, but BollingerRsiConfig does not define that field. This causes a runtime failure when get_exit_signal() is called while in a position, which can disable exit handling and leave positions unmanaged during adverse price movement. In an automated trading context, broken risk controls are security-relevant because they can directly lead to uncontrolled financial loss.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
This code generates buy/sell signals and updates position state, which can drive financially impactful actions, but it contains no confirmation prompt, logging, print statement, or explanatory comment/docstring warning users about live trading behavior or risk. For a code file, safety-relevant operational behavior should include some visible disclosure unless the warning is provided elsewhere in the skill documentation.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The strategy advertises a trailing stop in its documentation, but the implementation later references self.cfg.trailing_stop_pct even though that field is not defined in BreakoutVolumeConfig. In a live trading context this can cause a runtime failure when exit logic is evaluated, disabling or preventing a critical risk-control path and potentially leaving positions unmanaged.

Missing User Warnings

Medium
Confidence
99% confidence
Finding
The exit path uses self.cfg.trailing_stop_pct for a safety-critical stop decision, but that configuration attribute does not exist, so calling get_exit_signal while in a position will raise an exception. In an automated trading system, this can break exit handling at the moment risk controls are needed most, causing unbounded holding time or larger-than-expected losses.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The docstring describes a classic breakout strategy: going long when price breaks above the recent high and short when it breaks below the recent low. However, `get_signal()` compares the current price to `window_low` for longs and `window_high` for shorts using a threshold percentage, which is a rebound/drop-from-extreme rule rather than a high/low breakout check.

External Transmission

Medium
Category
Data Exfiltration
Content
Download historical candle data from data.binance.vision.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Data source: https://data.binance.vision/data/futures/um/daily/klines/
- Free, no API key needed
- Daily ZIP files containing 1-minute CSV candle data
- USDT-M Futures (most liquid markets)
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
Download historical candle data from data.binance.vision.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Data source: https://data.binance.vision/data/futures/um/daily/klines/
- Free, no API key needed
- Daily ZIP files containing 1-minute CSV candle data
- USDT-M Futures (most liquid markets)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.