Back to skill

Security audit

Pump.fun Token Launcher

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it handles crypto wallet keys and can make real Solana mainnet transactions with weak safeguards that users should review carefully.

Review this before installing if you will use real funds. Prefer a fresh low-balance wallet, avoid putting valuable private keys in .env, use local image files instead of arbitrary URLs, run dry-run first, and require explicit confirmation before any live mainnet launch or initial buy.

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

T09 · Insecure Skill Coding Practices

Error
Location
launch.ts:116
Finding
Arbitrary Image URL Fetching Enables SSRF and Potential Data Disclosure<![CDATA[ ## Vulnerability Details **File Location**: `launch.ts:116-121`; related data flow at `launch.ts:194-201` and `launch.ts:214-234` **Vulnerability Type**: Server-Side Request Forgery (SSRF) and unbounded remote content retrieval **Risk Level**: High ### Vulnerable Code ```ts async function loadImage(imagePath: string): Promise<Blob> { if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) { const res = await fetch(imagePath); if (!res.ok) throw new Error(`Failed to fetch image: ${res.status}`); return await res.blob(); } const resolved = path.resolve(imagePath); if (!fs.existsSync(resolved)) throw new Error(`Image not found: ${resolved}`); const buffer = fs.readFileSync(resolved); const ext = path.extname(resolved).toLowerCase(); const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : "image/jpeg"; return new Blob([buffer], { type: mime }); } ``` The function is invoked in dry-run mode: ```ts if (dryRun === "true") { console.log("✅ Dry run complete — parameters validated."); console.log(" Remove --dry-run to launch for real."); // Still validate image loads try { const blob = await loadImage(image); console.log(` Image loaded: ${blob.size} bytes (${blob.type})`); } catch (e: any) { console.error(` ❌ Image error: ${e.message}`); } return; } ``` In live mode, the downloaded response is passed to the external SDK: ```ts // Load image console.log("Uploading metadata to IPFS..."); const imageBlob = await loadImage(image); // Create SDK and launch const sdk = new PumpFunSDK(provider); const mintKeypair = Keypair.generate(); console.log(`Mint address: ${mintKeypair.publicKey.toBase58()}`); console.log("Sending transaction..."); try { const result = await sdk.createAndBuy( wallet, mintKeypair, { name, symbol, description, file: imageBlob, }, BigInt(Math.floor(buyAmountSol * LAMPORTS_PER_SOL)), slippageBps, ...[truncated 3616 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer local image files or maintain an explicit allowlist of trusted HTTPS image hosts. 2. Reject plaintext HTTP and require HTTPS for all remote images. 3. Resolve the destination hostname before connecting and reject all loopback, private, link-local, multicast, unspecified, documentation, and reserved IPv4 and IPv6 ranges. 4. Protect against DNS rebinding by ensuring the address used for the connection is the validated address. 5. Disable automatic redirects or validate the scheme, hostname, and resolved address of every redirect target. 6. Set strict connection and total-request timeouts with `AbortController`. 7. Stream responses and enforce a conservative maximum image size before buffering the complete body. 8. Permit only expected image content types and validate image signatures rather than trusting the `Content-Type` header or file extension. 9. Do not pass remotely fetched content to the SDK until all validation has completed. 10. Consider changing dry-run mode so it performs only local validation, or require explicit authorization before it makes any remote request. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
launch.ts:37
Finding
Wallet File Uses Unauthenticated Encryption and May Be Protected by an Empty Password<![CDATA[ ## Vulnerability Details **File Location**: `launch.ts:37-58`; insecure automatic wallet creation path at `launch.ts:86-93` **Vulnerability Type**: Weak protection of locally stored private-key material **Risk Level**: Medium ### Vulnerable Code ```ts function encryptKey(privateKey: Uint8Array, password: string): string { const salt = crypto.randomBytes(16); const key = crypto.scryptSync(password, salt, 32); const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv("aes-256-cbc", key, iv); const encrypted = Buffer.concat([cipher.update(privateKey), cipher.final()]); return JSON.stringify({ salt: salt.toString("hex"), iv: iv.toString("hex"), data: encrypted.toString("hex"), }); } function decryptKey(encryptedJson: string, password: string): Uint8Array { const { salt, iv, data } = JSON.parse(encryptedJson); const key = crypto.scryptSync(password, Buffer.from(salt, "hex"), 32); const decipher = crypto.createDecipheriv("aes-256-cbc", key, Buffer.from(iv, "hex")); return new Uint8Array(Buffer.concat([decipher.update(Buffer.from(data, "hex")), decipher.final()])); } ``` The automatic wallet-generation path does not reject an empty password or require confirmation: ```ts // 3. Generate new wallet console.log("No wallet found. Generating a new one..."); const kp = Keypair.generate(); const password = await prompt("Set a password to encrypt your wallet: "); fs.writeFileSync(walletPath, encryptKey(kp.secretKey, password)); console.log(`Wallet saved to .wallet.key`); console.log(`Public key: ${kp.publicKey.toBase58()}`); console.log(`Fund this wallet with SOL before launching tokens.`); return kp; ``` ### Technical Analysis The wallet private key is encrypted with AES-256-CBC, but the file format does not include a message authentication code. CBC encryption provides confidentiality but does not provide integrity or authenticity. An attacker who can modify `.wallet.key` can alter the salt, initialization vec ...[truncated 2961 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace AES-256-CBC with an authenticated-encryption mode such as AES-256-GCM or ChaCha20-Poly1305. 2. Store a versioned wallet format containing the KDF parameters, salt, nonce, ciphertext, and authentication tag. 3. Bind format metadata as authenticated additional data so attackers cannot silently alter encryption parameters. 4. Reject empty passwords in every wallet-creation path and apply consistent minimum-strength requirements. 5. Require password confirmation during automatic wallet generation, as already done in `setupWallet()`. 6. Create the wallet file with owner-only permissions, for example mode `0600`, and ensure the containing directory is not accessible to untrusted users. 7. Write the encrypted wallet to a securely created temporary file and atomically rename it to prevent partial-file corruption. 8. Validate parsed fields and lengths before attempting decryption, and return distinct integrity-failure handling without exposing cryptographic details. 9. Encourage use of a hardware wallet or external signer for funded production wallets so the Skill does not retain exportable private keys. 10. Document secure backup and recovery procedures because authenticated encryption detects modification but cannot recover a damaged wallet file. ]]>
Vulnerability Patterns
  • 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
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (19)

Credential Access

High
Category
Privilege Escalation
Content
bun install

# Copy and configure environment
cp .env.example .env
# Add your Helius RPC URL (free at https://dev.helius.xyz)

# Generate a wallet
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
import * as crypto from "crypto";
import * as readline from "readline";

dotenv.config({ path: path.join(import.meta.dir, ".env") });

// ── Helpers ──────────────────────────────────────────────
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
import * as crypto from "crypto";
import * as readline from "readline";

dotenv.config({ path: path.join(import.meta.dir, ".env") });

// ── Helpers ──────────────────────────────────────────────
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
try {
      return Keypair.fromSecretKey(bs58.decode(process.env.WALLET_PRIVATE_KEY));
    } catch {
      throw new Error("Invalid WALLET_PRIVATE_KEY in .env");
    }
  }
Confidence
89% confidence
Finding
Accepting a raw private key from WALLET_PRIVATE_KEY in .env creates a high-risk secret-handling pattern because plaintext key material may be stored on disk, inherited by subprocesses, exposed in backups, or leaked through developer workflows. In an agent skill context that automates on-chain transactions, compromise of this secret directly enables theft of wallet funds and unauthorized token launches.

Session Persistence

Medium
Category
Rogue Agent
Content
## Environment

Create a `.env` file (see `.env.example`):

```
HELIUS_RPC_URL=https://mainnet.helius-rpc.com/?api-key=YOUR_KEY
Confidence
84% confidence
Finding
The README encourages persistent storage of `WALLET_PRIVATE_KEY` in `.env` and also references an encrypted `.wallet.key` file. Persistent local storage of signing credentials in an agent-operated skill materially increases the attack surface: compromise of the workspace, backups, logs, or adjacent tools can lead to theft of funds and unauthorized token launches.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The environment section instructs users to place a `WALLET_PRIVATE_KEY` in `.env` and mentions encrypted wallet storage, but it does not clearly warn that the agent skill may handle highly sensitive private key material. In an agent ecosystem, insufficient disclosure increases the chance that users provide wallet credentials through unsafe channels or allow the agent to persist secrets without understanding the risk of total wallet compromise.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The README states the agent will 'automatically detect the skill and can launch tokens on command,' while the metadata describes broad triggers such as create, launch, deploy, or mint a token. In an agent setting, this can cause unintended invocation of a skill that performs irreversible, real-money blockchain actions, especially if user intent is ambiguous.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill performs sensitive actions that require network access and may read secrets from the environment, but it does not declare any explicit tool scope or permission boundaries. In an agent setting, this can lead to overbroad execution authority, unclear operator expectations, and accidental use of network/env capabilities without policy enforcement, which is especially risky because the skill can spend real funds and handle private keys.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The dry-run path claims to only validate parameters, but it still performs network access when the image is a remote URL. This can leak IP/network metadata, contact untrusted infrastructure, and violate operator expectations that dry-run mode is non-invasive and side-effect-free.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The description advertises very broad autonomous capability ('launch tokens... from your AI agent' and 'one command') without clear trigger boundaries, confirmation requirements, or risk constraints. In a skill that can generate wallets, upload metadata, and submit on-chain transactions, vague invocation language raises the chance of accidental or socially engineered execution of irreversible financial actions.

Description-Behavior Mismatch

Low
Confidence
92% confidence
Finding
The manifest describes a skill for launching tokens with wallet generation, metadata upload, and creation flow as part of that command. This file also implements a separate token status inspection command and a standalone wallet setup mode, which are additional user-facing behaviors not reflected in the described scope.

Natural-Language Policy Violations

Low
Confidence
21% confidence
Finding
The file does not contain clear natural-language evidence of a language or locale policy violation. While the description is in English, it does not explicitly force users into a language or locale in a way that violates the stated policy criteria.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"launch": "bun run launch.ts"
  },
  "dependencies": {
    "@coral-xyz/anchor": "^0.30.1",
    "@solana/spl-token": "^0.4.9",
    "@solana/web3.js": "^1.95.8",
    "bs58": "^5.0.0",
Confidence
92% confidence
Finding
Using a caret range for @coral-xyz/anchor permits automatic installation of newer minor/patch releases that may change behavior or introduce supply-chain risk. Because this skill handles blockchain transactions and wallet-related operations, an unexpected dependency update could affect signing, transaction construction, or secret handling.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "@coral-xyz/anchor": "^0.30.1",
    "@solana/spl-token": "^0.4.9",
    "@solana/web3.js": "^1.95.8",
    "bs58": "^5.0.0",
    "dotenv": "^16.4.7",
Confidence
92% confidence
Finding
The unpinned @solana/spl-token dependency allows non-deterministic resolution to newer compatible releases. In a token-launching skill, supply-chain changes in token program helpers can alter minting or account initialization behavior and increase operational and security risk.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "@coral-xyz/anchor": "^0.30.1",
    "@solana/spl-token": "^0.4.9",
    "@solana/web3.js": "^1.95.8",
    "bs58": "^5.0.0",
    "dotenv": "^16.4.7",
    "pumpdotfun-sdk": "^1.4.2"
Confidence
97% confidence
Finding
@solana/web3.js is both security-sensitive and specifically flagged here with known advisories, yet the caret range means the actually installed version cannot be verified from the manifest alone. Since this library participates in key handling and transaction submission, a compromised or vulnerable resolved version could cause wallet compromise, malicious transaction behavior, or denial of service.

Unverifiable Dependency: @solana/web3.js has 3 known advisory(ies) (CVE-2024-30253 (Handling untrusted input can result in a crash, leading to loss of availability ); CVE-2024-54134 (Modified package published to npm, containing malware that exfiltrates private k); MAL-2024-11183 (Malicious code in @solana/web3.js (npm))), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
98% confidence
Finding
The manifest references @solana/web3.js without an exact version while known advisories include malware and private-key exfiltration risks. In a skill that may generate or use wallets and sign Solana transactions, inability to verify the resolved version materially increases the chance of shipping a vulnerable or malicious dependency into a high-value environment.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"@coral-xyz/anchor": "^0.30.1",
    "@solana/spl-token": "^0.4.9",
    "@solana/web3.js": "^1.95.8",
    "bs58": "^5.0.0",
    "dotenv": "^16.4.7",
    "pumpdotfun-sdk": "^1.4.2"
  }
Confidence
83% confidence
Finding
The bs58 dependency is used in contexts that commonly involve key encoding/decoding, so allowing version drift can affect correctness and security-sensitive data handling. In a crypto skill, even small dependency changes can break wallet import/export behavior or introduce malicious supply-chain code.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"@solana/spl-token": "^0.4.9",
    "@solana/web3.js": "^1.95.8",
    "bs58": "^5.0.0",
    "dotenv": "^16.4.7",
    "pumpdotfun-sdk": "^1.4.2"
  }
}
Confidence
80% confidence
Finding
dotenv is unpinned, which is a supply-chain hygiene issue even if the package is not directly responsible for on-chain logic. Because environment loading may include RPC endpoints, API keys, or wallet-related configuration, an unexpected update could still have security implications.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"@solana/web3.js": "^1.95.8",
    "bs58": "^5.0.0",
    "dotenv": "^16.4.7",
    "pumpdotfun-sdk": "^1.4.2"
  }
}
Confidence
95% confidence
Finding
pumpdotfun-sdk is the core third-party package enabling the token launch flow, and leaving it unpinned creates substantial supply-chain risk. In this context, a malicious or flawed update could directly alter wallet generation, metadata upload, purchase logic, or destination addresses for on-chain actions.