Back to skill

Security audit

Solana Funding Rate Arbitrage

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Solana auto-trading skill, but its live-trading safeguards are weak enough that users should review it carefully before installing.

Only install this if you are prepared to audit and modify it before any live use. Keep dry_run enabled in the config, do not rely on the trade:dry command alone, avoid storing a raw private key in a sourced shell file, do not add the cron job until the environment loading is hardened, and treat the live auto-trader as unsafe until it fails closed on bad market data and has complete two-leg execution with rollback.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/src/trading/drift-client.ts:101
Finding
Live Trading Can Use Fabricated Fallback Market Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/src/trading/drift-client.ts:101-151`; `scripts/src/trading/flash-client.ts:89-143`; `scripts/src/trading/auto-trader.ts:485-493` **Vulnerability Type**: Fail-open use of synthetic financial data during live trading **Risk Level**: High ### Vulnerable Code ```ts // scripts/src/trading/drift-client.ts:101-151 const response = await axios.get(`${DRIFT_API}/perpMarkets`, { timeout: 10000 }); // ... } catch (error: any) { logger.warn(`Drift API error, using backup data: ${error.message}`); return this.getBackupMarketData(); } /** * Backup market data when API fails */ private getBackupMarketData(): DriftMarketInfo[] { // Use CoinGecko API for fallback funding rates const mockMarkets = [ { symbol: 'SOL-PERP', index: 0, price: 185, rate: 0.0005 }, { symbol: 'BTC-PERP', index: 1, price: 98000, rate: 0.0002 }, { symbol: 'ETH-PERP', index: 2, price: 3250, rate: 0.0004 }, ]; return mockMarkets.map(m => ({ marketIndex: m.index, symbol: m.symbol, oraclePrice: m.price, markPrice: m.price, fundingRate: m.rate, fundingRateApy: m.rate * 24 * 365 * 100, openInterest: 10000000, volume24h: 50000000 })); } ``` ```ts // scripts/src/trading/flash-client.ts:89-143 try { const response = await axios.get( `${COINGECKO_API}/derivatives/exchanges/flash_trade`, { timeout: 10000 } ); if (!response.data?.tickers) { throw new Error('No ticker data'); } // Live response processing omitted } catch (error: any) { logger.warn(`Flash API error: ${error.message}`); return this.getBackupMarketData(); } private getBackupMarketData(): FlashMarketInfo[] { return [ { symbol: 'SOL-PERP', oraclePrice: 185, fundingRate: 0.0008, fundingRateApy: 700, openInterest: 5000000, volume24h: 20000000 }, { symbol: 'BTC-PERP', oraclePrice: 98000, fundingRate: 0.0003, fundingRateApy: 262, openInterest: 10000000, volume24h: 50000000 }, { symbol: 'ET ...[truncated 2712 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Fail closed whenever live market data cannot be obtained or validated. 2. Restrict hard-coded fallback data to an explicit simulation-only implementation that cannot construct live trading clients. 3. Add metadata to every quote: - Data source - Retrieval timestamp - Market timestamp - Simulation status - Freshness and validation status 4. Reject execution if either leg uses synthetic, stale, incomplete, or untrusted data. 5. Cross-check prices and funding rates against at least one independent source before live execution. 6. Apply maximum data-age limits and circuit breakers for anomalous rate or price changes. 7. Require all markets used in one arbitrage decision to be from a consistent observation window. 8. Emit a high-priority alert and suspend trading when a required upstream data source fails. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/src/trading/auto-trader.ts:92
Finding
Advertised Dry-Run Controls Do Not Override Live Configuration<![CDATA[ ## Vulnerability Details **File Location**: `scripts/package.json:23`; `scripts/dry-run-1h.sh:15`; `scripts/src/trading/auto-trader.ts:92-127` **Vulnerability Type**: Unsafe configuration precedence permitting unintended live execution **Risk Level**: High ### Vulnerable Code ```json // scripts/package.json:23 "trade:dry": "DRY_RUN=true ts-node --transpile-only src/trading/auto-trader.ts" ``` ```bash # scripts/dry-run-1h.sh:15 DRY_RUN=true npx ts-node --transpile-only src/trading/auto-trader.ts 2>&1 | tee -a $LOG_FILE ``` ```ts // scripts/src/trading/auto-trader.ts:92-127 private loadConfig(): TraderConfig { try { if (fs.existsSync(CONFIG_PATH)) { const data = fs.readFileSync(CONFIG_PATH, 'utf-8'); const loaded = JSON.parse(data); logger.info(`Config loaded from ${CONFIG_PATH}`); return loaded; } } catch (error: any) { logger.warn(`Config load error: ${error.message}`); } // Default config return { strategy: 'ultra_safe', max_position_pct: 50, min_spread: 0.5, max_dd_pct: 2, auto_execute: true, dry_run: true, leverage: 1, check_interval_hours: 4, min_apy_threshold: 100, max_position_usd: 100, notification: { telegram: true, on_open: true, on_close: true, on_funding: true }, risk: { max_positions: 2, stop_loss_pct: 2, take_profit_pct: null, auto_rebalance: true, rebalance_threshold: 0.3 } }; } ``` ### Technical Analysis The `trade:dry` command and dry-run shell script set `DRY_RUN=true`, giving users a clear expectation that trading is simulated. However, `loadConfig()` never reads `process.env.DRY_RUN`. When `~/.secrets/funding-arb-config.json` exists, its contents are returned directly. A persisted value of `"dry_run": false` therefore overrides the user's apparent selection of the dry-run command. The environment variable has no effect. This is particularly dangerous because wallet init ...[truncated 1236 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Parse `process.env.DRY_RUN` explicitly and make a value of `true` override every persisted setting. 2. Use strict Boolean parsing rather than relying on JavaScript truthiness. 3. Create a separate simulation-only entry point that cannot instantiate clients capable of live submission. 4. Require an explicit live-trading flag, such as `--live`, in addition to configuration. 5. Require interactive confirmation before live execution when running in a terminal. 6. Refuse live execution when invoked through a command named `trade:dry`. 7. Print the effective configuration source and final mode before wallet initialization. 8. Add automated tests covering precedence among defaults, configuration files, environment variables, and command-line flags. 9. Consider requiring a dedicated low-value wallet and configurable spending limits for live mode. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/src/trading/auto-trader.ts:299
Finding
Sequential Two-Leg Execution Can Leave an Unhedged Live Position<![CDATA[ ## Vulnerability Details **File Location**: `scripts/src/trading/auto-trader.ts:299-327`; `scripts/src/trading/flash-client.ts:187-205` **Vulnerability Type**: Non-atomic financial transaction with missing rollback **Risk Level**: High ### Vulnerable Code ```ts // scripts/src/trading/auto-trader.ts:299-327 const driftResult = await this.driftClient.openPosition( `${opp.symbol}-PERP`, opp.recommendation.driftSide, positionSize / 2, // Half on each exchange this.config.leverage ); if (!driftResult.success) { logger.error(`Drift position failed: ${driftResult.error}`); return false; } const flashResult = await this.flashClient.openPosition( `${opp.symbol}-PERP`, opp.recommendation.flashSide, positionSize / 2, this.config.leverage ); if (!flashResult.success) { logger.error(`Flash position failed: ${flashResult.error}`); // Should close Drift position here in production return false; } ``` ```ts // scripts/src/trading/flash-client.ts:187-205 if (!this.wallet) { return { success: false, error: 'Flash: Wallet not initialized' }; } // TODO: Full Flash Trade SDK integration // For now, return error for non-dry-run return { success: false, error: 'Flash Trade SDK integration pending - use DRY_RUN mode for testing' }; ``` ### Technical Analysis The arbitrage strategy depends on opening two opposing legs. The implementation opens the Drift leg first and only then attempts the Flash leg. If Flash fails, the function returns without closing Drift. This is not merely a transient edge case: the Flash client's live `openPosition()` implementation always returns an error because the SDK integration is pending. Consequently, any successful live Drift opening is followed by a guaranteed Flash failure under the reviewed implementation. The code also records positions only after both legs report success. Therefore, the successfully opened first leg may not be represented in the local position manager, making automated recovery and ...[truncated 1315 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable all live auto-trading until both exchange integrations support opening, querying, and closing positions. 2. Perform capability and health checks for both exchanges before submitting either leg. 3. Use atomic or bundled transaction execution where the protocols permit it. 4. If atomic execution is impossible: - Persist a pending transaction record before the first order. - Record the first leg immediately after confirmation. - Attempt the second leg within a strict time limit. - Automatically submit a reduce-only compensating close if the second leg fails. - Continue retrying or alert an operator if rollback fails. 5. Reconcile local state against on-chain and exchange state at every startup. 6. Use idempotency keys and explicit transaction states such as `pending_first_leg`, `pending_hedge`, `rollback_required`, and `reconciled`. 7. Add price-deviation and maximum-unhedged-duration circuit breakers. 8. Treat ambiguous timeouts as unknown outcomes and verify actual account state before retrying. 9. Correct the documentation so incomplete live integrations are not described as full trading support. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cron-runner.sh:4
Finding
Scheduled Runner Executes Arbitrary Shell Content from Shared Secrets File<![CDATA[ ## Vulnerability Details **File Location**: `scripts/cron-runner.sh:4-16`; `SKILL.md:90-108` **Vulnerability Type**: Executable secret configuration loaded by a recurring scheduled process **Risk Level**: Medium ### Vulnerable Code ```bash # scripts/cron-runner.sh:4-16 # Run via crontab: 0 */4 * * * /path/to/cron-runner.sh SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" LOG_DIR="$HOME/.clawd/funding-arb/logs" LOG_FILE="$LOG_DIR/cron-$(date +%Y%m%d).log" # Ensure log directory exists mkdir -p "$LOG_DIR" # Load environment if [ -f "$HOME/.secrets/.env" ]; then source "$HOME/.secrets/.env" fi ``` ```md <!-- SKILL.md:90-108 --> Create `.env` in scripts directory or `~/.secrets/.env`: ```env # Required for live trading SOLANA_PRIVATE_KEY=[1,2,3,...] # Or use wallet file SOLANA_WALLET_PATH=/path/to/wallet.json # Optional SOLANA_RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY DEBUG=true # Verbose logging ``` ## Cron Setup Run every 4 hours: ```bash # Add to crontab -e 0 */4 * * * ~/clawd/skills/solana-funding-arb/scripts/cron-runner.sh ``` ``` ### Technical Analysis Bash `source` does not parse a file as passive environment data. It executes the file as shell code in the current process. Consequently, `~/.secrets/.env` can contain command substitutions, function definitions, redirections, or arbitrary commands, all of which run before the trader. The documentation recommends placing a highly sensitive Solana private key in this shared file and running the loader through cron every four hours. The schedule itself is explicit, user-installed, and relevant to the declared automated-trading functionality; therefore, it is not a covert backdoor. However, combining recurring execution with an executable shared secrets file unnecessarily broadens the trust boundary and increases the consequences of file modification. The code does not verify ownership, permissions, file type, or symbolic-link status before sourcing the file. ### Attack Path ...[truncated 1295 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use `source` for dotenv-style configuration. 2. Load environment variables inside the TypeScript application with a strict dotenv parser that accepts only expected key/value syntax. 3. Use an application-specific file rather than a shared `~/.secrets/.env`. 4. Validate variables against an allowlist such as: - `SOLANA_RPC_URL` - `SOLANA_WALLET_PATH` - Explicit operational flags 5. Prefer a wallet path or operating-system secret manager over storing a raw private key in an environment file. 6. Require the configuration and wallet files to: - Be owned by the current user - Be regular files rather than symbolic links - Have restrictive permissions such as `0600` 7. Set a restrictive `umask`, for example `umask 077`, before creating logs or state. 8. Use absolute paths to trusted local executables rather than relying on cron's `PATH`. 9. Document how to inspect and remove the cron entry. 10. Consider a hardened service with explicit environment configuration, filesystem restrictions, and a dedicated low-privilege account if continuous operation is required. ]]>
Vulnerability Patterns
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (142)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
This finding suggests some chunks are only type definitions or utilities unrelated to the advertised strategy, reinforcing that the published description overstates cohesive functionality. While less directly dangerous than secret handling, it still contributes to misleading operators about what is production-ready.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/src/dashboard/server.ts:22

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/src/protocols/drift.ts:13

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/src/trading/drift-client.ts:74

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/src/trading/flash-client.ts:68