Back to skill

Security audit

Solana Copy Trader

Security checks for vulnerabilities and agentic risk

Overview

This is a real Solana trading skill with mostly disclosed behavior, but its live-trading path handles wallet keys and transaction signing in ways that could put funds at risk.

Install only if you are comfortable reviewing and running a high-risk crypto trading bot. Use watch-only or paper mode by default, never use a primary wallet private key, fund only a dedicated low-balance burner wallet, and do not enable live mode until transaction validation, hard trade limits, and live exit controls are added and tested.

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
scripts/src/copy_trade.js:249
Finding
Remote Swap Transaction Is Signed Without Instruction Validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/src/copy_trade.js:249-264` **Vulnerability Type**: Signing of an untrusted remotely supplied blockchain transaction **Risk Level**: High ### Vulnerable Code ```js const { data: swapData } = await axios.post('https://quote-api.jup.ag/v6/swap', { quoteResponse: quote, userPublicKey: wallet.publicKey.toString(), wrapAndUnwrapSol: true, prioritizationFeeLamports: 10000, // ~0.00001 SOL priority fee }); // Deserialize + sign + send const swapTx = VersionedTransaction.deserialize( Buffer.from(swapData.swapTransaction, 'base64') ); swapTx.sign([wallet]); const sig = await connection.sendRawTransaction(swapTx.serialize(), { skipPreflight: false, maxRetries: 3, }); ``` ### Technical Analysis The live-trading path obtains an opaque serialized transaction from the remote Jupiter swap endpoint, deserializes it, and immediately signs it with the configured wallet. The application does not locally inspect or validate: - Program IDs and transaction instructions - Source and destination accounts - Input and output token mints - Input amount and minimum output amount - SOL or token transfer recipients - Fee payer and requested signers - Address lookup tables - Writable accounts - Unexpected token approvals, transfers, or account-closing operations The endpoint is consistent with the declared Jupiter integration, and there is no evidence that the current project intentionally submits a malicious transaction. However, the implementation gives the remote response authority to define what the wallet authorizes. TLS validation and Solana preflight do not confirm that a transaction matches the user's intended swap; they only protect transport and simulate whether the supplied transaction can execute. A compromised remote service, DNS/TLS trust path, proxy, or request dependency could return a valid but malicious transaction that transfers assets to an attacker-controlled account. ### Attack Path ...[truncated 973 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Decode and inspect every transaction before signing it. - Allowlist the expected Jupiter, System, Compute Budget, Associated Token Account, and SPL Token program IDs as narrowly as possible. - Verify the fee payer, required signers, address lookup tables, writable accounts, source accounts, destination accounts, token mints, maximum input amount, minimum output amount, and all transfer recipients. - Compare the decoded transaction against the accepted quote and reject any material discrepancy. - Reject unexpected instructions, additional transfers, approvals, account closures, or signer requests. - Enforce a local transaction policy independently of the remote API response. - Consider requiring explicit user confirmation before each live signature. - Continue using a separately funded burner wallet with only the amount required for intended trades. - Add automated tests using malicious serialized transactions to confirm that local validation fails closed. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/src/copy_trade.js:291
Finding
Documented Live-Trading Limits and Position Protections Are Not Enforced<![CDATA[ ## Vulnerability Details **File Location**: `scripts/src/copy_trade.js:97-104, 132-134, 218-236, 291-298`; `scripts/src/config.js:71-76`; `SKILL.md:82-87` **Vulnerability Type**: Missing financial limit validation and ineffective live-position controls **Risk Level**: High ### Vulnerable Code The copy-trading options accept caller-controlled values: ```js const { maxPositions = 3, solPerTrade = 0.01, takeProfitPct = 50, // sell at +50% stopLossPct = 20, // sell at -20% paper = true } = options; ``` The requested trade amount is converted directly to lamports without enforcing the configured maximum: ```js const lamports = Math.floor(solPerTrade * 1e9); ``` The live execution path submits the resulting amount: ```js } else { // REAL EXECUTION if (!wallet) { console.log('[CopyTrade] No wallet configured — paper only!'); continue; } await executeRealSwap(mint, TOKENS.SOL, lamports, quote); } ``` Caller options override all defaults: ```js const opts = { solPerTrade: 0.01, maxPositions: 3, takeProfitPct: 50, stopLossPct: 20, paper: true, ...options }; ``` A maximum value is loaded by the configuration module but is not applied to live copy trades: ```js config: { maxTradeSol: parseFloat(process.env.MAX_TRADE_SOL || '0.1'), minProfitPct: parseFloat(process.env.MIN_PROFIT_PCT || '0.5'), jitoTip: parseFloat(process.env.JITO_TIP || '0.001'), botToken: process.env.BOT_TOKEN, chatId: process.env.CHAT_ID, } ``` ### Technical Analysis The documentation presents `MAX_TRADE_SOL` as a safety limit, but `processWhaleTx()` never compares `solPerTrade` against `config.maxTradeSol`. A direct caller can therefore invoke the exported API with `paper: false` and an arbitrarily large finite trade amount. There is also no explicit validation that the value is finite, positive, affordable, or within a hard-coded upper bound. The advertised `takeProfitPct` and `stopLossPct` options are destructured but never ...[truncated 1672 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Validate every numeric option with `Number.isFinite()` and require positive values. - Enforce `solPerTrade <= config.maxTradeSol` when options are parsed, immediately before requesting a quote, and immediately before signing. - Reject invalid or unsafe configuration rather than silently clamping it. - Add a hard-coded absolute ceiling that cannot be increased through environment variables alone. - Verify the wallet's available balance while reserving transaction fees and rent. - Maintain authoritative live-position state based on confirmed on-chain balances and transaction signatures. - Implement live sell execution before exposing live mode. - Apply `takeProfitPct`, `stopLossPct`, and `maxPositions` to actual live positions. - Reconcile local state against the chain after every confirmed transaction and after restart. - Fail closed if position state, quote values, or safety configuration cannot be verified. - Remove the documented claims about automatic protections until they are implemented and tested. - Add unit and integration tests for oversized, negative, infinite, `NaN`, and insufficient-balance trade amounts. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/src/pumpfun.js:115
Finding
Pump.fun Safety Check Automatically Passes Any Retrievable Token<![CDATA[ ## Vulnerability Details **File Location**: `scripts/src/pumpfun.js:115-149` **Vulnerability Type**: Fail-open token safety validation **Risk Level**: Medium ### Vulnerable Code ```js async function pumpSafetyCheck(mint) { const token = await getPumpToken(mint); if (!token) return { score: 0, pass: false, reasons: ['Token metadata not found'] }; const reasons = []; let score = 50; // base score — token exists // Is it a pump.fun token? (ends in 'pump') if (token.isPump) { score += 10; reasons.push('Pump.fun token ✅'); } // Has name/symbol? if (token.name && token.name !== 'Unknown') score += 10; if (token.symbol && token.symbol !== '???') score += 10; // Already graduated to Raydium? if (token.complete) { score += 20; reasons.push('Graduated to Raydium ✅'); } return { score: Math.min(100, Math.max(0, score)), pass: score >= 40, token, reasons, }; } ``` ### Technical Analysis A token returned by `getPumpToken()` starts with a score of 50, while the passing threshold is only 40. Therefore, every token for which metadata can be retrieved passes automatically, even if it receives no positive evidence beyond existence. The additional score inputs are metadata characteristics rather than substantive security properties. The function does not verify: - Mint authority - Freeze authority - Liquidity depth or locked liquidity - Holder or creator concentration - Sellability - Token account restrictions - Creator history - Actual bonding-curve reserves - Market manipulation or honeypot behavior The result is consumed as a safety gate in the copy-trading logic. Although the current direct Pump.fun branch uses a placeholder paper-trading calculation rather than a completed live Pump.fun transaction, presenting this check as rug protection can create false confidence and unsafe future integration. ### Attack Path 1. An attacker or token creator deploys a high-risk token with retrievable H ...[truncated 902 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Use a fail-closed score starting at zero or require mandatory independent checks before setting `pass: true`. - Verify mint and freeze authorities directly from Solana account data. - Validate actual liquidity, reserves, sellability, holder concentration, creator holdings, and liquidity-lock status. - Treat token names, symbols, address suffixes, and metadata availability as informational only, not security evidence. - Require all critical checks to succeed; do not compensate for a failed critical check with unrelated score points. - Return separate `verified`, `unknown`, and `failed` results instead of reducing all checks to an easily bypassed aggregate score. - Rename the function to indicate metadata validation until genuine token-risk controls are implemented. - Add tests proving that retrievable but unsafe tokens fail the check. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (46)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description centers on copy trading Solana wallets, tracking whale transactions, Pump.fun monitoring, and simulated/live trade execution. The supplied code instead scans a fixed list of token mints for arbitrage opportunities using price-checking helpers, periodically repeats the scan, and reports profitable routes. It does not inspect wallet activity, copy trades, interact with Pump.fun in this chunk, execute trades, or simulate paper trades. While arbitrage scanning is mentioned in the description as one possible use case, the primary behavior of this code is materially different from the declared copy-trading-centric purpose, so this chunk is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a full whale copy-trading bot with wallet monitoring, real-time copy execution, paper/live trading modes, and Pump.fun support. The supplied code only implements market data and quote retrieval plus a basic arbitrage estimation helper and in-memory price tracker. While arbitrage scanning is mentioned in the description and is partially consistent with this module, the primary advertised capabilities of copy trading, wallet tracking, trade execution, and Pump.fun monitoring are absent from this code chunk. Therefore the description materially overstates and misrepresents what this specific code actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a full copy-trading/autonomous Solana bot with wallet monitoring, trade replication, Jupiter/Pump.fun execution, paper trading, and arbitrage scanning. The supplied code chunk is much narrower: it only provides helper functions for Pump.fun token discovery/metadata, trending/latest token retrieval, a constant-product bonding curve buy calculation, and a basic heuristic safety check. Monitoring pump.fun token launches is partially aligned with the description, but the primary declared purpose is not represented by this code. Therefore this is a material description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The description centers on whale wallet copy trading: tracking arbitrary wallets, copying their trades in real time, monitoring whale transactions, and possibly arbitrage. The supplied code does something materially different as its primary function: it is a launch-sniping bot for new Raydium/Pump.fun tokens. It subscribes to Solana program logs for Raydium pool initialization and Pump.fun mint events, identifies token mints, performs safety checks, requests Jupiter quotes, paper-buys, and later evaluates sell conditions using profit/stop-loss thresholds. That is not wallet-based copy trading. While both operate in the Solana trading domain and both mention Pump.fun and paper trading, the primary behavior is substantially different. Additionally, the code does not implement actual live execution here, only simulation plus a TODO for real swaps, and it does not scan for arbitrage. Therefore this is a clear description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The supplied code only retrieves and prints wallet trades, holdings, and token information from a Solana tracker data API. That aligns loosely with wallet/whale tracking, but it does not implement the core declared purpose of a copy trading bot. There is no code for placing trades, simulating trades, monitoring pump.fun launches, scanning arbitrage, or integrating with Jupiter or Pump.fun APIs. The actual behavior is a read-only tracking utility, which is materially narrower and different from the declared autonomous copy-trading functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
87% confidence
Finding
The code is clearly related to the declared domain of Solana whale tracking and copy trading, so it is not unrelated. It does accurately support wallet tracking, transaction monitoring, pattern analysis, and paper-trade simulation. However, the declared description presents a broader, more capable bot with real-time copying via Jupiter + Pump.fun APIs, live execution, arbitrage scanning, and pump.fun launch monitoring. This code chunk only covers the wallet-tracking/simulation portion and lacks the declared live execution and external API-based trading/monitoring capabilities. Therefore, the description materially overstates what this supplied code chunk actually does.

Credential Access

High
Category
Privilege Escalation
Content
```bash
cd solana-bot
npm install
cp .env.example .env  # fill in keys
node index.js copy    # paper mode (safe)
node index.js watch   # whale tracker only
node index.js scan    # arb scanner
Confidence
96% confidence
Finding
The quick-start instructions explicitly direct users to create a .env file and fill in keys, which includes a private key elsewhere in the document. In an agent skill context, encouraging secret provisioning without strict scoping, secret-management guidance, or strong warnings creates a real credential-handling risk and can lead to wallet compromise if the environment is exposed or misused.

Credential Access

High
Category
Privilege Escalation
Content
| `analyze` | `node index.js analyze` | Wallet pattern analysis |
| `safety` | `node index.js safety <mint>` | Token rug check |

## .env Setup

```env
PRIVATE_KEY=your_base58_private_key   # leave blank for watch-only
Confidence
99% confidence
Finding
The .env example requests a base58 Solana private key directly, creating a high-risk credential collection path for a financial automation skill. Because this skill concerns autonomous or semi-autonomous trading with real funds, exposure or misuse of this value would enable immediate unauthorized transactions and irreversible asset loss.

Missing User Warnings

High
Confidence
97% confidence
Finding
The documentation instructs users to configure a private key and describes live execution, but it does not present a prominent warning about irreversible financial loss, key compromise risk, and the difference between watch-only, paper, and live modes. In a crypto-trading context, this omission is dangerous because users may supply signing credentials or enable live trading without understanding the consequences.

Ae1

High
Category
analysis-evasion
Content
In `copy_trade.js` `startCopyTrader()`:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
3. node index.js analyze → Deep wallet analysis
4. node index.js paper   → Simulate copy trading

ADD YOUR WALLET to .env to enable:
→ Real execution (start with tiny amounts!)
→ Actual P&L tracking
═══════════════════════════════════════
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
3. node index.js analyze → Deep wallet analysis
4. node index.js paper   → Simulate copy trading

ADD YOUR WALLET to .env to enable:
→ Real execution (start with tiny amounts!)
→ Actual P&L tracking
═══════════════════════════════════════
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill advertises behavior that relies on sensitive environment variables, including a wallet private key, but does not declare any explicit tool scope or permissions boundary. In an agent ecosystem, missing scope declarations can cause the skill to be invoked without clear user understanding that secret material may be accessed, increasing the risk of unintended credential exposure or unsafe execution.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The skill uses broad activation language such as general trading, monitoring, and autonomous-bot requests, which can cause over-triggering in unrelated or only partially related financial conversations. In a trading skill that may access credentials or influence financial actions, overly broad routing increases the chance of unintended invocation and risky tool use.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The instructions tell users to export a Solana wallet private key from Phantom and store the raw secret in a `.env` file. Even though it recommends a burner wallet, this still normalizes handling plaintext private keys in a way that is easily leaked via source control, shell history, logs, backups, screenshots, or misconfigured deployments; in a live trading bot, disclosure of the key enables immediate theft of funds and unauthorized trades.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The header presents the script as educational and analysis-focused, but the file also exposes active copy-trading and sniping modes that can be adapted for live execution. This mismatch can mislead users and reviewers about the bot’s true capabilities, reducing informed consent and increasing the chance that risky trading automation is enabled without proper scrutiny.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
Wallet analysis results are sent to Telegram automatically, disclosing monitored wallet behavior and derived strategy data to an external service without an explicit notice or opt-in at the point of use. Even if the source wallet is public, aggregation and forwarding of behavior analytics can create privacy, operational security, or compliance concerns.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The scanner forwards arbitrage opportunities and run statistics to Telegram with no explicit disclosure in the user-facing flow. This leaks trading signals and operational telemetry to a third-party channel, which may expose strategy information or sensitive monitoring behavior.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
Observed whale wallet transactions are forwarded to Telegram in real time without a clear user warning. In a copy-trading context, this increases sensitivity because the bot is not merely observing public data but redistributing actionable transaction intelligence externally.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The 'copy' mode is labeled as paper mode, but the configuration and comment explicitly indicate it is intended to be switched to real trading by changing a flag. That discrepancy can cause unsafe assumptions during use or review, especially in a trading bot where flipping one option changes the impact from simulation to real fund movement.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script encourages users to add a wallet/private key to a .env file for real trading, but it does so without adjacent warnings about secret handling, key compromise, host security, or safer alternatives. In a live-trading bot context, that omission materially increases the risk of unsafe credential storage and accidental fund loss.

External Transmission

Medium
Category
Data Exfiltration
Content
async function sendTelegram(msg) {
  try {
    await axios.post(
      `https://api.telegram.org/bot${config.botToken}/sendMessage`,
      { chat_id: config.chatId, text: msg, parse_mode: 'HTML' },
      { timeout: 8000 }
    );
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
async function sendTelegram(msg) {
  try {
    await axios.post(
      `https://api.telegram.org/bot${config.botToken}/sendMessage`,
      { chat_id: config.chatId, text: msg, parse_mode: 'HTML' },
      { timeout: 8000 }
    );
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
92% confidence
Finding
The file’s instructional comments are written in Hindi/Hinglish, and the skill provides no indication that users can choose another language. This creates a locale/language policy issue because the skill content is effectively constrained to a specific language without documented opt-in or justification.

Natural-Language Policy Violations

Medium
Confidence
85% confidence
Finding
The multiline explanation string uses Hindi/Hinglish phrasing such as "quote lo" and "route test karo" and is printed directly to users. Because no alternative language option or opt-in is offered, this is a natural-language policy concern under the language/locale rule.

Static analysis

Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/src/pumpfun.js:16

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/src/config.js:33