skills/cosmos-scanner/SKILL.md
Use when the user wants to audit Cosmos SDK modules or CosmWasm smart contracts, scan IBC protocol interactions for relay, channel, or packet vulnerabilities, review Cosmos Go modules for state machine exploits, or analyze cross-chain message handling in the Cosmos ecosystem.
npx skillsauth add 0x-shashi/web3-audit-skills skills/cosmos-scannerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
4 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Security scanner for Cosmos SDK modules (Go), CosmWasm smart contracts (Rust/WASM), and Inter-Blockchain Communication (IBC) protocol interactions.
| Attribute | Value |
|-----------|-------|
| Chain | Cosmos Hub, Osmosis, Juno, Neutron, Injective, Sei, + 60 app-chains |
| Smart Contract Language | Rust (compiled to WASM) |
| SDK Language | Go (custom Cosmos SDK modules) |
| VM | CosmWasm (WASM-based) |
| Token Standard | CW20 (fungible), CW721 (NFT), CW1155 (multi-token) |
| Key Framework | cosmwasm-std, Cosmos SDK |
| Storage Model | Key-value store (binary prefixed maps) |
| Gas Model | Gas consumed per operation, configurable per chain |
| Category | Detection | Severity |
|----------|-----------|----------|
| Authorization | Missing info.sender check on Execute handlers | Critical |
| Authorization | Keeper function callable without proper auth (SDK modules) | Critical |
| Authorization | AuthZ overly broad grants enabling privilege escalation | High |
| IBC | Packet source/destination validation missing | Critical |
| IBC | Channel ordering assumption violation | High |
| IBC | Timeout and acknowledgement handling errors | High |
| State | Unbounded range() iteration (gas DoS) | High |
| State | Iterator invalidation during mutation | High |
| State | Storage key collision in multi-contract systems | Medium |
| Cross-Contract | SubMessage reply handler not checking msg_id | High |
| Cross-Contract | Bank send reentrancy via SubMessage reply | Medium |
| Math | Uint128/Uint256 overflow (panics in debug, wraps in release) | High |
| Math | Decimal256 precision loss in rate calculations | Medium |
| Governance | Parameter manipulation via governance proposal | Medium |
| Governance | Admin key not removable (centralization) | Medium |
| BlockHooks | BeginBlocker/EndBlocker gas consumption DoS (SDK modules) | High |
| Migration | migrate() entry point without access control | Critical |
┌────────────────────────────────────────────────┐
│ CosmWasm Contract Entry Points │
├────────────┬───────────┬───────────┬───────────┤
│ instantiate │ execute │ query │ migrate │
│ (once) │ (state) │ (read) │ (upgrade) │
└────────────┴───────────┴───────────┴───────────┘
│ │
State (Items, Maps, SnapshotMaps) SubMessages
│ │
Storage (KV Store) Reply Handler
| Entry Point | info.sender | State Access | Security Focus |
|-------------|---------------|--------------|----------------|
| instantiate | Contract deployer | Write | Set admin, validate config |
| execute | Transaction signer | Write | Primary attack surface |
| query | Not available | Read-only | Gas DoS, expensive computation |
| migrate | Contract admin | Write | Must check authorization |
| reply | Contract itself | Write | SubMessage result handling |
| sudo | Chain governance | Write | Privileged chain operations |
| Incident | Chain | Issue | Impact | |----------|-------|-------|--------| | Osmosis LP bug (2022) | Osmosis | Rounding error in LP share calculation | $5M at risk (white-hat) | | Juno whale governance (2022) | Juno | Governance used to confiscate tokens | Governance centralization debate | | Terra UST depeg (2022) | Terra | Algorithmic stablecoin design flaw | ~$40B market value lost | | Wormhole bridge (2022) | Multi | Guardian signature verification bypass | $320M stolen | | CW20 unlimited mint | Various | Missing admin check on mint execute | Token inflation | | IBC race condition | Various | Packet ordering assumption violated | Token duplication |
info.sender Check// VULNERABLE: Anyone can call
pub fn execute_withdraw(
deps: DepsMut,
_info: MessageInfo, // sender not checked!
amount: Uint128,
) -> Result<Response, ContractError> {
// Withdraws funds without checking who's requesting
let msg = BankMsg::Send {
to_address: "attacker".to_string(),
amount: vec![Coin { denom: "uatom".to_string(), amount }],
};
Ok(Response::new().add_message(msg))
}
// SAFE: Validates caller
pub fn execute_withdraw(
deps: DepsMut,
info: MessageInfo,
amount: Uint128,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
if info.sender != config.admin {
return Err(ContractError::Unauthorized {});
}
// Proceed with withdrawal...
}
// VULNERABLE: Iterates ALL entries — gas DoS as state grows
pub fn query_all_balances(deps: Deps) -> StdResult<Vec<(Addr, Uint128)>> {
let result: Vec<_> = BALANCES
.range(deps.storage, None, None, Order::Ascending)
.collect::<StdResult<Vec<_>>>()?; // Unbounded!
Ok(result)
}
// SAFE: Paginated with limit
pub fn query_balances(
deps: Deps,
start_after: Option<Addr>,
limit: Option<u32>,
) -> StdResult<Vec<(Addr, Uint128)>> {
let limit = limit.unwrap_or(30).min(100); // Cap at 100
let start = start_after.map(Bound::exclusive);
let result: Vec<_> = BALANCES
.range(deps.storage, start, None, Order::Ascending)
.take(limit as usize)
.collect::<StdResult<Vec<_>>>()?;
Ok(result)
}
// VULNERABLE: Reply handler doesn't check which SubMessage triggered it
#[entry_point]
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
// Assumes all replies are from the token transfer SubMessage
// But could be from any SubMessage!
handle_transfer_reply(deps, msg)
}
// SAFE: Checks reply ID
const TRANSFER_REPLY_ID: u64 = 1;
const MINT_REPLY_ID: u64 = 2;
#[entry_point]
pub fn reply(deps: DepsMut, _env: Env, msg: Reply) -> Result<Response, ContractError> {
match msg.id {
TRANSFER_REPLY_ID => handle_transfer_reply(deps, msg),
MINT_REPLY_ID => handle_mint_reply(deps, msg),
_ => Err(ContractError::UnknownReplyId { id: msg.id }),
}
}
Common Cosmos SDK, CosmWasm, and IBC error codes encountered during audits.
| Error Type | Error Name | Meaning |
|-----------|-----------|----------|
| StdError::NotFound | NotFound { kind } | Queried item not found in storage |
| StdError::InvalidBase64 | InvalidBase64 { msg } | Invalid base64 encoding in message |
| StdError::InvalidUtf8 | InvalidUtf8 { msg } | Invalid UTF-8 in string conversion |
| StdError::Overflow | Overflow { source } | Arithmetic overflow in Uint128/Uint256 operations |
| StdError::DivideByZero | DivideByZero { source } | Division by zero in math operation |
| StdError::ConversionOverflow | ConversionOverflow { source } | Type conversion exceeds target range |
| StdError::GenericErr | GenericErr { msg } | Catch-all error — check msg for specifics |
| Module | Codespace | Error Code | Meaning |
|--------|----------|-----------|----------|
| bank | bank | 5 | Insufficient funds for send |
| bank | bank | 8 | Send disabled for denom |
| staking | staking | 5 | Validator not found |
| staking | staking | 7 | Delegation not found |
| staking | staking | 12 | Insufficient shares for undelegation |
| auth | auth | 4 | Insufficient fee |
| auth | auth | 9 | Signature verification failed |
| gov | gov | 3 | Unknown proposal |
| gov | gov | 5 | Inactive proposal — voting period ended |
| Module | Error Code | Meaning |
|--------|-----------|----------|
| channel | 5 | Channel not found |
| channel | 11 | Packet already received — replay protection |
| channel | 17 | Packet timeout — message expired |
| connection | 5 | Connection not found |
| transfer | 3 | Invalid denomination trace |
| transfer | 6 | Receive disabled on this channel |
| client | 6 | Client state not found |
| client | 9 | Consensus state not found |
| Issue | Likely Cause | Solution |
|-------|-------------|----------|
| IBC message handling vulnerabilities missed | Scanner only checks contract logic, not IBC layer | Load resources/ibc-security.md and audit ibc_packet_receive / ibc_packet_ack handlers |
| CosmWasm reply handler issues not detected | Scanner doesn't follow submessage flow | Trace all SubMsg with ReplyOn::Success/ReplyOn::Error and match reply IDs |
| State machine exploit not flagged | Scanner checks individual messages, not sequences | Analyze multi-message transaction flows for state inconsistencies |
| Missing sudo handler audit | Scanner focuses on execute/query only | Check sudo() entry point — often used for privileged chain-level operations |
| Gas griefing not detected | Scanner doesn't model gas costs | Flag unbounded loops/iterations in execute and query handlers |
| Cross-contract call risks missed | Scanner doesn't trace inter-contract calls | Map all WasmMsg::Execute and WasmQuery::Smart calls to external contracts |
development
Systematically hunt for every variant of a discovered vulnerability across the entire codebase. Use when a bug is found and all instances of the same root cause pattern must be identified, or when performing variant analysis during competitive audits on Code4rena or Sherlock.
testing
Use when the user wants to audit TON smart contracts for security vulnerabilities, scan FunC or Tact contracts for message chain replay, bounce handling, or gas issues, review TON DeFi protocols for actor-model concurrency flaws, or analyze asynchronous message passing security.
tools
Analyze ERC20/ERC721/ERC1155 token implementations for non-standard behavior, fee-on-transfer mechanics, rebasing logic, blacklists, pausability, and integration risks. Use when reviewing protocols that interact with external tokens or implementing token-related features.
testing
Use when the user wants to audit Sui Move smart contracts, scan Sui-specific patterns including object ownership, shared objects, or dynamic fields, review Sui DeFi protocols for object model security issues, or analyze Sui-specific transaction and consensus patterns.