Back to skill

Security audit

Openclaw Fomo3d

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to match its advertised blockchain game and trading purpose, but it handles real-wallet signing in ways that can put funds at risk.

Review carefully before installing. Use only a dedicated low-value wallet, prefer testnet first, avoid storing a private key in config.json, and do not use mainnet trading commands unless you understand that transactions are irreversible, approvals may be unlimited, and trades have no slippage protection.

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
src/commands/setup.ts:18
Finding
Wallet Private Key Is Collected with Echo and Stored in a Plaintext Configuration File<![CDATA[ ## Vulnerability Details **File Location**: `src/commands/setup.ts:18-32`; `src/lib/config.ts:39-45` **Vulnerability Type**: Plaintext sensitive-data storage and insecure secret entry **Risk Level**: High ### Vulnerable Code ```ts // src/commands/setup.ts:18-32 const currentKey = existing.privateKey ? `${existing.privateKey.slice(0, 6)}...${existing.privateKey.slice(-4)}` : "(not set)" log(`Current private key: ${currentKey}`) const keyInput = (await question(rl, "Private key (Enter to keep current): ")).trim() const privateKey = keyInput || existing.privateKey // 网络 log(`\nCurrent network: ${existing.network}`) const netInput = (await question(rl, "Network (testnet/mainnet, Enter to keep): ")).trim() const network = (netInput === "testnet" || netInput === "mainnet") ? netInput : existing.network // RPC URL log(`\nCurrent RPC URL: ${existing.rpcUrl ?? "(default)"}`) const rpcInput = (await question(rl, "Custom RPC URL (Enter for default): ")).trim() const rpcUrl = rpcInput || existing.rpcUrl const config = { privateKey, network, ...(rpcUrl ? { rpcUrl } : {}) } writeConfig(config) ``` ```ts // src/lib/config.ts:39-45 export function writeConfig(config: Partial<Config>): void { const existing = readConfigFile() ?? {} const merged = { ...existing, ...config } writeFileSync(CONFIG_PATH, JSON.stringify(merged, null, 2) + "\n") } ``` ### Technical Analysis The setup command accepts a raw blockchain private key through a normal `readline` prompt. Standard terminal input remains visible while the user types, allowing shoulder surfing, terminal recording, or session logging to capture the secret. The unencrypted private key is subsequently written into `config.json` at the project root. The write operation does not explicitly request owner-only permissions such as mode `0o600`, inspect the permissions of an existing file, or use an operating-system credential store. Its effective accessibility therefore depends on the process umask and existing ...[truncated 1479 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not store raw private keys in `config.json`. Prefer an operating-system keychain, hardware wallet, external signer, or encrypted Ethereum keystore protected by a separate passphrase. 2. Retain `FOMO3D_PRIVATE_KEY` only as an explicitly documented fallback for controlled environments, and warn users that environment variables may be exposed through process-management or diagnostic tooling. 3. Disable terminal echo while reading secrets, then restore terminal state reliably in a `finally` block. 4. If file-based storage must remain available: - create the file with mode `0o600`; - verify and reject unsafe permissions on existing files; - use atomic writes through a securely created temporary file; - never preserve an old plaintext key unintentionally. 5. Add `config.json` to `.gitignore` and provide a safe `config.example.json` containing no credentials. 6. Document wallet isolation and recommend a dedicated low-value gaming wallet rather than a wallet holding unrelated assets. 7. Validate private-key format before storage and avoid displaying any key fragments unless the user explicitly requests them. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
src/commands/buy.ts:46
Finding
Token Trades Execute Without Slippage Protection<![CDATA[ ## Vulnerability Details **File Location**: `src/commands/buy.ts:46-57`, `src/commands/buy.ts:78-87`, `src/commands/sell.ts:62-73`, and `src/commands/sell.ts:102-111` **Vulnerability Type**: Unsafe decentralized-exchange transaction parameters **Risk Level**: High ### Vulnerable Code ```ts // src/commands/buy.ts:46-57 const hash = await walletClient.writeContract({ address: portal, abi: PORTAL_ABI, functionName: "swapExactInput", args: [{ inputToken: NATIVE_BNB, outputToken: fomoToken, inputAmount: bnbAmount, minOutputAmount: 0n, permitData: "0x", }], value: bnbAmount, chain: walletClient.chain, }) ``` ```ts // src/commands/buy.ts:78-87 const hash = await walletClient.writeContract({ address: pancakeRouter, abi: PANCAKE_ROUTER_ABI, functionName: "swapExactETHForTokensSupportingFeeOnTransferTokens", args: [0n, [wbnb, fomoToken], account, deadline], value: bnbAmount, chain: walletClient.chain, }) ``` ```ts // src/commands/sell.ts:62-73 const hash = await walletClient.writeContract({ address: portal, abi: PORTAL_ABI, functionName: "swapExactInput", args: [{ inputToken: fomoToken, outputToken: NATIVE_BNB, inputAmount: tokenAmount, minOutputAmount: 0n, permitData: "0x", }], chain: walletClient.chain, }) ``` ```ts // src/commands/sell.ts:102-111 const hash = await walletClient.writeContract({ address: pancakeRouter, abi: PANCAKE_ROUTER_ABI, functionName: "swapExactTokensForETHSupportingFeeOnTransferTokens", args: [tokenAmount, 0n, [fomoToken, wbnb], account, deadline], chain: walletClient.chain, }) ``` ### Technical Analysis All supported buy and sell paths set the minimum acceptable output to zero. The Portal calls use `minOutputAmount: 0n`, while PancakeSwap calls pass `0n` as `amountOutMin`. A minimum output is the principal on-chain protection against execution after an adverse price change. Setting it to zero instructs the contract to accept any non-revertin ...[truncated 1524 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Obtain a current expected-output quote before constructing each transaction. 2. Require a configurable maximum slippage tolerance and calculate: ```text minimumOutput = quotedOutput × (10000 - slippageBps) / 10000 ``` 3. Pass the resulting nonzero value as `minOutputAmount` or `amountOutMin`. 4. Reject the transaction when a reliable quote cannot be obtained, liquidity is below a defined threshold, or price impact exceeds a safe limit. 5. Show the expected output, minimum output, exchange route, price impact, and slippage tolerance before requesting confirmation. 6. Use a conservative default tolerance and require explicit confirmation for unusually high tolerances. 7. Preserve short deadlines as a secondary control, but do not treat deadlines as a substitute for slippage limits. 8. Consider transaction simulation immediately before submission and private transaction submission where supported to reduce mempool-based sandwich exposure. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/lib/erc20.ts:25
Finding
Automatic ERC-20 Approval Grants Permanent Unlimited Allowance<![CDATA[ ## Vulnerability Details **File Location**: `src/lib/erc20.ts:25-37` **Vulnerability Type**: Excessive token spending permission **Risk Level**: Medium ### Vulnerable Code ```ts log(`Approving token spend...`) const hash = await walletClient.writeContract({ address: tokenAddress, abi: ERC20_ABI, functionName: "approve", args: [spenderAddress, maxUint256], account: walletClient.account!, chain: walletClient.chain, }) log(`Approve tx: ${hash}`) await publicClient.waitForTransactionReceipt({ hash }) log("Approve confirmed") ``` ### Technical Analysis `ensureAllowance` receives the exact `requiredAmount`, but when the current allowance is insufficient it approves `maxUint256` instead of that required amount. This grants the selected spender a practically unlimited and persistent authority to transfer the token from the wallet. The approval remains after the intended transaction completes and also applies to tokens deposited into the wallet later. This exceeds the minimum privileges necessary to perform a single purchase, sale, bet, deposit, proposal, dispute, or spin. An ERC-20 allowance does not itself expose the private key, but it authorizes the spender contract to call `transferFrom` within the approved amount. Consequently, a vulnerability, malicious upgrade, compromised administrator, incorrect spender address, or malicious contract behind an approved address can convert the standing allowance into direct token loss. ### Attack Path 1. The user invokes a command that requires an ERC-20 allowance. 2. The existing allowance is below the operation’s required amount. 3. The CLI automatically submits an approval for `maxUint256`. 4. The intended operation completes, but the unlimited allowance remains active. 5. The approved spender is subsequently compromised, maliciously upgraded, or exploited, or an approved address does not represent the contract the user expected. 6. The spender calls `transferFrom` against the user’s wallet. 7. ...[truncated 628 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Approve only the exact `requiredAmount` by default: ```ts args: [spenderAddress, requiredAmount] ``` 2. Where supported, use permit-based, transaction-scoped authorization instead of a persistent allowance. 3. Offer unlimited approval only as an explicit opt-in optimization after clearly explaining its risk. 4. Provide a command to enumerate and revoke allowances created by the Skill. 5. Consider resetting the allowance to zero after the dependent operation, while accounting for token compatibility and transaction costs. 6. Validate spender addresses against a network-specific allowlist and display the spender, token, and requested allowance before approval. 7. If changing a nonzero allowance for tokens with restrictive approval behavior, first approve zero and then approve the exact new amount. 8. Document that contract upgrades or compromises can abuse standing allowances even when the wallet private key remains secure. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (43)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description focuses on gameplay and market operations on BNB Chain: Fomo3D participation, slot spins, prediction markets, token trading, dividends, and wallet management. The supplied code does none of those things directly. Instead, it provides a separate faucet feature for testnet only, claiming FOMO tokens by POSTing the user's wallet address to a Supabase Edge Function. That is a materially different primary purpose and involves an external centralized resource not disclosed in the description. While a faucet could be adjacent to a testnet gaming ecosystem, it is still an undeclared capability and not covered by the stated functionality.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The description presents a broad gaming and trading skill covering Fomo3D, slot machine, prediction markets, token trading, and wallet operations. The supplied code chunk is much narrower: it is a CLI command for viewing token info and balances. It reads local config, may derive an address from a stored private key, queries token status from FLAP/Portal, gets ERC-20 and native BNB balances, and formats output. This is not inherently malicious, but it is materially different from the declared primary purpose because none of the advertised game actions or market operations appear here. Additionally, deriving an account from a configured private key is a capability touching sensitive wallet material that is not reflected in the declared permissions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared description centers on three gaming products: Fomo3D, a slot machine, and prediction markets, plus broad user actions around those products. The supplied code chunk instead implements low-level token trading/status infrastructure for a FLAP Portal and PancakeSwap on BNB Chain. It contains contract addresses, swap ABIs, token status definitions, and a query function for token market state. While the description briefly mentions trading on FLAP Portal or PancakeSwap, that is only one small part of the declared scope; this chunk does not implement the prominently declared game mechanics or related operations. Therefore the code's actual purpose is materially narrower and different from the declared primary purpose, so this should be flagged as a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description describes a full-featured BNB Chain gaming/trading skill with on-chain gameplay, market operations, token trading, and wallet actions. The actual code chunk is a small output helper module unrelated to those domain capabilities. It does not interact with BNB Chain, wallets, smart contracts, games, markets, or exchanges; it only manages console output and JSON formatting. While this could be a supporting utility within a larger skill, the supplied code chunk itself does not accurately represent the declared purpose, so this is a clear description-behavior mismatch for the provided code.

Missing User Warnings

High
Confidence
96% confidence
Finding
The skill asks users to configure a raw blockchain private key and supports real-funds transactions, but the warninging and consent language is not proportionate to that risk. In this context, insufficient disclosure is dangerous because users may expose signing credentials or authorize irreversible on-chain actions without understanding the consequences.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill requires sensitive environment access (`FOMO3D_PRIVATE_KEY`) and networked blockchain interactions but does not declare an explicit tool scope or permissions boundary. That omission increases the chance the agent invokes a high-risk skill without adequate user awareness or sandbox policy enforcement.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The invocation text is very broad and encourages use for many generic gaming, trading, and wallet-management requests. In a skill that can sign blockchain transactions with a private key, overbroad triggering materially raises the risk of accidental invocation and unintended fund-moving actions.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
Share amounts for `purchase --shares` are **integers** (not wei). 1 share = 1 share.

### Auto-Approve

The CLI automatically checks ERC20 token allowance and approves if needed before `purchase`, `buy`, `sell`, `slot spin`, `slot deposit`, `pred bet`, `pred propose`, and `pred dispute`. No manual approval step required.
Confidence
95% confidence
Finding
The skill states it will automatically issue ERC-20 approvals before multiple transactional commands. Automatic approval is dangerous in a blockchain context because allowances can authorize future token spending beyond the immediate action, and users may not see or approve the exact amount, spender, or duration.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
**Pre-checks performed automatically:**
- Game must not be paused
- No pending withdrawal (must `settle` first)
- Token approval (auto-approved if needed)

**Output fields:** `txHash`, `blockNumber`, `status`, `sharesAmount`, `tokensCost` (wei)
Confidence
94% confidence
Finding
Auto-approval during share purchases delegates token-spending authority as part of a user flow that also spends funds in a high-risk gambling application. Because approvals are a separate authorization step from purchase execution, hiding them behind automation can expose more value than the user intended if the spender contract is buggy, upgraded, or malicious.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- `--amount`: Sell exact token amount (in wei, 18 decimals)
- `--percent`: Sell by percentage of holdings in basis points (10000=100%, 5000=50%, 1000=10%)

Cannot use both flags simultaneously. Token allowance is auto-approved for Portal/PancakeSwap.

**Example:** Sell 50% of holdings:
```bash
Confidence
94% confidence
Finding
Automatic approval for sell flows is especially risky because it can grant DEX/router contracts token-spending rights without deliberate user review. In a real-funds trading skill, this increases the blast radius of contract compromise, wrong-token routing, or mistaken approvals.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
**Pre-checks performed automatically:**
- Market must be Active (not paused)
- Amount must be >= minBet
- Token approval (auto-approved if needed)

**Output fields:** `txHash`, `blockNumber`, `status`, `marketId`, `side`, `amount` (wei)
Confidence
93% confidence
Finding
Auto-approving token spending for prediction-market bets combines gambling behavior with silent authorization changes. Users may think they are placing a single bet while the skill first creates a broader allowance that could be reused by the contract or misused if the target contract is unsafe.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
```bash
fomo3d pred propose --id <marketId> --outcome <yes|no|draw> --json
```
Propose the outcome for an Optimistic-type market. Requires bond (auto-approved from token balance). Bond is returned if proposal is not disputed and is finalized.

**Output fields:** `txHash`, `blockNumber`, `status`, `marketId`, `outcome`, `bondAmount` (wei)
Confidence
92% confidence
Finding
Auto-approving bond transfers for optimistic proposals silently authorizes token movement in a dispute-resolution system where funds may be locked or forfeited. Although the amounts may be smaller than trading flows, hidden approvals still reduce user control over on-chain authorization.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
```bash
fomo3d pred dispute --id <marketId> --json
```
Challenge a proposed outcome within the challenge period. Requires same bond amount (auto-approved). If you win the dispute, you get your bond back + 50% of proposer's bond.

**Output fields:** `txHash`, `blockNumber`, `status`, `marketId`, `bondAmount` (wei)
Confidence
92% confidence
Finding
Auto-approval for disputes is risky because it authorizes token transfer for an adversarial process where the user may lose part of the bond. In this skill context, silent approvals are more dangerous because they are tied to speculative/gambling workflows and potentially irreversible outcomes.

Rp1

Medium
Category
MCP Rug Pull
Confidence
97% confidence
Finding
The shebang uses `#!/usr/bin/env npx tsx`, which causes execution to resolve and potentially download `tsx` at runtime rather than relying on a pinned, locally installed dependency. That creates a supply-chain risk: an attacker controlling dependency resolution, registry responses, or the execution environment could cause unreviewed code to run before the CLI logic, which is especially dangerous for a wallet-enabled blockchain tool.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
faucet                        Claim 10000 test FOMO tokens

GAME ACTIONS:
  purchase --shares <n>         Buy shares (auto-approves token)
  exit                          Exit game, claim dividends
  settle                        Settle after round ends + claim prize
  end-round                     End expired round
Confidence
89% confidence
Finding
The help text advertises that `purchase --shares <n>` 'auto-approves token', indicating the command may grant token allowance automatically as part of a purchase flow. In a blockchain wallet context, automatic approvals are security-sensitive because they can authorize future token movement without a clear, separate user consent step, and excessive or unlimited approvals can lead to fund loss if the spender contract is compromised or misconfigured.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The Portal buy path sends an irreversible on-chain transaction with `minOutputAmount: 0n`, which provides no slippage protection and can result in receiving far fewer tokens than expected if price movement, poor liquidity, or manipulation occurs before execution. In a wallet-managing trading skill, the absence of an explicit confirmation about finality and pricing risk materially increases the chance of accidental financial loss.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
The PancakeSwap path uses `swapExactETHForTokensSupportingFeeOnTransferTokens` with `amountOutMin` set to `0n`, meaning the trade will execute regardless of how few tokens are returned. This exposes users to severe value loss from slippage, frontrunning, sandwich attacks, or illiquid/malicious token pairs, and the transaction is final once broadcast.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The command falls back to loading a private key via `requirePrivateKey(config)` and constructing a wallet client, which accesses sensitive credentials. In this file there is no confirmation prompt, warning log, or explanatory comment disclosing that credential material may be used when `--address` is omitted.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This command automatically grants token allowance for the dispute bond and immediately submits an on-chain dispute transaction with no explicit user confirmation or dry-run step. In a wallet-managing blockchain skill, that creates a real risk of unintended token approval and irreversible fund expenditure if the user mistypes parameters, misunderstands the action, or the skill is invoked in an unexpected context.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
This command performs two state-changing blockchain actions in sequence: it may automatically approve token spending via ensureAllowance and then submits proposeOutcome using the user's private key, without any explicit confirmation prompt, dry-run, or final transaction summary in this file. In a wallet-managing gaming/betting skill, silent approval plus transaction execution is dangerous because a user can unintentionally authorize token movement and lock funds as a bond, especially if CLI arguments are mistaken, maliciously supplied, or socially engineered.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The command automatically derives the user's wallet address from the configured private key and then queries and displays wallet balances without an explicit opt-in or warning. In a blockchain gaming/trading skill, this can unintentionally disclose sensitive financial metadata in terminals, logs, screenshots, CI runs, or shared environments, even though it does not print the private key itself.

Missing User Warnings

Medium
Confidence
84% confidence
Finding
When no --address is provided, the command loads a private key via requirePrivateKey and derives the wallet address from it. Although this is part of wallet-related functionality, this file contains no user-facing disclosure, prompt, or explanatory comment that sensitive credential material will be accessed.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This code reads a sensitive credential via `process.env.FOMO3D_PRIVATE_KEY` and falls back to a config file value, but there is no user-facing log, prompt, or explanatory comment warning that a private key will be consumed. For code files, access to sensitive environment variables or credentials should have some visible disclosure unless clearly documented elsewhere.

Missing User Warnings

Medium
Confidence
85% confidence
Finding
writeConfig writes merged configuration directly to config.json, and the Config schema includes a privateKey field. In a wallet-managing blockchain skill, this can result in plaintext private key storage on disk, exposing funds if the file is read by other local users, malware, backups, or accidental commits.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The function automatically grants maxUint256 allowance whenever the current allowance is insufficient, creating an effectively unlimited approval to the spender. If the spender contract is compromised, malicious, upgradeable, or incorrectly configured, it can drain all present and future tokens of that type from the user's wallet without further consent; in this gaming/trading skill context, frequent token interactions make this more dangerous, not less.

Static analysis

No suspicious patterns detected.