Back to skill

Security audit

Solana Trading Api

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed Solana trading skill, but its reference client can sign and submit remote-built transactions with a wallet key without checking the transaction contents.

Install only if you are comfortable giving a local process signing authority over a dedicated, low-balance Solana trading wallet. Keep dry-run enabled until reviewed, avoid using a primary wallet, and add local transaction validation or human confirmation before live signing.

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

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:879
Finding
Server-Supplied Solana Transactions Are Signed Without Semantic Validation<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 879-887 and 1131-1157 **Vulnerability Type**: Blind signing of remotely supplied blockchain transactions **Risk Level**: High ### Vulnerable Code ```javascript function signVersionedTx(swapTxBase58) { const txBytes = bs58.decode(swapTxBase58); const tx = VersionedTransaction.deserialize(txBytes); tx.sign([getWallet()]); const signedBytes = tx.serialize(); return Buffer.from(signedBytes).toString('base64'); } ``` The function is invoked on the transaction supplied in an authenticated WebSocket fill: ```javascript async function handleOrderFilled(msg) { try { assertSchema(validateOrderFilled, msg, 'order_filled message'); } catch (e) { log({ step: 'order_fill_error', order_id: msg.order_id || 'unknown', error: e.message }); return; } const { order_id, order_type, triggered_mcap, filled_mcap, token_address } = msg; const swap_tx = msg.data?.swap_tx; log({ step: 'order_filled', order_id, order_type, token: token_address, triggered_mcap, filled_mcap }); if (msg.already_dispatched) { log({ step: 'order_fill_skipped', order_id, reason: 'already_dispatched' }); return; } // Verify server_signature before signing or submitting. const verified = await verifyOrderFilledSignature(msg); if (!verified) { log({ step: 'order_fill_skipped', order_id, reason: 'server_signature_verification_failed' }); return; } if (!swap_tx) { log({ step: 'order_fill_error', order_id, error: 'missing swap_tx' }); return; } // Staleness check (all fills; skip ratio when filled_mcap is 0 or null) if (filled_mcap != null && filled_mcap > 0 && triggered_mcap != null && triggered_mcap / filled_mcap < 0.85) { log({ step: 'order_fill_skipped', order_id, reason: 'stale', triggered_mcap, filled_mcap }); return; } const signedBase64 = signVersionedTx(swap_tx); const result = await submitTx(signedBase64, { token: token_address, action ...[truncated 3194 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Perform semantic transaction validation before signing** - Deserialize the transaction and resolve all static and address-lookup-table account keys. - Decode every instruction. - Reject unknown or unexpected program IDs. - Verify that the wallet is only a signer where explicitly required. - Verify the fee payer and impose a maximum transaction fee. 2. **Bind the transaction to the requested trade** - Confirm that the token mint matches the requested token. - Confirm that buy and sell directions match the original request or stored order. - Verify source and destination token accounts and their owners. - Enforce maximum input and minimum output amounts locally. - Check that slippage does not exceed the locally approved limit. - Confirm that SOL and token transfers remain within configured per-trade limits. 3. **Reject dangerous or unrelated instructions** - Reject unexpected system transfers, authority changes, token approvals, account closures, durable nonce operations, and arbitrary program invocations. - Maintain a narrowly scoped allowlist of reviewed swap programs and instruction types. - Reject transactions containing extra instructions that are unnecessary for the expected swap. 4. **Validate address lookup tables** - Resolve lookup tables through a trusted RPC endpoint. - Include the resolved accounts in policy checks. - Reject missing, changed, or unapproved lookup table entries. 5. **Add independent intent verification** - Persist the original order parameters locally. - Compare every fill transaction with the locally stored order rather than trusting message metadata alone. - Present a human-readable transaction summary and require confirmation when recipients, programs, amounts, or fees materially differ from expectations. 6. **Reduce wallet exposure** - Use a dedicated, low-balance trading wallet rather than a primary wallet. - Prefer a hardw ...[truncated 476 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (8)

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill enables irreversible on-chain swaps and order execution using a wallet identity, yet the top-level description lacks a prominent user-facing risk warning about live trading, slippage, loss of funds, and transaction finality. In an agent setting, this increases the chance that a user authorizes actions without understanding that trades are financial operations that cannot be undone.

External Transmission

Medium
Category
Data Exfiltration
Content
Return market cap (and optional price/pool) for given token addresses.

**Request:** `GET https://api.traderouter.ai/mcap?tokens=MINT1,MINT2` (comma-delimited Solana mint addresses).

**Response:** Object keyed by token address. Each value can include `marketCap`, `pair_address`, `pool_type`, `priceUsd`. Empty object if no tokens provided or none found.
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
Return market cap (and optional price/pool) for given token addresses.

**Request:** `GET https://api.traderouter.ai/mcap?tokens=MINT1,MINT2` (comma-delimited Solana mint addresses).

**Response:** Object keyed by token address. Each value can include `marketCap`, `pair_address`, `pool_type`, `priceUsd`. Empty object if no tokens provided or none found.
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
Return market cap (and optional price/pool) for given token addresses.

**Request:** `GET https://api.traderouter.ai/mcap?tokens=MINT1,MINT2` (comma-delimited Solana mint addresses).

**Response:** Object keyed by token address. Each value can include `marketCap`, `pair_address`, `pool_type`, `priceUsd`. Empty object if no tokens provided or none found.
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Description-Behavior Mismatch

Medium
Confidence
96% confidence
Finding
The manifest describes the skill as operating through the TradeRouter API, specifically citing REST /swap, /holdings, /protect and the TradeRouter WebSocket. However, the reference client includes a direct fallback path that submits transactions to an arbitrary Solana RPC endpoint with sendRawTransaction/confirmTransaction, which is a distinct execution channel outside the declared API surface.

Description-Behavior Mismatch

Low
Confidence
89% confidence
Finding
The manifest says the skill supports REST endpoints POST /swap, POST /holdings, POST /protect, and the WebSocket for limit orders, but the file also documents GET /mcap and GET /flex as supported use cases. This creates a description/behavior mismatch because the actual documented skill scope is broader than the manifest's enumerated interface.

Intent-Code Divergence

Low
Confidence
93% confidence
Finding
The main WebSocket documentation repeatedly states registration is challenge-response only and requires signing the server nonce with the wallet private key before authenticated use. But the definition-of-done TWAP checklist shortens this to 'connect → register', which contradicts the documented required authentication sequence for placing orders.

Intent-Code Divergence

Low
Confidence
95% confidence
Finding
The surrounding documentation explains that WebSocket registration succeeds only when the client signs the challenge nonce with the wallet private key. In preflight, an unauthenticated registration failure is reported as 'check server signature', which contradicts the actual mechanism and could mislead operators about what failed.

Static analysis

No suspicious patterns detected.