Back to skill

Security audit

Solana Easy Swap

Security checks for vulnerabilities and agentic risk

Overview

This Solana swap skill is mostly coherent with its stated purpose, but it can sign and broadcast real transactions from a local keypair while trusting opaque remote transaction bytes and mutable local prepared state.

Review before installing. This skill can move funds from the Solana keypair you configure, and it trusts Jupiter/RPC plus local prepared files at the point of signing. Use only a dedicated low-balance wallet, confirm every swap manually, avoid third-party destinations unless intended, and prefer a version that validates decoded transactions before signing and fixes dependency advisories.

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
scripts/swap.mjs:194
Finding
Unvalidated Remote Transaction Is Signed After User Confirmation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/swap.mjs`, lines 194–243, 260–275, and 295–336 **Vulnerability Type**: Signing of an untrusted, remotely constructed transaction without semantic validation **Risk Level**: High ### Vulnerable Code ```js // Jupiter swap (build tx) let swapTxBase64; try { const swapBody = { quoteResponse, userPublicKey: owner.toBase58(), dynamicComputeUnitLimit: true, prioritizationFeeLamports: { priorityLevelWithMaxLamports: { maxLamports, priorityLevel: 'high', }, }, }; if (destination) swapBody.destinationTokenAccount = destination.toBase58(); const res = await fetch(`${JUPITER_BASE}/swap`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(swapBody), }); if (!res.ok) { const body = await res.text(); if (res.status >= 500) fail('BACKEND_UNAVAILABLE', `Jupiter swap build error: ${res.status}`, true); fail('BACKEND_QUOTE_FAILED', `Jupiter swap build failed: ${res.status} ${body}`); } const swapData = await res.json(); swapTxBase64 = swapData.swapTransaction; if (!swapTxBase64) fail('BACKEND_QUOTE_FAILED', 'Jupiter returned no swap transaction'); } catch (e) { if (e.code) throw e; fail('BACKEND_UNAVAILABLE', `Jupiter swap build unreachable: ${e.message}`, true); } ``` ```js const prepared = { prepareId, txBase64: swapTxBase64, fromMint, toMint, amountIn, slippage, owner: owner.toBase58(), destination: destination ? destination.toBase58() : owner.toBase58(), expiresAt, executed: false, expectedOut: quoteResponse.outAmount || null, minOut: quoteResponse.otherAmountThreshold || null, priceImpact: quoteResponse.priceImpactPct || null, }; writeFileSync(prepareFilePath(prepareId), JSON.stringify(prepared)); ``` ```js // Sign const keypair = loadKeypair(); let signedTx; try { const txBuf = Buffer.from(prepared.txBase64, 'base64'); const tx = VersionedTransac ...[truncated 3950 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Decode and validate the transaction before signing** - Resolve versioned-message address lookup tables. - Inspect every instruction, account, signer, and writable account. - Reject malformed, unsupported, or unexpected instructions. 2. **Allowlist permitted programs and instruction types** - Permit only the expected Jupiter routing, Solana system, SPL Token, associated-token-account, and compute-budget programs. - Reject authority changes, delegate approvals, unrelated transfers, and account closures unless explicitly expected and confirmed. 3. **Bind execution to the confirmed swap** - Verify the wallet signer and source account. - Verify the input and output mints. - Enforce the confirmed maximum input and minimum output. - Verify the destination account against the confirmed destination. - Validate priority-fee and compute-budget limits. - Calculate a digest of the fully validated transaction and bind that digest to the confirmation record. - Recalculate and compare the digest immediately before signing. 4. **Protect prepared state** - Store prepared swaps in a dedicated owner-only directory. - Create directories with mode `0700` and files with mode `0600`. - Use atomic file creation and reject symlinks where supported. - Add authenticated integrity protection to detect modification between preparation and execution. - Remove expired or completed prepared-state files securely. 5. **Reduce wallet exposure** - Recommend a dedicated low-balance trading wallet rather than a primary wallet. - Document that Jupiter and the configured RPC endpoint are security-critical trust dependencies. - Consider locally constructing the transaction from validated route data where practical. 6. **Add adversarial tests** - Test transactions with altered destinations, excessive inputs, unexpected SOL transfers, delegate approvals, account closures, unknown programs, and excess ...[truncated 88 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (11)

Known Vulnerable Dependency: ws==8.19.0 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
94% confidence
Finding
The lockfile includes ws 8.19.0, which is reported as affected by memory disclosure and memory-exhaustion denial-of-service issues. Because this skill performs live Solana RPC/WebSocket interactions through transitive dependencies, a vulnerable WebSocket client/server library in a network-facing crypto-trading skill materially increases the risk of process instability or data leakage during remote interaction.

Known Vulnerable Dependency: ws==7.5.10 — 1 advisory(ies): CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
93% confidence
Finding
The lockfile also includes ws 7.5.10, which is separately flagged for memory-exhaustion denial of service. Having multiple vulnerable ws branches increases attack surface, and in a network-connected token-swapping skill that relies on RPC and websocket communications, remote service disruption could block trading operations or destabilize the agent process.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill can read a signing key from the environment/file system and perform networked transaction execution, but it declares no explicit tool scope or permission boundaries. In a skill that can move on-chain assets, missing scope declarations increases the risk of the agent invoking powerful capabilities without clear sandboxing or user/admin awareness, which can lead to unauthorized fund movement if the skill is triggered unexpectedly or misused.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The invocation description is very broad ('swap, trade, exchange, buy, or sell' various Solana tokens), which can cause the skill to activate on common financial language even when the user did not intend to initiate an on-chain swap. In this context, accidental activation is more dangerous than usual because the skill reads a keypair and can ultimately sign real blockchain transactions, so misrouting a conversation into this skill could expose users to unintended asset movement.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The skill loads private key material directly from SOLANA_KEYPAIR_PATH and uses it for signing, but this file offers no disclosure, scoping, or safeguards around secret handling. In an autonomous or chat-invoked skill, that increases the risk of users unknowingly granting hot-wallet signing authority to code paths that can move funds, especially when combined with the automatic execution flow.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The execute path deserializes a transaction produced by Jupiter, signs it with the local keypair, and immediately broadcasts it, but this file provides no independent transaction review or explicit user confirmation at execution time. In a chat-driven swapping skill, that is dangerous because an upstream prompt-injection, argument mix-up, stale prepared file, or maliciously crafted prepared transaction could cause irreversible on-chain asset movement without the user seeing the exact accounts, amounts, and destination before signing.

Known Vulnerable Dependency: bn.js==5.2.2 — 1 advisory(ies): CVE-2026-2739 (bn.js affected by an infinite loop)

Low
Category
Supply Chain
Confidence
84% confidence
Finding
The lockfile pins bn.js 5.2.2, which is flagged for an infinite-loop issue. In this skill, bn.js is only a transitive dependency of Solana libraries and there is no evidence in the lockfile alone of attacker-controlled parsing paths, so the practical risk is mainly denial of service rather than code execution.

Known Vulnerable Dependency: stream-json==1.9.1 — 1 advisory(ies): CVE-2026-71429 (stream-json: pick/ignore/filter/replace filters are O(depth²) on nested input — )

Low
Category
Supply Chain
Confidence
80% confidence
Finding
stream-json 1.9.1 is flagged for O(depth²) behavior on deeply nested input in certain filters, which can enable resource-consumption attacks. Here it is only a transitive dependency of jayson tooling and may not be exercised on attacker-controlled nested JSON in normal skill operation, so the issue appears real but of limited practical impact in this context.

Known Vulnerable Dependency: uuid==8.3.2 — 1 advisory(ies): CVE-2026-41907 (uuid: Missing buffer bounds check in v3/v5/v6 when buf is provided)

Low
Category
Supply Chain
Confidence
74% confidence
Finding
uuid 8.3.2 is flagged for a missing buffer bounds check in specific version-generation APIs when a caller supplies a buffer argument. This is a real dependency risk, but in this lockfile it is transitive and there is no indication the affected API pattern is used by the skill, making exploitation less likely and impact comparatively low.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"version": "1.0.0",
  "type": "module",
  "dependencies": {
    "@solana/web3.js": "^1.98.0",
    "bs58": "^6.0.0"
  }
}
Confidence
91% confidence
Finding
The dependency uses a caret range, which allows newer minor/patch releases to be installed without explicit review. In a wallet- and transaction-handling skill, a compromised or breaking upstream release could change signing, RPC, or serialization behavior and introduce supply-chain risk into sensitive token swap flows.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"type": "module",
  "dependencies": {
    "@solana/web3.js": "^1.98.0",
    "bs58": "^6.0.0"
  }
}
Confidence
89% confidence
Finding
The bs58 dependency is also specified with a caret range, permitting automatic adoption of future releases that have not been audited in this skill. Because bs58 is commonly involved in key/material encoding in Solana tooling, an unexpected upstream change or malicious publish could affect key handling or transaction construction in a high-trust financial context.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/swap.mjs:12