T09 · Insecure Skill Coding Practices
- Location
scripts/jup_swap.py:318- Finding
Remote API-Supplied Transaction Is Signed Without Instruction Validation
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This Solana skill matches its stated wallet purpose, but it handles real mainnet funds with unsafe private-key storage and transaction-signing practices that require careful review.
Install only if you are comfortable using it with a dedicated low-value Solana wallet. Do not reuse a wallet holding important funds, avoid sourcing .env files, review every recipient, amount, mint, and network before running transaction commands, and prefer devnet or a hardware/managed wallet workflow where possible.
scripts/jup_swap.py:318Remote API-Supplied Transaction Is Signed Without Instruction Validation
scripts/initialize.py:25Wallet Private Key Is Persisted in a Plaintext File Without Enforced Access Controls
SKILL.md:42Setup Documentation Instructs Users to Execute the Environment File as Shell Code
requirements.txt:1Security-Critical Dependencies Are Unpinned and Lack Integrity Verification
The supplied code only covers a narrow subset of the declared functionality: Jupiter-based quoting and swapping, plus listing token metadata. It does not implement wallet creation, balance queries, SOL/token transfers, or Pump.fun token launches, all of which are explicitly claimed in the description. The primary purpose of this code chunk is swap operations via Jupiter Ultra API, using a private key and API key from environment variables to sign and execute transactions. That is consistent with part of the declaration ('swap via Jupiter') but not with the broader declared scope, so the description materially overstates what this code actually does.
The description presents a multi-capability Solana wallet skill, but the supplied code only handles Pump.fun token launching. It loads an existing private key from SOLANA_PRIVATE_KEY, uploads token assets/metadata to Pump.fun/IPFS endpoints, builds Pump.fun-specific create and optional buy instructions, and sends the transaction. There is no code for wallet creation, balance retrieval, SOL/token transfers, or Jupiter swaps. The Pump.fun launch portion matches part of the description, but the overall declared purpose materially overstates the implemented functionality, so this is a mismatch.
The code chunk is specifically a Solana wallet utility script for address lookup, SOL balance, token balance, SOL transfer, and SPL token transfer. Those parts align with part of the description. However, the declared purpose materially overstates the skill by including wallet creation, token swaps via Jupiter, and token launching on Pump.fun, none of which appear in this code. The trigger list also includes swap/jupiter/pumpfun terms that are unsupported by the supplied implementation. This is a description-behavior mismatch because significant declared capabilities are absent from the actual code.
The documentation instructs users to extract a private key from .env into an environment variable via shell command. While environment variables are common, this pattern normalizes direct handling of raw private keys in plaintext shell workflows, increasing exposure through shell history, process inspection, accidental logging, and downstream tool leakage.
IMPORTANT: After running initialize.py, export the private key to your environment:
export SOLANA_PRIVATE_KEY=$(grep SOLANA_PRIVATE_KEY .env | cut -d '=' -f2)
Or source the .env file:
Telling users to source .env can load secrets into the full shell session and any child processes, potentially exposing wallet private keys to unrelated commands, logs, debugging tools, or agent subprocesses. In a cryptocurrency skill, broad secret propagation is especially risky because compromise immediately enables fund theft.
export SOLANA_PRIVATE_KEY=$(grep SOLANA_PRIVATE_KEY .env | cut -d '=' -f2)
Or source the .env file:
```bash
source .env
The repeated .env secret-handling guidance reinforces unsafe credential practices for a blockchain private key. Because this secret authorizes irreversible on-chain actions, documentation that encourages casual plaintext handling materially increases the blast radius of any local compromise or accidental disclosure.
Or source the .env file:
source .env
The skill documents mainnet transactions, swaps, token transfers, and token launches without prominent warnings about irreversible loss, scams, slippage, or wrong-address risk. In blockchain contexts, users may treat examples as safe defaults, so missing safety framing substantially raises the chance of real financial harm.
The script is explicitly designed to generate a Solana wallet and persist the private key in a plaintext .env file. Storing cryptocurrency private keys in an unencrypted local file materially increases the chance of credential theft through local compromise, accidental inclusion in source control, backups, logs, or unsafe shell workflows.
#!/usr/bin/env python3
"""
Solana Wallet Initialization
Creates a new wallet and saves the private key to .env file
"""
import os
This function's purpose is to save a Solana private key into .env, which is not an appropriate storage mechanism for high-value cryptographic secrets. In a wallet-management skill, compromise of this file would directly allow theft of all assets controlled by the key.
def save_to_env(private_key_base58: str, env_path: Path = None):
"""Save private key to .env file."""
if env_path is None:
env_path = Path.cwd() / ".env"
Defaulting secret storage to Path.cwd() / '.env' makes the private key land in the current working directory, which may be a project folder, shared workspace, synced directory, or repository root. That increases the probability of accidental exposure via commits, artifact collection, or other tooling that reads local config files.
def save_to_env(private_key_base58: str, env_path: Path = None):
"""Save private key to .env file."""
if env_path is None:
env_path = Path.cwd() / ".env"
env_content = {}
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
env_content = {}
# Read existing .env if it exists
if env_path.exists():
with open(env_path, "r") as f:
for line in f:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
env_content = {}
# Read existing .env if it exists
if env_path.exists():
with open(env_path, "r") as f:
for line in f:
Writing SOLANA_PRIVATE_KEY directly to a .env file creates a plaintext credential exposure path for the wallet's signing authority. For a cryptocurrency wallet, exposure of this secret is equivalent to full account takeover and likely irreversible asset loss.
# Update or add SOLANA_PRIVATE_KEY
env_content["SOLANA_PRIVATE_KEY"] = private_key_base58
# Write back to .env
with open(env_path, "w") as f:
for key, value in env_content.items():
f.write(f"{key}={value}\n")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Check if wallet already exists
existing_key = os.environ.get("SOLANA_PRIVATE_KEY")
env_path = Path.cwd() / ".env"
if env_path.exists():
with open(env_path, "r") as f:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Check if wallet already exists
existing_key = os.environ.get("SOLANA_PRIVATE_KEY")
env_path = Path.cwd() / ".env"
if env_path.exists():
with open(env_path, "r") as f:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Check if wallet already exists
existing_key = os.environ.get("SOLANA_PRIVATE_KEY")
env_path = Path.cwd() / ".env"
if env_path.exists():
with open(env_path, "r") as f:
This line participates in persisting the freshly generated private key to .env, reinforcing insecure plaintext storage of the full 64-byte secret. In the context of a Solana wallet skill, that makes the issue more dangerous because the secret immediately controls real fund transfers and token operations.
private_key_bytes = bytes(keypair)
private_key_base58 = base58.b58encode(private_key_bytes).decode("utf-8")
# Save to .env
saved_path = save_to_env(private_key_base58, env_path)
print("✅ New wallet created!\n")
The instructions encourage exporting the private key from .env into the shell environment, which broadens secret exposure to shell history, process environments, crash dumps, and subprocess inheritance. While not as severe as the original plaintext storage, it increases the attack surface for credential leakage.
print()
print("To use the wallet, export the private key to your environment:")
print()
print(" export SOLANA_PRIVATE_KEY=$(grep SOLANA_PRIVATE_KEY .env | cut -d '=' -f2)")
print()
print("Or source the .env file:")
print()
Telling users to source the .env file loads the wallet private key directly into the shell environment, potentially exposing it to subprocesses and operational mistakes. In a wallet skill, normalizing this pattern can lead to widespread insecure handling of high-value signing credentials.
print()
print(" export SOLANA_PRIVATE_KEY=$(grep SOLANA_PRIVATE_KEY .env | cut -d '=' -f2)")
print()
print("Or source the .env file:")
print()
print(" source .env")
print()
The script instructs users to extract and export a base58 private key from a local .env file into an environment variable, normalizing direct plaintext handling of the wallet secret. In an agent or multi-process environment, environment variables and shell history are often exposed to subprocesses, logs, crash dumps, or other users, increasing the chance of wallet compromise and total asset theft.
print("Error: SOLANA_PRIVATE_KEY environment variable not set.")
print("Run initialize.py first to create a wallet, then export the key:")
print(
" export SOLANA_PRIVATE_KEY=$(grep SOLANA_PRIVATE_KEY .env | cut -d '=' -f2)")
sys.exit(1)
try:
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
data.extend(bytes(user)) # creator pubkey
data.append(1 if is_mayhem_mode else 0) # is_mayhem_mode bool
return Instruction(PUMP_PROGRAM_ID, bytes(data), accounts)
def create_ata_instruction(
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
data.extend(bytes(user)) # creator pubkey
data.append(1 if is_mayhem_mode else 0) # is_mayhem_mode bool
return Instruction(PUMP_PROGRAM_ID, bytes(data), accounts)
def create_ata_instruction(
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
data.extend(bytes(user)) # creator pubkey
data.append(1 if is_mayhem_mode else 0) # is_mayhem_mode bool
return Instruction(PUMP_PROGRAM_ID, bytes(data), accounts)
def create_ata_instruction(
The script constructs and broadcasts a live Solana mainnet transaction, including an optional dev buy that spends SOL, with no final confirmation prompt immediately before submission. Because the default network is mainnet and preflight is skipped, a user can unintentionally spend funds or launch a token irreversibly with minimal friction and reduced safety checks.
The skill declares access to sensitive capabilities through its documented behavior—environment secrets, file reads/writes, and networked blockchain actions—but does not constrain them with explicit tool scoping. In an agent setting, missing scope boundaries increases the chance of unintended secret exposure, arbitrary file modification, or unreviewed transaction/network actions beyond the user's intent.
No suspicious patterns detected.