Back to skill

Security audit

Project 0 - DeFi Native Prime Broker

Security checks for vulnerabilities and agentic risk

Overview

This DeFi skill is purpose-aligned, but it gives an agent high-impact wallet signing authority and includes under-scoped swap and transaction verification guidance.

Review before installing. Use only a dedicated Solana wallet with limited funds, require explicit approval immediately before every signature, avoid exposing a main private key, and do not let the agent sign Jupiter swap transactions unless it decodes and verifies the transaction against the approved quote. Be aware that wallet portfolio lookups disclose your wallet address and holdings to ai.0.xyz.

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
SKILL.md:605
Finding
Blind Signing of a Remotely Supplied Jupiter Transaction<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:605-609` **Vulnerability Type**: Blind signing of untrusted transaction data **Risk Level**: High ### Vulnerable Code ```typescript const swapTx = VersionedTransaction.deserialize( Buffer.from(swapResponse.swapTransaction, "base64"), ); swapTx.sign([wallet]); const sig = await connection.sendRawTransaction(swapTx.serialize()); ``` ### Technical Analysis The skill directs the agent to deserialize and sign a transaction returned by the remote Jupiter API without independently validating the transaction instructions. It does not verify: - The invoked Solana program IDs - Input and output token mints - Source and destination token accounts - Transfer amounts and minimum output - Recipients of funds - Token approvals or account-authority changes - Priority fees and other transaction costs - Whether the transaction corresponds to the quote and user-approved plan Although the skill states that the user should see a plan before execution, approval of a textual plan does not prove that the opaque transaction returned later by the external API implements that plan. The wallet keypair grants the ability to authorize transactions, so every instruction must be treated as untrusted until decoded and verified. ### Attack Path 1. The user approves a legitimate-looking token swap plan. 2. The agent requests a serialized swap transaction from the external Jupiter endpoint. 3. An attacker compromises the API, its upstream infrastructure, or another component in the response path. 4. The attacker returns a valid serialized transaction containing unintended transfers, approvals, excessive fees, or malicious program invocations. 5. The agent deserializes the response and signs it without inspecting its instructions. 6. The signed transaction is submitted to Solana and executes with the wallet’s authority. ### Impact Assessment Successful exploitation could authorize unintended movement of tokens held by the ...[truncated 468 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Decode every transaction instruction before signing. 2. Allowlist the expected Jupiter and Solana program IDs and reject unknown programs. 3. Verify that source accounts belong to the configured wallet. 4. Verify the exact input mint, output mint, maximum input amount, minimum output amount, recipients, slippage, and fee limits against the approved quote. 5. Reject unexpected authority changes, delegate approvals, account closures, and transfers to unrelated recipients. 6. Simulate the transaction and reject simulation errors or unexplained balance changes. 7. Display the independently verified transaction details and obtain explicit user confirmation immediately before signing. 8. Re-fetch wallet balances after execution and verify the expected token deltas. 9. Prefer constrained transaction construction or verified instruction generation over signing an opaque transaction blob. ]]>

other

Warning
Location
SKILL.md:268
Finding
Wallet Identity and Portfolio Disclosure to a Remote Service<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:268-275` **Vulnerability Type**: Wallet privacy disclosure **Risk Level**: Medium ### Vulnerable Code ```text GET https://ai.0.xyz/api/wallet/{address} ``` ```typescript const res = await fetch(`https://ai.0.xyz/api/wallet/${walletAddress}`); const data = await res.json(); // data.wallet, data.total_usd_value, data.tokens[] ``` ### Technical Analysis The skill sends the user’s Solana wallet address to `ai.0.xyz` to obtain its complete token portfolio. A wallet address is public blockchain data and is not equivalent to a private key. Nevertheless, transmitting it to a service allows the operator to associate the address and holdings with request metadata such as IP address, time, agent activity, and requested financial strategy. This request is relevant to personalized yield recommendations, and the reviewed material does not transmit the private key to the wallet API. However, the workflow directs the disclosure without requiring informed consent or offering a local or user-selected RPC alternative. Generic bank and strategy queries also do not require disclosure of a wallet address, so the wallet request should only occur when portfolio-specific analysis is requested. ### Attack Path 1. The agent obtains `WALLET_ADDRESS` from the environment, derives it from the configured keypair, or receives it from the user. 2. The agent places the address in a request URL sent to `ai.0.xyz`. 3. The remote service receives the wallet identity together with request and network metadata. 4. The service retrieves or observes the wallet’s complete holdings. 5. The service operator, a compromised logging system, or another party with access to the logs correlates the wallet with the user’s activity and financial interests. ### Impact Assessment The exposure can enable portfolio profiling, wallet deanonymization, targeted phishing, and identification of high-value users. No private-key disclosure is shown, ...[truncated 359 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Obtain explicit consent before sending a wallet address to `ai.0.xyz`. 2. Clearly identify the destination, the data being disclosed, and the potential for wallet-to-session correlation. 3. Offer a local or user-selected Solana RPC balance lookup as the privacy-preserving default. 4. Do not query a wallet when the user only requests generic bank rates or strategy information. 5. Minimize logging and retention of wallet requests on the service side. 6. Avoid placing wallet addresses in URLs where intermediaries and access logs commonly retain them; use a privacy-conscious request design where supported. 7. Allow the user to provide selected token balances manually if they do not want to disclose the entire portfolio. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:605
Finding
Swap Example Reports Success Without Checking Program-Level Failure<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:605-611` **Vulnerability Type**: Missing transaction-result validation **Risk Level**: Medium ### Vulnerable Code ```typescript const swapTx = VersionedTransaction.deserialize( Buffer.from(swapResponse.swapTransaction, "base64"), ); swapTx.sign([wallet]); const sig = await connection.sendRawTransaction(swapTx.serialize()); await connection.confirmTransaction(sig, "confirmed"); console.log(`Swap: https://solscan.io/tx/${sig}`); ``` ### Technical Analysis The swap-specific example waits for transaction confirmation but discards the returned confirmation object and does not inspect `confirmation.value.err`. A Solana transaction can be included in a confirmed block while its program instructions fail. The code then prints a swap result that implies success. This contradicts the general transaction-verification requirements elsewhere in the same file, which correctly state that program-level errors must be checked. An agent may reasonably follow the operation-specific example and therefore produce incorrect state reporting. ### Attack Path 1. The agent submits the signed Jupiter swap transaction. 2. The transaction is included in a block but fails at the program level because of slippage, stale state, insufficient funds, an invalid route, or another execution error. 3. `confirmTransaction` returns a confirmation containing a non-null error. 4. The example ignores that result and prints a swap link as though the operation succeeded. 5. The workflow may proceed to a deposit or borrowing operation under the false assumption that the expected output tokens are available. ### Impact Assessment This issue does not itself grant an attacker additional system privileges. It can, however, cause incorrect financial state reporting, wasted fees, failed follow-up transactions, and unsafe strategy execution. In a multi-step DeFi workflow, acting on an incorrectly assumed balance can increase operational a ...[truncated 24 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Capture and validate the confirmation result before reporting success: ```typescript const sig = await connection.sendRawTransaction(swapTx.serialize()); const confirmation = await connection.confirmTransaction(sig, "confirmed"); if (confirmation.value.err) { throw new Error( `Swap failed: ${JSON.stringify(confirmation.value.err)}\n` + `https://solscan.io/tx/${sig}`, ); } console.log(`Swap succeeded: https://solscan.io/tx/${sig}`); ``` Additionally: 1. Simulate the transaction before submission. 2. Re-fetch source and destination token balances after confirmation. 3. Verify that the actual balance changes satisfy the approved input amount and minimum output. 4. Do not start dependent deposit or borrow operations until post-transaction state has been verified. 5. Apply the same verification helper to every transaction path so operation-specific examples cannot omit mandatory checks. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (10)

Credential Access

High
Category
Privilege Escalation
Content
import { Keypair } from "@solana/web3.js";
import fs from "fs";

// keypairPath from user message, or WALLET_KEYPAIR from .env
const wallet = Keypair.fromSecretKey(
  Uint8Array.from(JSON.parse(fs.readFileSync(keypairPath, "utf-8"))),
);
Confidence
92% confidence
Finding
The skill instructs the agent to locate a wallet keypair path from user input or `.env` and read the secret key file directly from disk. In an agent setting, this is highly sensitive credential access: if the skill is invoked broadly or the environment is shared, the agent may load and use private keys with minimal isolation, increasing the blast radius of compromise or misuse.

Credential Access

High
Category
Privilege Escalation
Content
import { Connection } from "@solana/web3.js";
import { Project0Client, getConfig } from "@0dotxyz/p0-ts-sdk";

// RPC_URL from .env or provided by user
const connection = new Connection(RPC_URL, "confirmed");
const config = getConfig("production");
const client = await Project0Client.initialize(connection, config);
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
```typescript
import { VersionedTransaction } from "@solana/web3.js";

// JUP_API_KEY from .env or provided by user
const inputMint = "So11111111111111111111111111111111111111112"; // SOL
const outputMint = "Bybit2vBJGhPF52GBdNaQfUJ6ZpThSgHBobjWZpLPb4B"; // bbSOL
const amount = 100000000; // raw integer units (0.1 SOL = 100000000 lamports)
Confidence
83% confidence
Finding
The skill directs the agent to retrieve a Jupiter API key from `.env` or user input and use it in outbound requests. While less sensitive than a wallet private key, this is still credential handling by the agent and can lead to unauthorized third-party API usage, billing abuse, or accidental exposure through logs or error messages.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
Deposit funds to earn yield across Solana's highest-yielding venues.
  Borrow stablecoins against deposited collateral instead of selling crypto.
  Execute advanced yield strategies via rate arbitrage and looping.
  All operations are on-chain and permissionless -- no accounts, no approval process.
  Note: This skill requires a wallet keypair to sign transactions. Use a dedicated
  wallet with limited funds -- never expose your main private key. The agent will
  always ask for confirmation before signing. Read-only operations need no keypair.
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Session Persistence

Medium
Category
Rogue Agent
Content
## On-Chain: Interacting with the Protocol

Use the TypeScript SDK for actions that require signing: create account, deposit,
withdraw, borrow, repay. Requires a Solana keypair and user authorization.

### Prerequisites
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
console.log(`Success: https://solscan.io/tx/${sig}`);
```

**Never report a transaction as successful without checking
`confirmation.value.err`.** If `err` is not null, the transaction landed
on-chain but the program rejected it — the user's funds were not moved.
Confidence
75% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The skill explicitly warns elsewhere that `confirmTransaction` does not guarantee program success, but the Jupiter swap example still reports success immediately after confirmation without checking `confirmation.value.err`. This can cause the agent to misreport failed swaps as successful, leading to incorrect follow-on actions such as depositing assets that were never received.

External Transmission

Medium
Category
Data Exfiltration
Content
// 1. Get quote
const quoteResponse = await (
  await fetch(
    `https://api.jup.ag/swap/v1/quote?inputMint=${inputMint}` +
      `&outputMint=${outputMint}` +
      `&amount=${amount}` +
      `&slippageBps=50` +
Confidence
50% 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
// 1. Get quote
const quoteResponse = await (
  await fetch(
    `https://api.jup.ag/swap/v1/quote?inputMint=${inputMint}` +
      `&outputMint=${outputMint}` +
      `&amount=${amount}` +
      `&slippageBps=50` +
Confidence
50% 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
// 2. Build swap transaction
const swapResponse = await (
  await fetch("https://api.jup.ag/swap/v1/swap", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.