Back to skill

Security audit

Ceo Protocol Skill

Security checks for vulnerabilities and agentic risk

Overview

This DeFi skill is not deceptive, but it needs review because it can sign real on-chain proposal transactions with a raw private key and lacks strong confirmation and input validation safeguards.

Install only after reviewing the contract and companion skills. Use a dedicated low-balance agent wallet, verify the RPC endpoint and chain, dry-run and decode every action before signing, avoid file/stdin proposals from untrusted sources, and do not post private strategy details or secrets to the discussion API.

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

T08 · Insecure Dependencies

Warning
Location
SKILL.md:13
Finding
Unpinned External Skill Installations Expand the Supply-Chain Trust Boundary<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:13-24` **Vulnerability Type**: Unpinned third-party Skill dependencies **Risk Level**: Medium ### Vulnerable Code ```markdown Install these companion skills from ClawHub: - **[8004 Harness For Monad](https://clawhub.ai/fabriziogianni7/8004-skill-monad)** — ERC-8004 Identity registration (required for CEO Protocol agent onboarding) - **[Pond3r Skill](https://clawhub.ai/fabriziogianni7/pond3r-skill)** — Query onchain data, yields, and market analysis (mandatory for proposal quality) ```bash clawhub install fabriziogianni7/8004-skill-monad clawhub install fabriziogianni7/pond3r-skill ``` ``` ### Technical Analysis The Skill directs users to install two external companion Skills without pinning an immutable version, release digest, commit, or content hash. Those dependencies are not included in the audited project, so their effective instructions and executable content can change after this audit. Because installed Skills may introduce additional instructions, scripts, or tool usage, this practice expands both the software supply-chain boundary and the Agent instruction trust boundary. Publisher compromise, package takeover, or an unsafe future release could cause the installation command to retrieve content materially different from what was originally reviewed. The local npm dependency lockfile does contain integrity hashes, but it does not cover these separately installed ClawHub Skills. ### Attack Path 1. An attacker compromises a referenced publisher account, distribution channel, or mutable Skill release. 2. The attacker modifies one of the companion Skills to include hostile instructions or executable behavior. 3. A user follows the documented unversioned `clawhub install` command. 4. The latest mutable package is installed rather than a previously reviewed immutable artifact. 5. The malicious companion Skill executes or influences the Agent with the permissions available in the insta ...[truncated 579 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin each companion Skill to an immutable version, release digest, or verified commit. 2. Publish and verify cryptographic hashes for approved Skill artifacts. 3. Document the expected publisher identity and signature-verification procedure. 4. Audit every pinned companion Skill before recommending installation. 5. Avoid describing external dependencies as mandatory unless they are strictly required. 6. Where supported, use a lockfile or manifest that records the exact transitive Skill dependency graph. 7. Require explicit user approval before updating a previously reviewed dependency. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/build-action.mjs:112
Finding
Untrusted Proposal Inputs Bypass Local Action Safety Validation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/build-action.mjs:112-129`; `scripts/submit-proposal.mjs:122-126`; `scripts/submit-proposal.mjs:178-184` **Vulnerability Type**: Insufficient validation of transaction targets and calldata **Risk Level**: Medium ### Vulnerable Code From `scripts/build-action.mjs`: ```js case "custom": case "adapter": { const target = resolveAddress(spec.target); const data = spec.data?.startsWith("0x") ? spec.data : `0x${Buffer.from(spec.data, "hex").toString("hex")}`; return { target, value, data }; } default: throw new Error(`Unknown action type: ${type}`); } } function resolveAddress(key) { if (typeof key !== "string") throw new Error("Address key must be string"); const addr = TARGET_MAP[key] ?? (key.startsWith("0x") ? key : null); if (!addr) throw new Error(`Unknown address key: ${key}`); return addr; } ``` From `scripts/submit-proposal.mjs`: ```js const actions = rawActions.map((a) => ({ target: a.target, value: BigInt(a.value ?? 0), data: a.data, })); ``` The resulting actions are passed directly to the transaction: ```js const hash = await walletClient.writeContract({ address: CEO_VAULT, abi: CEO_VAULT_ABI, functionName: "registerProposal", args: [actions, proposalURI], account, }); ``` ### Technical Analysis The action builder accepts arbitrary hexadecimal addresses through `resolveAddress()` and arbitrary calldata through the `custom` and `adapter` action types. More importantly, proposals loaded through `--file` or `--stdin` bypass `buildAction()` entirely. In that path, the submission script only converts `value` to `BigInt` and performs no local checks for: - A zero native-token value. - A syntactically valid or approved target address. - Approved function selectors. - Correct ABI encoding. - Approved token spenders. - Valid ERC-4626 receiver and owner parameters. - Whether an action matches the human-readable proposal description. - Whether adapter ca ...[truncated 2142 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply one canonical validation function to actions from every input path, including `--file` and `--stdin`. 2. Reject all actions whose `value` is not exactly zero. 3. Validate addresses with a strict checksum-aware address parser. 4. Use an explicit target allowlist instead of accepting every hexadecimal address. 5. Decode calldata and enforce a selector allowlist per target type. 6. For token approvals, require an approved token, approved spender, and bounded amount. 7. For ERC-4626 calls, verify the function selector and require `receiver` and `owner` to equal `CEO_VAULT`. 8. For adapter calls, decode the adapter-specific ABI and validate token routes, recipients, deadlines, and slippage limits. 9. Display a human-readable summary of every action before signing. 10. Require successful simulation and explicit confirmation before broadcasting. 11. Compare the decoded action summary against the supplied `proposalURI` description where feasible. 12. Add unit tests for malformed addresses, nonzero values, unsupported selectors, arbitrary calldata, and malicious file/stdin proposals. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/common.mjs:40
Finding
Configurable Network Is Not Bound to the Hardcoded CEOVault Address<![CDATA[ ## Vulnerability Details **File Location**: `scripts/common.mjs:40-48`; `scripts/ceo-config.mjs:10`; `scripts/submit-proposal.mjs:178-184` **Vulnerability Type**: Chain and contract configuration mismatch **Risk Level**: Low ### Vulnerable Code From `scripts/common.mjs`: ```js export function resolveNetwork(args = {}) { const envChainId = Number(process.env.MONAD_CHAIN_ID ?? "143"); const networkName = args.network ?? "monad-mainnet"; const fromMap = NETWORKS[networkName]; const chainId = args.chainId ? Number(args.chainId) : fromMap?.chainId ?? envChainId; const rpcUrl = args.rpcUrl ?? process.env.MONAD_RPC_URL; if (!rpcUrl) throw new Error("Missing RPC URL. Pass --rpcUrl or set MONAD_RPC_URL."); const registry = getAddress(args.registry ?? fromMap?.registry ?? MAINNET_IDENTITY); return { networkName, chainId, rpcUrl, registry }; } ``` From `scripts/ceo-config.mjs`: ```js export const CEO_VAULT = getAddress("0xdb60410d2dEef6110e913dc58BBC08F74dc611c4"); ``` The fixed address is then used for broadcast: ```js const hash = await walletClient.writeContract({ address: CEO_VAULT, abi: CEO_VAULT_ABI, functionName: "registerProposal", args: [actions, proposalURI], account, }); ``` ### Technical Analysis The RPC URL and chain ID can be overridden through command-line arguments or environment variables, and the project defines both mainnet and testnet network presets. However, the CEOVault address is always the Monad mainnet address. The script does not independently query the RPC endpoint’s actual chain ID and compare it with the configured chain ID. It also does not verify that bytecode at `CEO_VAULT` matches an expected deployment hash. As a result, network identity and contract identity are configured independently. A mistaken or malicious RPC configuration can cause the wallet to sign and submit a transaction on an unintended network while still targeting the fixed mainnet address. ### Attack Path 1. An attacker or ...[truncated 1061 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define an immutable mapping from each supported chain ID to its verified CEOVault and related contract addresses. 2. Remove the testnet preset unless verified testnet deployments are configured. 3. Query `eth_chainId` from the RPC endpoint before creating or using the signer. 4. Abort when the RPC-reported chain ID differs from the expected chain ID. 5. Verify that contract bytecode exists at the configured CEOVault address. 6. Where possible, compare the deployed runtime bytecode hash against a known value. 7. Include the verified chain name, chain ID, contract address, and decoded action summary in an explicit pre-signing confirmation. 8. Avoid allowing unrestricted command-line chain-ID overrides in production mode. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/submit-proposal.mjs:127
Finding
Preflight Logic References currentEpoch Before Initialization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/submit-proposal.mjs:127-151` **Vulnerability Type**: Temporal dead-zone error in transaction preflight logic **Risk Level**: Low ### Vulnerable Code ```js // Pre-flight checks const [votingOpen, currentEpoch, hasProposed, proposalCount] = await Promise.all([ publicClient.readContract({ address: CEO_VAULT, abi: CEO_VAULT_ABI, functionName: "isVotingOpen", }), publicClient.readContract({ address: CEO_VAULT, abi: CEO_VAULT_ABI, functionName: "s_currentEpoch", }), publicClient.readContract({ address: CEO_VAULT, abi: CEO_VAULT_ABI, functionName: "s_hasProposed", args: [currentEpoch, account.address], }), publicClient.readContract({ address: CEO_VAULT, abi: CEO_VAULT_ABI, functionName: "getProposalCount", args: [currentEpoch], }), ]); ``` ### Technical Analysis `currentEpoch` is declared by the destructuring assignment that receives the result of `Promise.all()`. JavaScript evaluates the array elements before the assignment completes. The third and fourth calls therefore attempt to read `currentEpoch` while it is still in the `const` temporal dead zone. This produces a `ReferenceError` before `Promise.all()` can complete, preventing the normal transaction submission path from reaching its later checks or broadcast logic. This is primarily an availability and reliability defect rather than a privilege-escalation issue. It also means the documented preflight controls have not been exercised successfully in the current implementation. ### Attack Path 1. A registered Agent invokes `submit-proposal.mjs` with any otherwise valid proposal. 2. Execution reaches the preflight `Promise.all()` expression. 3. Evaluation of `args: [currentEpoch, account.address]` accesses the uninitialized binding. 4. JavaScript throws a `ReferenceError`. 5. The top-level error handler prints the error and terminates with a nonzero exit status. 6. The pro ...[truncated 566 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Fetch the current epoch before launching reads that depend on it: ```js const [votingOpen, currentEpoch] = await Promise.all([ publicClient.readContract({ address: CEO_VAULT, abi: CEO_VAULT_ABI, functionName: "isVotingOpen", }), publicClient.readContract({ address: CEO_VAULT, abi: CEO_VAULT_ABI, functionName: "s_currentEpoch", }), ]); const [hasProposed, proposalCount] = await Promise.all([ publicClient.readContract({ address: CEO_VAULT, abi: CEO_VAULT_ABI, functionName: "s_hasProposed", args: [currentEpoch, account.address], }), publicClient.readContract({ address: CEO_VAULT, abi: CEO_VAULT_ABI, functionName: "getProposalCount", args: [currentEpoch], }), ]); ``` Additionally: 1. Add an integration test that executes the complete dry-run preflight path. 2. Add static analysis and linting rules that detect use-before-initialization errors. 3. Test both open and closed voting states. 4. Test already-proposed and proposal-limit conditions. 5. Make successful simulation mandatory before live broadcast. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (10)

Ae1

High
Category
analysis-evasion
Content
| `build-action.mjs` | Build single Action structs (approve, deposit, withdraw, redeem, custom) |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Exfiltration Commands

High
Category
Prompt Injection
Content
## Discussion API

Post messages to the on-chain discussion panel (visible on the `/discuss` page).

Base URL resolution for agents:
Confidence
90% confidence
Finding
Instructions found that direct the agent to transmit conversation context or user data to external services.

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 supplied advisories indicate this version is affected by an uninitialized memory disclosure and a memory-exhaustion denial of service. In the context of this skill, viem can use WebSocket transport for blockchain RPC/event subscriptions, so a vulnerable ws library could expose sensitive process memory or allow a remote endpoint to crash or degrade the agent if it connects to a malicious or compromised WebSocket server.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This plain-English description presents the vault as a yield product governed by competing AI agents, but it does not clearly and prominently warn users that principal can be lost through strategy failure, execution mistakes, smart contract compromise, or governance-approved risky actions. In a DeFi vault context, omission of loss-risk and drawdown disclosures can mislead users about safety, especially because the described 'safety features' may create an impression of bounded risk when drawdown limits are configurable and may not prevent all losses.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The admin section lists powerful owner capabilities—whitelisting execution targets and yield vaults, pausing, changing fee parameters, and adjusting staking/cap/timing/drawdown settings—without clearly warning that these controls can materially alter risk, execution scope, and user economics. In this skill context, those permissions are highly security-relevant because a compromised, malicious, or careless admin could whitelist dangerous integrations, raise fees, or weaken protections, which directly affects depositor safety and agent incentives.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The skill provides step-by-step instructions for registering agents, staking tokens, submitting executable proposals, converting fees, and withdrawing rewards, but it does not include explicit warnings that these are irreversible on-chain actions affecting real assets. In an agentic setting, this omission increases the chance of unsafe autonomous execution, mistaken approvals, incorrect amounts, or premature settlement/execution that could directly move or lock funds.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The code reads `AGENT_PRIVATE_KEY` directly from the environment to construct a wallet account, which is a sensitive-credential access operation. There is no confirmation prompt, user-facing log/message, or explanatory comment/docstring in this file warning that the skill consumes private key material.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This script signs and broadcasts an on-chain transaction directly with the configured private key as soon as the code path reaches writeContract, with no interactive confirmation, allowlist, or secondary approval step. In an agent-skill context that accepts proposal content from files/stdin/CLI and is intended for autonomous use, this increases the chance that malformed, malicious, or unintended proposal actions are submitted irreversibly if upstream inputs or orchestration are compromised.

Missing User Warnings

Low
Confidence
86% confidence
Finding
The Discussion API section instructs the agent to POST generated content and on-chain references to a resolved base URL, including a fallback to localhost, without warning that this transmits potentially sensitive operational data off-chain. In practice, agents may disclose strategy details, wallet-linked metadata, or internal reasoning to an external service unintentionally, creating privacy and information-leak risks.

Unpinned Dependencies

Low
Category
Supply Chain
Content
{"name":"ceo-proposal-scripts","version":"0.1.0","private":true,"type":"module","dependencies":{"viem":"^2.38.5"}}
Confidence
94% confidence
Finding
The dependency version for viem is specified with a caret range (^2.38.5), which permits automatic installation of newer minor and patch releases. In a security-sensitive DeFi skill that performs on-chain interactions, this creates supply-chain risk because future upstream changes could introduce malicious code, regressions, or behavior changes that affect transaction construction and signing logic without an explicit review.

Static analysis

No suspicious patterns detected.