Back to skill

Security audit

Public

Security checks for vulnerabilities and agentic risk

Overview

This skill is purpose-aligned with minting an NFT, but it asks the agent to handle raw wallet private keys and sign payment and transaction data without enough user control or validation.

Review before installing. Use only a dedicated, minimally funded wallet, do not paste a valuable wallet private key into the agent, require explicit confirmation before each signature and broadcast, and independently verify the payment amount, chain, contract, recipient, transaction calldata, and fees.

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

T09 · Insecure Skill Coding Practices

Error
Location
skill.md:73
Finding
Blind Signing of Server-Controlled USDC Payment Authorization<![CDATA[ ## Vulnerability Details **File Location**: `skill.md:73-84` **Vulnerability Type**: Unvalidated cryptographic signing of remote payment data **Risk Level**: Critical ### Vulnerable Code ```javascript const res = await fetch("https://budsbase.xyz/api/prepare", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ wallet: wallet.address, challengeId: "CHALLENGE_ID", answer: "ANSWER" }), }); const { prepareId, payment } = await res.json(); // 2b. Sign USDC payment (EIP-712) const paymentSignature = await wallet.signTypedData(payment.domain, payment.types, payment.values); console.log(JSON.stringify({ prepareId, paymentSignature })); ``` ### Technical Analysis The remote `/api/prepare` endpoint controls all EIP-712 components passed to `wallet.signTypedData`: the domain, type definitions, and values. The Skill does not locally verify the following security-critical properties: - Base mainnet chain ID - Official Base USDC contract address - EIP-712 verifying contract - Authorization type and field layout - Payment recipient - Exact payment amount of 1 USDC - Authorization validity interval - Authorization nonce - Whether the authorization is limited to the advertised mint A statement elsewhere in the documentation that the payment is 1 USDC does not cryptographically enforce that constraint. Because an EIP-712 signature can authorize an on-chain token operation, signing arbitrary structured data supplied by a remote service crosses a financial trust boundary. ### Attack Path 1. An attacker compromises `budsbase.xyz`, its API infrastructure, DNS resolution, or another component capable of controlling the `/api/prepare` response. 2. The malicious endpoint returns altered EIP-712 data, such as a larger token amount, a different recipient, a different verifying contract, or another signing schema. 3. The Skill passes the response directly to `wallet.signTypedData` without validation or presenting decoded deta ...[truncated 747 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not sign EIP-712 data directly from a remote response. - Construct the expected authorization locally from independently verified inputs wherever possible. - Hard-code or securely pin the expected Base chain ID and official USDC contract address. - Allow only the exact expected EIP-712 primary type and field schema. - Verify the recipient against an audited, documented payment recipient. - Enforce an exact amount of 1 USDC using the token's correct decimal representation. - Enforce short and reasonable `validAfter` and `validBefore` bounds. - Validate the authorization nonce and reject reused or malformed values. - Decode and display the complete authorization to the user. - Require explicit user confirmation immediately before signing. - Prefer an external or hardware wallet that independently displays the typed-data details. - Abort on every unknown field, unexpected domain value, schema variation, or contract mismatch. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
skill.md:114
Finding
Blind Signing and Submission of an Arbitrary Server-Provided Transaction<![CDATA[ ## Vulnerability Details **File Location**: `skill.md:114-129` **Vulnerability Type**: Unvalidated signing of a remote blockchain transaction **Risk Level**: Critical ### Vulnerable Code ```javascript import { ethers } from "ethers"; const PK = "YOUR_PRIVATE_KEY"; if (!/^0x[0-9a-fA-F]{64}$/.test(PK)) throw new Error("Invalid private key — must be 0x + 64 hex chars"); const wallet = new ethers.Wallet(PK); const transaction = /* transaction object from Step 3 */; const signedTransaction = await wallet.signTransaction(transaction); console.log(JSON.stringify({ signedTransaction })); ``` ```bash curl -X POST https://budsbase.xyz/api/broadcast \ -H "Content-Type: application/json" \ -d '{"signedTransaction": "0x<from_above>"}' ``` ### Technical Analysis The transaction object originates from the remote `/api/complete` response and is signed without local semantic validation. The instructions do not require verification of: - `chainId` - Destination address (`to`) - Native asset amount (`value`) - Contract method selector - Decoded calldata and arguments - Expected NFT contract - Mint recipient - Token approvals or asset-transfer operations - Nonce correctness - Gas limits and fee bounds - Transaction type A remote service should not be trusted to define an arbitrary transaction under a user's wallet authority. Merely signing locally protects the raw private key from direct transmission, but it does not protect the wallet from malicious transaction contents. The signed transaction is also sent back to the same service that generated it, giving that service the ability to broadcast the signed operation. ### Attack Path 1. An attacker compromises or controls the backend that responds to `/api/complete`. 2. Instead of returning the documented mint transaction, the backend returns a transaction that transfers ETH, grants a token approval, invokes an unintended contract, or otherwise acts against the wallet. 3. The Skill assigns that response to ` ...[truncated 752 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin Base mainnet chain ID `8453` and reject every other chain. - Pin the audited NFT contract address rather than trusting the server-provided destination. - Decode the calldata locally using a pinned contract ABI. - Permit only the exact expected mint function and argument structure. - Confirm that the mint recipient equals the user's wallet address. - Require `value` to be zero or exactly equal to a separately documented and locally enforced value. - Reject token approvals, transfers, delegate calls, contract deployments, and unknown method selectors. - Independently retrieve and validate the wallet nonce. - Set strict gas-limit and fee caps and independently estimate gas. - Present the decoded destination, method, arguments, value, chain, and fees to the user. - Require explicit confirmation through an external wallet or hardware signer. - Broadcast through a trusted, independently configured Base RPC endpoint rather than returning the signed transaction to the transaction-generating service. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
skill.md:145
Finding
Skill Requests Reusable Wallet Private Keys and Assumes Full Signing Authority<![CDATA[ ## Vulnerability Details **File Location**: `skill.md:145-157` **Vulnerability Type**: Excessive credential access and violation of least privilege **Risk Level**: High ### Vulnerable Code ```markdown ## Agent Behavior - The user will provide their **wallet address** and **private key**. That's all you need — handle the entire mint flow from there without asking further questions. - **Step 1** (challenge): use `curl`. - **Step 2** (prepare & sign payment): use a **single node script** that fetches `/prepare` and signs the USDC payment. Do NOT split this into separate curl + node steps. - **ethers fallback:** Before running the script, check if ethers is available: `node -e "require('ethers')"`. If it fails, install to a temp location: `npm install --prefix /tmp ethers`, then run the script with `NODE_PATH=/tmp/node_modules`. - **Step 3** (complete): use `curl` — this settles payment and returns the unsigned mint tx. - **Step 4** (sign & broadcast): use a **single node script** that signs the transaction from Step 3, then use `curl` to POST the signed tx to `/broadcast`. - After each step, briefly tell the user what happened before moving to the next. - Handle errors gracefully — if a step fails, explain why and retry or stop. - **Mint limit reached (409):** If `/challenge` returns 409, ask the user for a new wallet address and private key, then restart the flow with the new wallet. - Never expose the user's private key in output or logs. - Signing must always happen locally — never send private keys over the network. ``` ### Technical Analysis A reusable EVM private key grants complete signing authority over the corresponding wallet and is substantially more privileged than required to perform a single NFT mint. Requesting the key through an agent conversation places it in the agent's input and execution context before any local-signing protection applies. The instruction to request another wallet address and private key after a mint-limit resp ...[truncated 1617 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never ask users to paste private keys into a conversation or agent prompt. - Integrate with an external wallet provider, hardware wallet, WalletConnect-compatible signer, or operating-system-protected keystore. - Keep signing in a user-controlled process that does not reveal key material to the agent. - Request separate, explicit approval for the payment authorization and mint transaction. - Display decoded transaction and typed-data details in the signing interface. - Use a dedicated, minimally funded wallet if external-wallet integration is impossible. - Do not request a second private key when a mint limit is reached; instruct the user to select another account through their wallet interface. - Prevent secrets from entering command-line arguments, source files, shell history, logs, telemetry, and temporary files. - Isolate signing from network-facing and dynamically installed code. - Document that any private key previously disclosed through an agent channel should be treated as potentially compromised and that assets should be migrated to a newly generated wallet. ]]>

T08 · Insecure Dependencies

Error
Location
skill.md:148
Finding
Unpinned Runtime Dependency Installation in a Private-Key Signing Workflow<![CDATA[ ## Vulnerability Details **File Location**: `skill.md:148-151` **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: High ### Vulnerable Code ```markdown - **Step 2** (prepare & sign payment): use a **single node script** that fetches `/prepare` and signs the USDC payment. Do NOT split this into separate curl + node steps. - **ethers fallback:** Before running the script, check if ethers is available: `node -e "require('ethers')"`. If it fails, install to a temp location: `npm install --prefix /tmp ethers`, then run the script with `NODE_PATH=/tmp/node_modules`. - **Step 3** (complete): use `curl` — this settles payment and returns the unsigned mint tx. - **Step 4** (sign & broadcast): use a **single node script** that signs the transaction from Step 3, then use `curl` to POST the signed tx to `/broadcast`. ``` ### Technical Analysis The fallback command installs the latest version of `ethers` and its resolved dependency graph at runtime. No exact version, lockfile, integrity hash, trusted registry configuration, or provenance verification is specified. This installation occurs in a workflow where the package is subsequently loaded into a Node.js process that handles a reusable private key and produces financial signatures. NPM package lifecycle scripts may also execute during installation unless explicitly disabled. Consequently, compromise of a package release, maintainer account, transitive dependency, registry path, or local NPM configuration could introduce attacker-controlled code into the signing environment. Installing under `/tmp` does not provide a security boundary. `/tmp` may also be shared or exposed to local manipulation depending on host configuration. ### Attack Path 1. The signing environment does not already contain `ethers`. 2. The Skill executes `npm install --prefix /tmp ethers`. 3. NPM resolves an unpinned current release and its transitive dependencies from the configured registry. 4. A co ...[truncated 925 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not install signing dependencies dynamically during Skill execution. - Vendor and audit the required signing implementation, or provide it as part of a reproducible, reviewed package. - Pin `ethers` and every transitive dependency to exact versions using a committed lockfile. - Verify package integrity and provenance against trusted, preapproved values. - Use a dedicated trusted registry and reject unexpected registry configuration. - Disable lifecycle scripts with `--ignore-scripts` where compatible with the selected package. - Install into a private, permission-restricted directory rather than a shared `/tmp` prefix. - Use an isolated runtime with no access to unrelated files, credentials, or environment variables. - Keep raw private keys outside the dependency process by delegating signing to an external wallet or hardware device. - Establish a dependency update process that includes security review and reproducible-build verification before deployment. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (9)

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The security section claims the skill does not access the filesystem, run shell commands, or execute arbitrary code, but later instructions explicitly require curl, node, npm, and local file writes. This contradiction can mislead users and agents into granting trust under false assumptions, increasing the chance they expose sensitive credentials or permit unsafe execution paths.

Session Persistence

Medium
Category
Rogue Agent
Content
**Install locally:**
```bash
mkdir -p ~/.openclaw/skills/base-buds
curl -s https://budsbase.xyz/skill.md > ~/.openclaw/skills/base-buds/SKILL.md
```
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.

Skill Enumeration

Medium
Category
Agent Snooping
Content
**Install locally:**
```bash
mkdir -p ~/.openclaw/skills/base-buds
curl -s https://budsbase.xyz/skill.md > ~/.openclaw/skills/base-buds/SKILL.md
```

**Or just read the URL directly!**
Confidence
80% confidence
Finding
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.

External Transmission

Medium
Category
Data Exfiltration
Content
**Install locally:**
```bash
mkdir -p ~/.openclaw/skills/base-buds
curl -s https://budsbase.xyz/skill.md > ~/.openclaw/skills/base-buds/SKILL.md
```

**Or just read the URL directly!**
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Unrestricted Tool Access

Medium
Category
Excessive Agency
Content
## Security

- Your EVM private key should **never** leave your local environment — signing happens locally
- This skill makes only HTTP API calls. It does not access your filesystem, run shell commands, or execute arbitrary code

## How It Works
Confidence
80% confidence
Finding
Skill grants unrestricted tool access without appropriate constraints. An agent with unfettered tool access can perform arbitrary actions including file modification, network requests, and code execution.

External Transmission

Medium
Category
Data Exfiltration
Content
const wallet = new ethers.Wallet(PK);

// 2a. Submit challenge answer, get payment data
const res = await fetch("https://budsbase.xyz/api/prepare", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ wallet: wallet.address, challengeId: "CHALLENGE_ID", answer: "ANSWER" }),
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
## Agent Behavior

- The user will provide their **wallet address** and **private key**. That's all you need — handle the entire mint flow from there without asking further questions.
- **Step 1** (challenge): use `curl`.
- **Step 2** (prepare & sign payment): use a **single node script** that fetches `/prepare` and signs the USDC payment. Do NOT split this into separate curl + node steps.
  - **ethers fallback:** Before running the script, check if ethers is available: `node -e "require('ethers')"`. If it fails, install to a temp location: `npm install --prefix /tmp ethers`, then run the script with `NODE_PATH=/tmp/node_modules`.
Confidence
90% confidence
Finding
The instruction to handle the entire mint flow from the user's wallet address and private key 'without asking further questions' encourages autonomous execution of financial operations. In this context, that reduces human verification at the exact points where payment authorization and transaction signing occur, increasing the risk of unintended spending or abuse.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill directs the agent to execute curl, node, and npm commands, including installing dependencies at runtime, even though the capability needed is nominally HTTP requests and local signing. This expands the attack surface significantly: shell execution, package installation, and script execution can be abused to compromise the host environment or exfiltrate secrets if the instructions or fetched dependencies are tampered with.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill instructs the user to provide a wallet private key and tells the agent to complete the mint flow without further questions, but it does not require an explicit warning or confirmation about irreversible on-chain spending and transaction signing. In context, this is especially dangerous because the flow authorizes a USDC transfer and signs a blockchain transaction, both of which can cause immediate asset loss if the transaction data is malicious or misunderstood.

Static analysis

No suspicious patterns detected.