Back to skill

Security audit

Breeze x402

Security checks for vulnerabilities and agentic risk

Overview

This skill is for real Solana payments and transactions, but it asks the agent to handle a wallet private key and sign server-supplied transactions without enough safeguards.

Review before installing. Use only a dedicated low-balance Solana wallet, avoid storing the private key in plaintext .env or wallet-backup.json, pin dependencies, and require explicit review of destination, amount, token, strategy, fees, and decoded transaction instructions before any signing or broadcast.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:389
Finding
Blind Signing and Broadcasting of Untrusted Server-Supplied Transactions<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 389-408; also demonstrated at lines 132-144 **Vulnerability Type**: Missing transaction validation before cryptographic signing **Risk Level**: High ### Vulnerable Code ```typescript async function signAndSend(txString: string) { const bytes = Uint8Array.from(Buffer.from(txString, "base64")); // Try versioned transaction first, then legacy try { const tx = VersionedTransaction.deserialize(bytes); tx.sign([keypair]); const sig = await connection.sendRawTransaction(tx.serialize()); await connection.confirmTransaction(sig, "confirmed"); return sig; } catch { const tx = Transaction.from(bytes); tx.partialSign(keypair); const sig = await connection.sendRawTransaction(tx.serialize()); await connection.confirmTransaction(sig, "confirmed"); return sig; } } ``` ### Technical Analysis The Skill accepts an encoded transaction returned by the remote Breeze API, deserializes it, signs it with the user's Solana keypair, and broadcasts it without verifying its contents. Successful deserialization is incorrectly treated as sufficient authorization. The code does not validate: - Invoked Solana program IDs - Transaction instructions - Source and destination accounts - Token mint addresses - Transfer amounts - Account ownership or authority changes - Delegate approvals - Compute-budget or fee settings - Unexpected additional instructions - Whether the returned transaction corresponds to the user's confirmed request The API endpoint may also be changed through `X402_API_URL`. Consequently, a compromised endpoint, malicious endpoint configuration, DNS or infrastructure compromise, or upstream server vulnerability could provide an arbitrary transaction for signing. The versioned-transaction branch catches every error, including signing or transmission errors, and then attempts to parse the same data as a legacy transaction. This broad fallback further obscures the actual failure ...[truncated 1620 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Decode and inspect every transaction instruction before signing. 2. Maintain strict allowlists for: - Permitted Solana program IDs - Expected strategy and token accounts - Token mint addresses - Transfer recipients - Instruction types 3. Verify that transaction amounts exactly match the user-confirmed amount, subject only to explicitly disclosed fees. 4. Reject transactions containing additional or unexpected instructions. 5. Verify account ownership, signer requirements, writable accounts, delegate changes, and authority changes. 6. Bind the returned transaction to the original request by checking the wallet, strategy ID, mint, amount, and operation type. 7. Simulate the transaction through a trusted RPC endpoint and inspect balance changes before signing. 8. Present a human-readable transaction summary and require explicit user confirmation before any deposit or withdrawal signature. 9. Restrict or remove arbitrary `X402_API_URL` overrides for signing workflows. If overrides are required, require separate confirmation and an allowlisted HTTPS origin. 10. Use a dedicated, low-value wallet with limited funds rather than a general-purpose wallet. 11. Catch deserialization errors separately from signing, sending, and confirmation errors; do not use a broad catch block to change transaction formats after unrelated failures. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:198
Finding
Unpinned Security-Sensitive Wallet and Payment Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 198-200; similar installation instructions appear at lines 29 and 72 **Vulnerability Type**: Unpinned third-party dependencies and weakened dependency resolution **Risk Level**: Medium ### Vulnerable Code ```bash # npm (--legacy-peer-deps required) npm install @faremeter/fetch @faremeter/payment-solana @faremeter/wallet-solana @faremeter/info @solana/web3.js bs58 --legacy-peer-deps ``` ### Technical Analysis The installation command does not pin exact package versions or enforce a reviewed lockfile. Each installation can therefore resolve to package versions that differ from those originally audited. These dependencies are security-sensitive because they: - Construct and authorize x402 payments - Handle Solana keypairs and wallet objects - Process network responses - Serialize and sign blockchain transactions - Execute within the same Node.js process that reads `WALLET_PRIVATE_KEY` The `--legacy-peer-deps` option suppresses normal peer-dependency conflict enforcement. Although this option is not inherently malicious, it increases the likelihood of installing an incompatible or insufficiently reviewed dependency graph. No malicious package is proven to be present in the reviewed project. The vulnerability is the uncontrolled supply-chain trust granted to future dependency versions. ### Attack Path 1. A listed package, transitive dependency, maintainer account, or package publication process is compromised. 2. A malicious version is published under the legitimate package name. 3. A user follows the documented unpinned installation command. 4. npm resolves and installs the malicious or unexpectedly changed release. 5. Package lifecycle code may execute during installation, or malicious runtime code executes when imported. 6. The package gains access to the process, network, wallet objects, and potentially the environment containing `WALLET_PRIVATE_KEY`. 7. The malicious dependency can ...[truncated 681 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every direct dependency to an exact reviewed version rather than using unconstrained package names. 2. Commit a reviewed lockfile containing integrity hashes. 3. Use `npm ci` in documented workflows so installation fails when the lockfile and manifest differ. 4. Resolve package compatibility explicitly instead of relying on `--legacy-peer-deps`. 5. Audit direct and transitive dependencies for provenance, maintainers, lifecycle scripts, and known vulnerabilities. 6. Consider disabling lifecycle scripts during installation where compatible with the required packages. 7. Use dependency update automation that requires review and testing before lockfile changes are accepted. 8. Run the Skill in an isolated environment with minimal filesystem access and only the network destinations required for its function. 9. Keep the signing key outside the general Node.js process where possible, such as in a hardware wallet or narrowly scoped signing service. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:39
Finding
Plaintext Private-Key Duplication and Unsafe Environment Loading<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 39-40 and 154-155 **Vulnerability Type**: Insecure storage and loading of sensitive wallet credentials **Risk Level**: Medium ### Vulnerable Code ```js fs.writeFileSync('wallet-backup.json', JSON.stringify(Array.from(keypair.secretKey))); fs.writeFileSync('.env', `WALLET_PRIVATE_KEY=${secretKeyBase58}\n`); ``` The generated environment file is subsequently loaded with: ```bash # Set WALLET_PRIVATE_KEY from the .env created in Step 0 export $(cat .env | xargs) && node deposit.js ``` ### Technical Analysis The wallet-generation instructions create two plaintext copies of the same Solana private key: - `wallet-backup.json` - `.env` The file-writing calls do not specify restrictive permissions. Effective permissions therefore depend on the runtime's umask and surrounding environment. On an insecure or shared system, other local users or processes may be able to read the files. Creating duplicate secret material also increases the number of locations that must be protected, excluded from version control, removed from backups, and securely deleted. The documented security rule specifically says to add `wallet-backup.json` to `.gitignore`, but it does not provide an equivalent explicit instruction for `.env` at that point. This increases the risk of accidental source-control exposure. The shell pattern `export $(cat .env | xargs)` is not a safe dotenv parser. It exports every parsed assignment into the child process, handles quoting and whitespace unreliably, and unnecessarily exposes the secret through the process environment. ### Attack Path 1. The user follows the wallet-generation instructions. 2. The Skill creates two plaintext private-key files using ambient filesystem permissions. 3. One of the files is exposed through permissive permissions, source control, backup synchronization, workspace collection, or another local process. 4. An attacker obtains the base58 private key or r ...[truncated 859 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer a hardware wallet, operating-system keychain, encrypted secret manager, or isolated signing service. 2. Use a dedicated low-value wallet funded only for the required operation. 3. Avoid creating duplicate plaintext copies of the private key. 4. If file storage is unavoidable, create the file with mode `0600` and verify its ownership and permissions. 5. Add both `.env` and `wallet-backup.json` to `.gitignore` before generating either file. 6. Add secret-scanning and pre-commit checks to detect accidental wallet-key commits. 7. Do not place wallet secrets in synchronized, shared, temporary, or automatically backed-up project directories. 8. Replace `export $(cat .env | xargs)` with a trusted dotenv loader that reads only the explicitly required variable. 9. Avoid exposing long-lived private keys to a general-purpose agent process; use narrowly scoped signing approval instead. 10. Document key rotation and emergency fund-transfer procedures in case either plaintext file is exposed. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (9)

Credential Access

High
Category
Privilege Escalation
Content
// Save secret key to file — never print it to the console
fs.writeFileSync('wallet-backup.json', JSON.stringify(Array.from(keypair.secretKey)));
fs.writeFileSync('.env', `WALLET_PRIVATE_KEY=${secretKeyBase58}\n`);
console.log('Saved .env with WALLET_PRIVATE_KEY and wallet-backup.json — keep both secret and out of git!');
```
Confidence
95% confidence
Finding
The skill explicitly generates a private key and writes it into a plaintext `.env` file, creating a credential-at-rest exposure. Plaintext secret storage is easily leaked through backups, shell history, editor plugins, accidental commits, or multi-user system access, and compromise of this key enables full wallet theft.

Credential Access

High
Category
Privilege Escalation
Content
// Save secret key to file — never print it to the console
fs.writeFileSync('wallet-backup.json', JSON.stringify(Array.from(keypair.secretKey)));
fs.writeFileSync('.env', `WALLET_PRIVATE_KEY=${secretKeyBase58}\n`);
console.log('Saved .env with WALLET_PRIVATE_KEY and wallet-backup.json — keep both secret and out of git!');
```

```bash
Confidence
94% confidence
Finding
The adjacent guidance normalizes saving `WALLET_PRIVATE_KEY` and a full wallet backup locally, which materially increases the chance of credential exposure. Because this key controls on-chain funds and authorizes paid API requests, theft has direct financial consequences.

Credential Access

High
Category
Privilege Escalation
Content
```

```bash
# Set WALLET_PRIVATE_KEY from the .env created in Step 0
export $(cat .env | xargs) && node deposit.js
```
Confidence
97% confidence
Finding
Using `export $(cat .env | xargs)` to load secrets is unsafe because it encourages handling the private key through shell parsing and process environment manipulation, which can leak via shell history, process inspection, debug logs, or malformed `.env` contents. For a wallet private key, this is a meaningful credential-handling weakness.

Credential Access

High
Category
Privilege Escalation
Content
```bash
# Set WALLET_PRIVATE_KEY from the .env created in Step 0
export $(cat .env | xargs) && node deposit.js
```

---
Confidence
96% confidence
Finding
This instruction operationalizes loading the wallet private key from a plaintext `.env` into the environment before executing a transaction-signing script. In context, the credential immediately enables signing and broadcasting financial transactions, so any leakage can lead to unauthorized asset transfer.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The README states that the skill signs and broadcasts Solana transactions, but it does not prominently warn that these actions move funds and can be irreversible once submitted on-chain. In a skill designed for deposits, withdrawals, and paid API calls, omission of an explicit user-consent and transaction-review warning increases the risk of unintended asset loss through misuse, prompt injection, or operator misunderstanding.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The README requires a WALLET_PRIVATE_KEY environment variable but does not warn that it is a highly sensitive secret granting spending authority over on-chain assets. In the context of an autonomous or semi-autonomous agent skill that performs payments and broadcasts transactions, encouraging private-key injection without strong secret-handling guidance materially raises the chance of wallet compromise and fund theft.

External Transmission

Medium
Category
Data Exfiltration
Content
```bash
# x402 server health
curl https://x402.breeze.baby/healthz

# Breeze strategy info (no auth needed)
curl https://api.breeze.baby/strategy-info/43620ba3-354c-456b-aa3c-5bf7fa46a6d4
Confidence
60% 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
curl https://x402.breeze.baby/healthz

# Breeze strategy info (no auth needed)
curl https://api.breeze.baby/strategy-info/43620ba3-354c-456b-aa3c-5bf7fa46a6d4

# Wallet USDC balance (replace YOUR_WALLET_ADDRESS)
curl https://api.mainnet-beta.solana.com \
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill instructs the agent to sign and broadcast Solana transactions as part of deposit/withdraw workflows, but it does not require an explicit final user confirmation immediately before execution. In an agent setting, this creates a real risk of unintended fund movement if a user request is ambiguous, manipulated, or misinterpreted, especially because the same wallet is also authorized for x402 micropayments.

Static analysis

No suspicious patterns detected.