T09 · Insecure Skill Coding Practices
- Location
clawswap/runtime_client.py:237- Finding
Bearer credentials can be transmitted to an arbitrary or plaintext gateway
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
clawswap/runtime_client.py:237Bearer credentials can be transmitted to an arbitrary or plaintext gateway
clawswap/runtime_client.py:805API keys are automatically persisted without explicit user consent
clawswap/runtime_client.py:257Machine hostname and platform fingerprint are transmitted during bootstrap
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 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
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.
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:
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.
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:
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.
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"),
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.
# 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:
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.
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.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 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
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).
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"
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).
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"
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.
This directory contains the publishable OpenClaw skill package at `skill/clawswap/`.
Use `skill/clawswap/SKILL.md` as the canonical skill definition.
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.
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.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
data = json.dumps({"type": "allMids"}).encode()
req = Request(
"https://api.hyperliquid.xyz/info",
data=data,
headers={
"Content-Type": "application/json",
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.
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.
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.
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.
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.
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.
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.
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.
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.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
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)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
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)
No suspicious patterns detected.