Back to skill

Security audit

metabot

Security checks for vulnerabilities and agentic risk

Overview

The skill appears purpose-aligned, but it handles real wallet authority and stores recovery phrases and API keys in plaintext with weak safeguards.

Review this carefully before installing. Use it only in a constrained workspace with no valuable wallet funds, do not commit or share account.json, rotate any API key written there, and require explicit confirmation of the exact account, content, fee, and destination before any on-chain action.

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
scripts/utils.ts:195
Finding
Wallet Mnemonics and LLM API Keys Are Persisted in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `scripts/utils.ts:195-203`; sensitive values originate from `scripts/create_agents.ts:72-99` **Vulnerability Type**: Plaintext storage of wallet recovery secrets and API credentials **Risk Level**: High ### Vulnerable Code `scripts/create_agents.ts:72-99`: ```ts const mnemonic = await generateMnemonic() const newAgentAddressIndex = parseAddressIndexFromPath(DEFAULT_PATH) const addresses = await getAllAddress(mnemonic, { addressIndex: newAgentAddressIndex }) const publicKey = await getPublicKey('mvc', mnemonic, { addressIndex: newAgentAddressIndex }) const pathStr = getPath({ defaultPath: DEFAULT_PATH }) const newAccount: Account = { mnemonic, mvcAddress: addresses.mvcAddress, btcAddress: addresses.btcAddress, dogeAddress: addresses.dogeAddress, publicKey, userName: '', path: pathStr, llm: [ { provider: llmFromEnv.provider, apiKey: llmFromEnv.apiKey, baseUrl: llmFromEnv.baseUrl, model: llmFromEnv.model, temperature: llmFromEnv.temperature, maxTokens: llmFromEnv.maxTokens, }, ], } ``` `scripts/utils.ts:195-203`: ```ts export function writeAccountFile(data: AccountData): void { try { const filtered = data.accountList.filter( (account) => account.mnemonic && account.mnemonic.trim() !== '' ) filtered.forEach(normalizeAccountLLM) const filteredData: AccountData = { accountList: filtered } fs.writeFileSync(ACCOUNT_FILE, JSON.stringify(filteredData, null, 4), 'utf-8') ``` ### Technical Analysis A generated wallet recovery mnemonic and an LLM API key loaded from environment configuration are inserted directly into an account object. The entire object is then serialized as readable JSON in the shared root-level `account.json` file. No encryption, operating-system credential store, secret reference, or explicit restrictive file mode is used. The effective permissions therefore depend on the process umask and any pre-existi ...[truncated 2168 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not store recovery mnemonics or raw API keys in `account.json`. 2. Store wallet secrets in an OS keychain, hardware wallet, encrypted vault, or dedicated secret manager. 3. Persist only an opaque secret identifier in account records and retrieve the secret only for the duration of a signing operation. 4. Keep LLM API keys in a secret store or environment configuration rather than copying them into each account. 5. If file-based storage is unavoidable, use authenticated encryption with a user-supplied key that is not stored beside the ciphertext. 6. Create sensitive files with mode `0600`, verify ownership, reject unsafe permissions, and use atomic writes. 7. Separate public account metadata from signing secrets so other Skills can consume addresses and profile data without receiving wallet authority. 8. Add `account.json`, encrypted secret files, backups, and local environment files to ignore rules for version control and packaging. 9. Instruct existing users to rotate exposed API keys and migrate funds to newly generated wallets after secure storage is implemented. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.ts:65
Finding
Ambiguous and Fail-Open Account Selection Can Sign with the Wrong Wallet<![CDATA[ ## Vulnerability Details **File Location**: `scripts/main.ts:65-74`; matching behavior is implemented in `scripts/utils.ts:241-258` **Vulnerability Type**: Fail-open authorization and ambiguous identity selection **Risk Level**: High ### Vulnerable Code `scripts/main.ts:65-74`: ```ts } else { // Select existing wallet const username = extractUsername(userPrompt) if (username) { currentAccount = findAccountByKeyword(username, accountData) } if (!currentAccount) { // Use first account as default currentAccount = accountData.accountList[0] } console.log(`📝 Using wallet: ${currentAccount.mvcAddress}`) } ``` `scripts/utils.ts:241-258`: ```ts export function findAccountByKeyword(keyword: string, accountData: AccountData): Account | null { if (!keyword) return null const lowerKeyword = keyword.toLowerCase().trim() for (const account of accountData.accountList) { if ( (account.userName && account.userName.toLowerCase().includes(lowerKeyword)) || (account.mvcAddress && account.mvcAddress.toLowerCase().includes(lowerKeyword)) || (account.btcAddress && account.btcAddress.toLowerCase().includes(lowerKeyword)) || (account.dogeAddress && account.dogeAddress.toLowerCase().includes(lowerKeyword)) || (account.metaid && account.metaid.toLowerCase().includes(lowerKeyword)) ) { return account } } return null } ``` ### Technical Analysis Account lookup uses substring matching across usernames, addresses, and MetaID values and returns the first matching entry. This does not detect multiple matches and makes the result depend on account-list ordering. More critically, if an account identifier was supplied but lookup returns no match, the main entry point silently falls back to `accountList[0]`. Subsequent code uses the selected account’s plaintext mnemonic to create and broadcast MetaID or Buzz transactions. This is a fail-open identity-selection policy. Failure to resolve the r ...[truncated 1615 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the first-account fallback whenever the caller supplied an account identifier. 2. Require exact, normalized matching for usernames, full addresses, or MetaID values. 3. Collect all matches and reject the operation if there are zero or multiple candidates. 4. Use a stable unique account identifier rather than display-name substring matching. 5. Before signing, display the exact account name, full address, chain, public payload, destination outputs, and estimated maximum fee. 6. Require explicit confirmation for every operation that publishes content or spends wallet funds, unless the caller has configured a narrowly scoped and auditable policy. 7. Bind the confirmed account identifier to the signing operation so it cannot be resolved again differently later in the flow. 8. Add tests for typos, empty identifiers, duplicate names, prefix collisions, substring collisions, and reordered account lists. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/check_environment.sh:33
Finding
Automatic Installation Uses Unlocked Dependency Resolution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/check_environment.sh:33-51`; dependency ranges are declared in `package.json:16-30` **Vulnerability Type**: Unsafe automatic dependency installation and non-reproducible supply-chain resolution **Risk Level**: Medium ### Vulnerable Code `scripts/check_environment.sh:33-51`: ```sh # Auto-install dependencies if node_modules missing (installs TypeScript / ts-node) if [ ! -d "$SKILL_ROOT/node_modules" ]; then echo "📦 未检测到依赖,正在执行 npm install..." (cd "$SKILL_ROOT" && npm install) echo "✅ 依赖安装完成" fi # Verify ts-node available (global or via npx) if ! command -v ts-node &> /dev/null; then if [ -f "$SKILL_ROOT/node_modules/.bin/ts-node" ]; then echo "✅ ts-node 已通过 npm 安装,请使用: npx ts-node scripts/..." else echo "⚠️ 未找到 ts-node,正在安装依赖..." (cd "$SKILL_ROOT" && npm install) echo "✅ 请使用: npx ts-node scripts/main.ts \"<用户提示词>\"" fi fi ``` `package.json:16-30`: ```json "dependencies": { "@scure/bip39": "1.6.0", "bip32": "^4.0.0", "@metalet/utxo-wallet-service": "0.3.33-beta.5", "bitcoinjs-lib": "6.1.7", "ecpair": "^2.1.0", "@bitcoinerlab/secp256k1": "1.2.0", "crypto-js": "^4.2.0", "decimal.js": "^10.4.3", "meta-contract": "^0.4.16", "sharp": "^0.33.0" }, "devDependencies": { "@types/node": "^20.0.0", "@types/crypto-js": "^4.2.2", "typescript": "^5.0.0", "ts-node": "^10.9.0" } ``` ### Technical Analysis The recommended environment-check workflow automatically invokes `npm install` when dependencies are missing. The audited project tree contains no dependency lockfile, while several direct dependencies use caret ranges. Installation can therefore resolve package and transitive dependency versions that were not present when the Skill was reviewed. NPM installation may also execute package lifecycle scripts. If a dependency account, package release, or transitive dependency is compromised, installation-time code executes with ...[truncated 1643 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Generate, review, and commit a `package-lock.json`. 2. Replace automatic `npm install` with `npm ci`, which fails if the manifest and lockfile disagree. 3. Pin security-sensitive direct dependencies to exact reviewed versions where practical. 4. Review the complete transitive dependency graph, package maintainers, provenance, and lifecycle scripts. 5. Require explicit user approval before downloading or installing dependencies. 6. Consider installing with lifecycle scripts disabled during verification and enabling only individually reviewed build steps where required. 7. Use dependency integrity and provenance verification in CI. 8. Run dependency installation and the Skill itself in a constrained environment with no unnecessary access to wallet files, unrelated environment secrets, SSH material, or other project directories. 9. Add automated vulnerability and package-takeover monitoring for the locked dependency graph. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (85)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Reading mnemonics from account.json, deriving wallets across multiple chains, signing transactions/messages, and fetching UTXOs are high-sensitivity wallet operations. Because these are not directly reflected in the skill description, the skill invites under-informed consent and unsafe deployment in environments that would not approve a wallet-management tool.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Reading mnemonics from account.json, deriving wallets across multiple chains, signing transactions/messages, and fetching UTXOs are high-sensitivity wallet operations. Because these are not directly reflected in the skill description, the skill invites under-informed consent and unsafe deployment in environments that would not approve a wallet-management tool.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Reading mnemonics from account.json, deriving wallets across multiple chains, signing transactions/messages, and fetching UTXOs are high-sensitivity wallet operations. Because these are not directly reflected in the skill description, the skill invites under-informed consent and unsafe deployment in environments that would not approve a wallet-management tool.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Reading mnemonics from account.json, deriving wallets across multiple chains, signing transactions/messages, and fetching UTXOs are high-sensitivity wallet operations. Because these are not directly reflected in the skill description, the skill invites under-informed consent and unsafe deployment in environments that would not approve a wallet-management tool.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Reading mnemonics from account.json, deriving wallets across multiple chains, signing transactions/messages, and fetching UTXOs are high-sensitivity wallet operations. Because these are not directly reflected in the skill description, the skill invites under-informed consent and unsafe deployment in environments that would not approve a wallet-management tool.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Reading mnemonics from account.json, deriving wallets across multiple chains, signing transactions/messages, and fetching UTXOs are high-sensitivity wallet operations. Because these are not directly reflected in the skill description, the skill invites under-informed consent and unsafe deployment in environments that would not approve a wallet-management tool.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Reading mnemonics from account.json, deriving wallets across multiple chains, signing transactions/messages, and fetching UTXOs are high-sensitivity wallet operations. Because these are not directly reflected in the skill description, the skill invites under-informed consent and unsafe deployment in environments that would not approve a wallet-management tool.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Reading mnemonics from account.json, deriving wallets across multiple chains, signing transactions/messages, and fetching UTXOs are high-sensitivity wallet operations. Because these are not directly reflected in the skill description, the skill invites under-informed consent and unsafe deployment in environments that would not approve a wallet-management tool.

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill states that MetaBot mnemonics and account data are stored in a root-level account.json, but it does not provide a clear warning that this file contains highly sensitive credentials. Users may inadvertently commit, share, back up insecurely, or expose the file to other tools, leading to wallet compromise and loss of control over on-chain identities or funds.

Missing User Warnings

High
Confidence
97% confidence
Finding
The document explicitly instructs storing highly sensitive secrets including wallet mnemonics and LLM API keys in a shared project-root `account.json`. In the context of a crypto/agent skill that shares this file with other skills, this increases the chance of accidental exposure through source control, broader filesystem access, logs, backups, or other components reading the same file, which could lead to wallet compromise and credential theft.

Missing User Warnings

High
Confidence
98% confidence
Finding
The script writes the generated mnemonic and related wallet/account data directly to local storage immediately after creation, without any confirmation, encryption, or warning to the user. Exposure of this file would allow full takeover of the created blockchain identities and any associated funds, and the same storage also contains other sensitive configuration, compounding impact.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env node

/**
 * 环境变量配置 - 读取 .env / .env.local 获取 LLM 等配置
 */

import * as fs from 'fs'
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
#!/usr/bin/env node

/**
 * 环境变量配置 - 读取 .env / .env.local 获取 LLM 等配置
 */

import * as fs from 'fs'
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
#!/usr/bin/env node

/**
 * 环境变量配置 - 读取 .env / .env.local 获取 LLM 等配置
 */

import * as fs from 'fs'
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
#!/usr/bin/env node

/**
 * 环境变量配置 - 读取 .env / .env.local 获取 LLM 等配置
 */

import * as fs from 'fs'
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
import * as path from 'path'

const ROOT_DIR = path.join(__dirname, '..', '..')
const ENV_FILE = path.join(ROOT_DIR, '.env')
const ENV_LOCAL_FILE = path.join(ROOT_DIR, '.env.local')
const ENV_EXAMPLE_FILE = path.join(ROOT_DIR, '.env.example')
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
const ROOT_DIR = path.join(__dirname, '..', '..')
const ENV_FILE = path.join(ROOT_DIR, '.env')
const ENV_LOCAL_FILE = path.join(ROOT_DIR, '.env.local')
const ENV_EXAMPLE_FILE = path.join(ROOT_DIR, '.env.example')

function parseEnvFile(filePath: string): Record<string, string> {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

High
Confidence
95% confidence
Finding
The script generates a new wallet mnemonic and stores it directly in the account file, creating a plaintext long-term secret at rest. In an agent skill context, this is especially dangerous because the user may not realize the assistant is creating and persisting custodial wallet material locally, and compromise of that file results in full wallet takeover across supported chains.

Missing User Warnings

High
Confidence
89% confidence
Finding
The code derives signing credentials from the mnemonic and transmits signature/public-key material to external services for reward initialization without explicit user disclosure or consent. Even if the mnemonic itself is not sent, hidden network use of wallet-derived credentials expands trust boundaries and can enable account linkage, unintended authorization flows, or abuse if the remote service or transport/logging path is compromised.

Missing User Warnings

High
Confidence
97% confidence
Finding
This function derives the private key from the loaded mnemonic and signs arbitrary transaction material without any visible approval, policy check, or transaction preview in the code shown. That is dangerous because a compromised caller, prompt-injection path, or unintended tool invocation could produce valid blockchain signatures that move funds or authorize irreversible on-chain actions.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill documents commands that invoke environment-sensitive and network-capable tooling, but it does not declare an explicit tool scope such as permissions or allowed-tools. That creates an authorization ambiguity where an agent may over-grant execution, network access, or filesystem access beyond what the user expects.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
The skill encourages sending Buzz messages to the default MVC network without clearly warning that the content may be transmitted or permanently recorded on-chain and may be publicly visible. Users could disclose sensitive or personal information under the mistaken assumption that this is a normal private application message.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding
Using 'npx ts-node' without pinning a version makes execution depend on whatever package version is currently resolved from the registry or local environment. This weakens reproducibility and can expose users to supply-chain compromise or unexpected behavior if a malicious or incompatible version is installed.

Rp1

Medium
Category
MCP Rug Pull
Confidence
88% confidence
Finding
The second unpinned 'npx ts-node' usage has the same supply-chain and reproducibility problem: runtime behavior depends on external package resolution at execution time. In a skill that can manipulate wallets and send on-chain data, that execution ambiguity is especially risky.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The natural-language description is entirely in Chinese and provides no indication that users may choose another language or that the skill is region-specific. Under the policy criteria, forcing a specific language without opt-in is a locale/language policy concern.

Static analysis

No suspicious patterns detected.