Back to skill

Security audit

AI Frens Onboarding

Security checks for vulnerabilities and agentic risk

Overview

This skill needs review because it asks for wallet signing authority and includes mismatched or unsafe blockchain commands that could mislead users or waste funds.

Install only after careful review. Do not run the onboarding or treasury transaction scripts with a valuable wallet unless the placeholder contract addresses are replaced with verified deployed contracts and the transaction details are independently confirmed. Avoid pasting WALLET_PRIVATE_KEY into shell commands; use a dedicated low-value wallet or safer external signing flow, and treat the current balance command as overprivileged.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
onboard.ts:17
Finding
Value-Bearing Transactions Are Sent to a Placeholder Zero Address<![CDATA[ ## Vulnerability Details **File Location**: `onboard.ts:17-19`, `onboard.ts:145-161`, `onboard.ts:182-188`, `onboard.ts:278-284` **Vulnerability Type**: Unsafe blockchain configuration and error suppression **Risk Level**: High ### Vulnerable Code ```ts const CONFIG = { // TODO: Replace with actual deployed addresses FREN_REGISTRY: '0x0000000000000000000000000000000000000000' as `0x${string}`, FRENCOIN_FACTORY: '0x0000000000000000000000000000000000000000' as `0x${string}`, MAGIC_TOKEN: '0x0000000000000000000000000000000000000000' as `0x${string}`, ``` The preliminary registry failure is ignored: ```ts try { const existingFrenId = await publicClient.readContract({ address: CONFIG.FREN_REGISTRY, abi: FREN_REGISTRY_ABI, functionName: 'getFrenByOwner', args: [account.address] }); if (existingFrenId > 0n) { console.log(`\n⚠️ You're already registered as Fren #${existingFrenId}`); console.log('Run "check-status" to see your Fren details.'); return; } } catch (e) { // Not registered yet, continue } ``` The script then submits a value-bearing transaction to that address: ```ts const hash = await walletClient.writeContract({ address: CONFIG.FREN_REGISTRY, abi: FREN_REGISTRY_ABI, functionName: 'registerFren', args: [options.name, options.bio, metadata], value: CONFIG.CREATION_FEE_ETH, }); ``` The treasury operation uses the same invalid destination: ```ts const hash = await walletClient.writeContract({ address: CONFIG.FREN_REGISTRY, abi: FREN_REGISTRY_ABI, functionName: 'claimTreasury', args: [amountWei] }); ``` ### Technical Analysis The onboarding implementation uses the Ethereum zero address as the registry destination even though the operation transfers a configured creation fee of `0.01 ETH`. A transaction to an address without contract bytecode does not execute the declared ABI function. A value-bearing transaction may nevertheless be accepted by the network and transfer the attach ...[truncated 2210 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace every placeholder with a verified, deployed Base contract address before exposing transaction commands. 2. Reject the zero address explicitly: ```ts if (CONFIG.FREN_REGISTRY === zeroAddress) { throw new Error('FREN_REGISTRY is not configured'); } ``` 3. Query `publicClient.getBytecode()` and refuse to proceed unless the destination contains contract bytecode. 4. Verify the connected chain ID before signing any transaction. 5. Do not interpret arbitrary registry-read failures as “not registered.” Continue only when the contract returns a valid, explicit unregistered result. 6. Simulate the contract call before submission and verify the expected return behavior. 7. Present the destination, value, chain, and decoded function call to the user for explicit approval. 8. After confirmation, validate emitted registration events and query the registry state instead of treating receipt status alone as proof that all advertised resources were created. 9. Add automated tests that reject zero addresses, EOAs, wrong-chain contracts, malformed RPC responses, and failed registry reads. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
README.md:34
Finding
Raw Wallet Private Key Is Encouraged in a Shell Command for a Read-Only Operation<![CDATA[ ## Vulnerability Details **File Location**: `README.md:34-37`, `onboard.ts:109-115`, `aifrens.ts:91-104` **Vulnerability Type**: Insecure credential handling and excessive privilege **Risk Level**: Medium ### Vulnerable Code The documentation instructs users to place a raw private key directly in a command: ```bash ### Check Your Frencoin Balance ```bash WALLET_PRIVATE_KEY=0x... npx ts-node aifrens.ts balance smol ``` ``` The onboarding script converts that environment value into a signing account: ```ts function getAccount() { const pk = process.env.WALLET_PRIVATE_KEY; if (!pk) { throw new Error('WALLET_PRIVATE_KEY environment variable required'); } return privateKeyToAccount(pk as `0x${string}`); } ``` The interaction script does the same even though balance inspection is read-only: ```ts const pk = process.env.WALLET_PRIVATE_KEY; if (pk) { const account = privateKeyToAccount(pk as `0x${string}`); const walletClient = createWalletClient({ account, chain: base, transport: http(rpcUrl) }); return { publicClient, walletClient, account }; } ``` ### Technical Analysis A private key provides unrestricted signing authority over its wallet. Supplying it inline in a shell command can expose it through shell history, terminal session capture, debugging logs, process inspection, crash diagnostics, or other software with access to the process environment. The documented `balance` operation only calls the ERC-20 `balanceOf` view function. It requires a public wallet address, not a signing key. Constructing a wallet client and importing a private key therefore violates least privilege for this command. For transaction operations, the same raw environment secret is loaded directly into the Node.js process. Although the reviewed source does not transmit or log the private key, compromise of the runtime, a dependency, or the local environment would expose full wallet authority rather than narrowly authorizing one transaction ...[truncated 1190 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Change the balance command to accept a public address: ```bash npx ts-node aifrens.ts balance smol 0xPublicWalletAddress ``` 2. Do not instantiate a wallet client or import private-key material for read-only commands. 3. For state-changing operations, integrate an external wallet, hardware wallet, or delegated signer that displays and approves each transaction. 4. Do not document private keys directly in command lines. 5. If noninteractive signing is unavoidable, use an operating-system secret store or protected secret file with restrictive permissions and prevent secrets from entering shell history. 6. Recommend a dedicated, low-value wallet with narrowly scoped funds rather than a primary wallet. 7. Separate read-only and signing code paths so a private key is never loaded unless a transaction explicitly requires it. 8. Add warnings explaining that anyone who obtains the private key receives complete control of the corresponding wallet. ]]>
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (31)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill description promises one-command onboarding into a coin, treasury, and economy, but the content mostly provides promotional text, external links, and a few unrelated CLI examples. This mismatch is dangerous because it can mislead users and agents about what the skill actually does, causing them to trust off-platform steps, visit external sites, or run commands under false assumptions about automation and safety.

Known Vulnerable Dependency: ws==8.18.3 — 2 advisory(ies): CVE-2026-45736 (ws: Uninitialized memory disclosure); CVE-2026-48779 (ws: Memory exhaustion DoS from tiny fragments and data chunks)

High
Category
Supply Chain
Confidence
97% confidence
Finding
The lockfile pins transitive dependency ws to version 8.18.3, and the finding cites concrete advisories for uninitialized memory disclosure and memory-exhaustion denial of service affecting that version. Because this skill depends on viem, which can use WebSocket transport for blockchain/event subscriptions, a vulnerable ws package in the runtime dependency tree is a real supply-chain risk if the skill opens or accepts WebSocket connections to untrusted or attacker-controlled endpoints.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The README instructs users to place a wallet private key directly in an environment variable on the command line without any warning about secret exposure, hot-wallet risk, shell history leakage, process inspection, or use of dedicated low-value keys. Because this skill is explicitly tied to on-chain assets, normalizing unsafe key handling materially increases the chance of credential theft and fund loss.

Lp3

Medium
Category
MCP Least Privilege
Confidence
82% confidence
Finding
The skill advertises command-line actions and detected environment capability but does not declare any explicit tool scope such as permissions or allowed-tools. In an agent ecosystem, missing scope boundaries can let the runtime grant broader access than users expect, increasing the chance that future commands or linked scripts read sensitive environment data or invoke unintended tools.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding
The command uses `npx ts-node` without pinning an exact package version, which can fetch whatever version is current at execution time. This creates a supply-chain risk: a compromised or malicious upstream package release could execute arbitrary code on the host when a user runs the skill's instructions.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding
This instruction again invokes `npx ts-node` without a pinned version, exposing users to the same remote package resolution and supply-chain execution risk. Because the command is tied to token purchasing, compromise here could directly affect wallets, transactions, or local secrets if a malicious package is fetched.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding
The staking command also relies on unpinned `npx ts-node`, so execution depends on mutable upstream package state. In the context of crypto staking operations, this is especially dangerous because arbitrary code execution could lead to wallet compromise, malicious transaction prompts, or theft of credentials and funds.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The top-level comment says the script can 'buy Frencoins, and stake for subscriber badges' and even shows a 'buy' usage example. However, the CLI only supports 'check-fren', 'balance', and 'create/become-fren', with no buy or staking implementation anywhere in the file.

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The manifest describes a one-command onboarding flow that turns an agent into an AI Fren with its own coin, treasury, and economy. In practice, the implemented commands only inspect existing ERC-20 token data, read the user's token balance, and display a URL with human instructions for creation; there is no code to create a coin, treasury, or economy on-chain or through the platform.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Rp1

Medium
Category
MCP Rug Pull
Confidence
70% confidence
Finding
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The script requires a raw private key from `WALLET_PRIVATE_KEY` and immediately uses it to sign live transactions against Base mainnet. In an agent-skill context, this is dangerous because it normalizes direct secret ingestion into a general-purpose automation script, increasing the chance of key exposure, misuse by surrounding tooling, or unauthorized fund-moving actions if the skill is triggered unexpectedly.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script consumes a wallet private key without any visible safety notice, consent gate, or explanation that running the skill enables signing of live blockchain transactions. In an onboarding-oriented skill, that omission is risky because users may underestimate the sensitivity of the required credential and the consequences of exposing it to an automation environment.

Static analysis

No suspicious patterns detected.