Back to skill

Security audit

Agent Wallet

Security checks for vulnerabilities and agentic risk

Overview

This wallet skill is mostly coherent with its purpose, but it handles crypto seed phrases and can send or bridge funds with too little containment for routine installation.

Install only for disposable or low-value test wallets unless the seed handling, Solana derivation, transaction confirmation, test scripts, and allowance behavior are fixed. Do not reuse an existing funded mnemonic, do not run the bundled test bridge scripts with a real seed, and review every transfer or bridge before allowing an agent to execute it.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/wallet.js:109
Finding
Wallet Mnemonic Exposed Through Agent-Visible Standard Output<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wallet.js:109-130` **Vulnerability Type**: Plaintext disclosure of wallet recovery credentials **Risk Level**: High ### Vulnerable Code ```javascript console.log(` ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 🔐 NEW WALLET GENERATED ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ⚠️ CRITICAL: Save this seed phrase securely! It will NOT be shown again. Anyone with this phrase can access your funds. Seed Phrase: ┌────────────────────────────────────────────────┐ │ ${mnemonic.split(' ').slice(0, 6).join(' ').padEnd(44)} │ │ ${mnemonic.split(' ').slice(6, 12).join(' ').padEnd(44)} │ └────────────────────────────────────────────────┘ Your Addresses: ├─ Solana: ${formatAddress(solanaAddr)} ├─ Base: ${formatAddress(evmAddr)} └─ Ethereum: ${formatAddress(evmAddr)} (same as Base) Full Addresses: ├─ Solana: ${solanaAddr} ├─ Base: ${evmAddr} └─ Ethereum: ${evmAddr} Add to .env: WALLET_SEED_PHRASE="${mnemonic}" ``` ### Technical Analysis The wallet creation command prints the complete BIP-39 mnemonic to standard output twice: once in the seed phrase box and again in the proposed `.env` assignment. In an AI Agent environment, process output is commonly captured in tool results, conversation history, execution traces, telemetry, terminal logs, or orchestration-system logs. Consequently, stdout is not an appropriate confidential channel for a wallet mnemonic. This behavior also conflicts with the security statement in `SKILL.md:150-152` that private keys are held in memory and the seed is never logged. Although displaying a newly generated mnemonic may be necessary during initial wallet creation, returning it through an Agent-visible execution channel is not the minimum-privilege method of delivering it. ### Attack Path 1. A user or Agent invokes `node scripts/wallet.js create`. 2. The command prints the complete mnemonic to stdout. 3. The Agent framework, terminal, CI syste ...[truncated 759 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not print the mnemonic to ordinary stdout or stderr in Agent-operated environments. - Deliver the mnemonic through an explicitly protected, out-of-band interface or a dedicated secret-management integration. - If local display is unavoidable, require an interactive terminal, refuse execution when stdout is redirected, and clearly warn that Agent transcripts may retain the value. - Store generated wallet material only after explicit user consent in a secret store with restrictive access controls. - Never include the mnemonic in proposed shell commands or `.env` assignment text. - Redact all mnemonic and private-key values from application logs, error handlers, telemetry, and tool responses. - Update `SKILL.md` so its security claims accurately describe the remaining exposure. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wallet.js:510
Finding
Raw Private Keys Delegated to Third-Party Bridge Adapters<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wallet.js:510-541` **Vulnerability Type**: Excessive secret exposure across a third-party dependency trust boundary **Risk Level**: Medium ### Vulnerable Code ```javascript // Derive keys const solanaKeypair = deriveSolanaKeypair(seedPhrase); const evmWallet = deriveEVMWallet(seedPhrase); // Get Solana private key as base58 const solanaPrivateKeyBase58 = bs58.encode(solanaKeypair.secretKey); // Get EVM private key (with 0x prefix) const evmPrivateKey = evmWallet.privateKey; // Create adapters for source chain if (fromKey === 'solana') { const solanaAdapterModule = await import('@circle-fin/adapter-solana-kit'); fromAdapter = solanaAdapterModule.createSolanaKitAdapterFromPrivateKey({ privateKey: solanaPrivateKeyBase58, }); } else { const viemAdapterModule = await import('@circle-fin/adapter-viem-v2'); fromAdapter = viemAdapterModule.createViemAdapterFromPrivateKey({ privateKey: evmPrivateKey, }); } // Create adapters for destination chain if (toKey === 'solana') { const solanaAdapterModule = await import('@circle-fin/adapter-solana-kit'); toAdapter = solanaAdapterModule.createSolanaKitAdapterFromPrivateKey({ privateKey: solanaPrivateKeyBase58, }); } else { const viemAdapterModule = await import('@circle-fin/adapter-viem-v2'); toAdapter = viemAdapterModule.createViemAdapterFromPrivateKey({ privateKey: evmPrivateKey, }); } ``` ### Technical Analysis The bridge implementation extracts complete private keys and supplies them directly to third-party adapter packages. This expands the trusted computing base from the project’s signing code to all adapter code and relevant transitive dependencies. The reviewed project does not contain evidence proving that the installed adapters transmit the keys over the network. Therefore, this finding is a trust-boundary and least-exposure issue rather than a confirmed instance of key exfiltration. However, the application i ...[truncated 1377 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Prefer adapter APIs that accept signer callbacks, wallet clients, or isolated signing providers instead of raw private-key strings. - Keep key material inside a minimal, locally controlled signing boundary and provide dependencies only with transaction requests and resulting signatures. - Consider hardware-backed, operating-system-backed, or remote signing services that never reveal private keys to application dependencies. - Pin security-sensitive adapter dependencies to reviewed exact versions and require explicit review before lockfile updates. - Audit adapter and transitive dependency source code for telemetry, logging, serialization, and outbound network behavior. - Separate EVM and Solana credentials rather than deriving every chain from one high-value mnemonic where feasible. - Correct the README claim unless the implementation can technically guarantee that dependencies cannot export key material. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
test-bridge.js:18
Finding
Executable Test Script Discloses Partial Private-Key Material and Automatically Bridges Funds<![CDATA[ ## Vulnerability Details **File Location**: `test-bridge.js:18-50` **Additional Locations**: `test-bridge-debug.js:9-54`, `test-bridge-events.js:9-56` **Vulnerability Type**: Unsafe production-secret use and automatic financial side effects in test utilities **Risk Level**: High ### Vulnerable Code `test-bridge.js`: ```javascript const hdNode = ethers.HDNodeWallet.fromPhrase(seedPhrase.trim()); const privateKey = hdNode.privateKey; console.log("Wallet address:", hdNode.address); console.log("Private key prefix:", privateKey.slice(0, 10) + "..."); const kit = new BridgeKit(); const bridgeUSDC = async () => { try { // Single adapter for both chains (like Circle's example) const adapter = createViemAdapterFromPrivateKey({ privateKey: privateKey, }); console.log("---------------Starting Bridging---------------"); console.log("From: Base_Sepolia"); console.log("To: Ethereum_Sepolia"); console.log("Amount: 1.00 USDC"); const result = await kit.bridge({ from: { adapter, chain: "Base_Sepolia" }, to: { adapter, chain: "Ethereum_Sepolia" }, amount: "1.00", }); console.log("RESULT", inspect(result, false, null, true)); } catch (err) { console.log("ERROR", inspect(err, false, null, true)); } }; bridgeUSDC(); ``` `test-bridge-debug.js` performs the same automatic operation: ```javascript const bridgePromise = kit.bridge({ from: { adapter, chain: "Base_Sepolia" }, to: { adapter, chain: "Ethereum_Sepolia" }, amount: "1.00", }); const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Bridge timeout after 60s")), 60000) ); const result = await Promise.race([bridgePromise, timeoutPromise]); ``` `test-bridge-events.js` also initiates the bridge automatically: ```javascript const bridgePromise = kit.bridge({ from: { adapter, chain: "Base_Sepolia" }, to: { adapter, chain: "Ethereum_Sepolia" }, amount: "1.00", }); const timeoutPromise = new ...[truncated 2176 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove transaction-capable debugging scripts from the production Skill package. - Replace live bridge calls with mocked adapters and deterministic unit tests. - Require explicitly supplied disposable test credentials rather than loading the normal `WALLET_SEED_PHRASE`. - Enforce testnet chain IDs and reject execution if any mainnet network is detected. - Add a mandatory `--execute` switch and interactive transaction summary before any test sends funds. - Never print any portion of a private key, mnemonic, or secret key. - Implement real operation cancellation where supported; do not treat `Promise.race()` as cancellation. - Ensure test scripts exit with a failure status after errors so automation does not treat incomplete operations as successful. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/bridge-async.js:255
Finding
Bridge Grants Token Allowance Beyond the Required Amount<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bridge-async.js:255-265` **Vulnerability Type**: Excessive ERC-20 spending authorization **Risk Level**: Medium ### Vulnerable Code ```javascript // For V2, we need to approve amount + maxFee (10%) // Use a generous allowance to avoid issues const approveAmount = BigInt(state.amountWei) * 2n; // 2x amount to cover any fees // Check current allowance const allowance = await usdc.allowance(wallet.address, fromChain.tokenMessenger); if (allowance >= approveAmount) { console.log('✅ Already has sufficient allowance'); return setState(bridgeId, { status: STATES.APPROVED }); } // Approve generous amount const tx = await usdc.approve(fromChain.tokenMessenger, approveAmount); ``` ### Technical Analysis The bridge approves the CCTP Token Messenger to spend 200% of the requested bridge amount. The burn operation elsewhere in the implementation limits `maxFee` to 10% of the amount, so a two-times allowance exceeds the amount and configured maximum fee required for the transaction. ERC-20 allowances commonly remain active after the intended operation. If the bridge consumes less than the approved amount, the residual authorization remains available to the spender contract. This violates least privilege by granting more token authority than the declared bridge operation requires. ### Attack Path 1. A user starts an asynchronous bridge for a given USDC amount. 2. The script approves the Token Messenger for twice that amount. 3. The bridge consumes only the amount and applicable fee, leaving residual allowance. 4. The configured spender contract, its upgrade authority, or the address configuration is compromised. 5. The compromised spender invokes `transferFrom()` against the remaining allowance. 6. Additional USDC is removed from the wallet without a new user approval. ### Impact Assessment The potential loss is bounded by the unused allowance and the wallet’s available USDC balance. The finding does ...[truncated 260 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Approve only the bridge amount plus the exact maximum fee required by the selected CCTP mode. - Calculate the required approval using the same fee formula used by the burn transaction. - Revoke unused allowance after the bridge completes or fails. - Prefer permit-based or single-use authorization mechanisms if supported. - Validate the chain ID, USDC contract, and Token Messenger address immediately before authorization. - Display the exact spender, amount, chain, and residual allowance to the user before signing. - Add tests asserting that approval never exceeds the required burn amount and maximum fee. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/wallet.js:73
Finding
Solana Key Derivation Does Not Implement the Documented BIP-44 Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/wallet.js:73-76` **Vulnerability Type**: Incorrect cryptographic key derivation and recovery incompatibility **Risk Level**: Medium ### Vulnerable Code ```javascript function deriveSolanaKeypair(seedPhrase) { // Solana: m/44'/501'/0'/0' - using first 32 bytes of seed const seed = bip39.mnemonicToSeedSync(seedPhrase); return Keypair.fromSeed(seed.slice(0, 32)); } ``` ### Technical Analysis The comment and `SKILL.md` state that the Solana account uses the hardened derivation path `m/44'/501'/0'/0'`. The implementation does not derive that path. Instead, it computes the 64-byte BIP-39 seed and directly passes its first 32 bytes to `Keypair.fromSeed()`. Standard Solana wallets using SLIP-0010/Ed25519 derivation at the documented path will therefore derive a different keypair from the same mnemonic. This creates a dangerous mismatch between the documented recovery procedure and the wallet actually holding funds. The generated account remains deterministic, but it is not the account users are told to expect from standard derivation software. ### Attack Path 1. The Skill generates or imports a mnemonic. 2. It derives a Solana keypair from the first 32 bytes of the BIP-39 seed. 3. The user deposits assets into the resulting Solana address. 4. The original Skill installation becomes unavailable or the user attempts wallet migration. 5. The user imports the mnemonic into a standard wallet implementing `m/44'/501'/0'/0'`. 6. The standard wallet derives a different address, and the funded account is not visible or directly accessible through the expected recovery process. ### Impact Assessment The issue can cause practical loss of access to Solana assets during disaster recovery, migration, or interoperability with standard wallet software. It does not reveal the private key to an attacker, but it undermines a core security property of mnemonic wallets: reliable recovery from documented derivatio ...[truncated 93 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Implement hardened Ed25519 derivation for `m/44'/501'/0'/0'` using a vetted SLIP-0010-compatible library. - Add deterministic test vectors that verify the mnemonic, derivation path, private key, and public address. - Test recovery against established Solana wallet tooling before claiming compatibility. - Clearly version the old derivation scheme if existing users may already have funded accounts. - Provide a safe migration utility that derives both legacy and corrected accounts and transfers assets only after explicit user confirmation. - Do not silently change derivation for existing wallets without a compatibility and migration plan. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (70)

Credential Access

High
Category
Privilege Escalation
Content
git clone https://github.com/voltagemonke/Agent-wallet.git
cd Agent-wallet
npm install
cp .env.example .env
# Edit .env with your seed phrase
```
Confidence
90% confidence
Finding
The documentation instructs users to create a local .env file for wallet secrets, which is a sensitive credential storage pattern. While common in development, using plaintext environment files for seed phrases in a wallet-management skill raises the risk of accidental disclosure through source control, backups, malware, shell history, or agent/logging exposure.

Credential Access

High
Category
Privilege Escalation
Content
cd Agent-wallet
npm install
cp .env.example .env
# Edit .env with your seed phrase
```

---
Confidence
89% confidence
Finding
The explicit instruction to edit .env with a seed phrase normalizes storing a highly privileged wallet secret in plaintext configuration. In this skill context, compromise of that file means complete takeover of all supported chain accounts derived from the mnemonic and potential immediate theft of assets.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The finding shows the skill actually performs bridge estimation/testing with hidden wallet secret access and lacks the advertised support for wallet creation, balances, transfers, and Solana. In a wallet skill, such discrepancies undermine informed consent and can mislead automated routing systems into invoking a transaction-capable component under incomplete or false assumptions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The finding shows the skill actually performs bridge estimation/testing with hidden wallet secret access and lacks the advertised support for wallet creation, balances, transfers, and Solana. In a wallet skill, such discrepancies undermine informed consent and can mislead automated routing systems into invoking a transaction-capable component under incomplete or false assumptions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The finding shows the skill actually performs bridge estimation/testing with hidden wallet secret access and lacks the advertised support for wallet creation, balances, transfers, and Solana. In a wallet skill, such discrepancies undermine informed consent and can mislead automated routing systems into invoking a transaction-capable component under incomplete or false assumptions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The finding shows the skill actually performs bridge estimation/testing with hidden wallet secret access and lacks the advertised support for wallet creation, balances, transfers, and Solana. In a wallet skill, such discrepancies undermine informed consent and can mislead automated routing systems into invoking a transaction-capable component under incomplete or false assumptions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The finding shows the skill actually performs bridge estimation/testing with hidden wallet secret access and lacks the advertised support for wallet creation, balances, transfers, and Solana. In a wallet skill, such discrepancies undermine informed consent and can mislead automated routing systems into invoking a transaction-capable component under incomplete or false assumptions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The finding shows the skill actually performs bridge estimation/testing with hidden wallet secret access and lacks the advertised support for wallet creation, balances, transfers, and Solana. In a wallet skill, such discrepancies undermine informed consent and can mislead automated routing systems into invoking a transaction-capable component under incomplete or false assumptions.

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
Run: `node scripts/wallet.js create`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
- **One seed per agent** - Each agent instance isolated
- **Seed shown once** - Only at creation, never logged
- **Memory only** - Private keys derived on-demand, never persisted
- **No chat import** - Seeds added via .env only (except generation)

## Error Handling
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
- **One seed per agent** - Each agent instance isolated
- **Seed shown once** - Only at creation, never logged
- **Memory only** - Private keys derived on-demand, never persisted
- **No chat import** - Seeds added via .env only (except generation)

## Error Handling
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

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/bridge-async.js:120

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/wallet.js:523

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
test-bridge-debug.js:11

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
test-bridge-events.js:11

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
test-bridge.js:19

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
test-estimate.js:11

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
test-estimate2.js:11