Generated banner for Cosmos Vulnerability Scanner
Generated logo for Cosmos Vulnerability Scanner

Cosmos Vulnerability Scanner

Trail of BitsOfficialPublished by Trail of Bits

  • Skill
  • Workflow
  • Security
  • CC-BY-SA-4.0
  • No issues found

From the author

Scans Cosmos SDK blockchain modules and CosmWasm contracts for consensus-critical vulnerabilities — chain halts, fund loss, state divergence. 25 core + 16 IBC + 10 EVM + 3 CosmWasm patterns. Use when auditing custom x/ modules, reviewing IBC integrations, or assessing pre-launch chain security. Updated for SDK v0.53.x.
— the description by Trail of Bits

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The Cosmos Vulnerability Scanner is an automated security auditing tool designed for Cosmos SDK-based blockchains. It performs a multi-phase analysis of custom modules and smart contracts to identify vulnerabilities that could lead to chain halts, state divergence, or loss of funds. The skill orchestrates parallel scanning agents that evaluate code against specific patterns related to core SDK logic, IBC integrations, EVM compatibility, and CosmWasm contracts. It emphasizes consensus-critical execution paths, ensuring that findings are relevant to the blockchain's stability and security.

What it does

  • Generates a technical inventory and threat model via a discovery phase.
  • Spawns parallel scanning agents to analyze code against predefined vulnerability patterns.
  • Filters findings to focus exclusively on consensus-critical execution paths.
  • Supports specialized scanning for IBC, EVM, and CosmWasm components.
  • Writes detailed vulnerability reports as individual markdown files.
  • Verifies that all required security patterns have been assessed.

When to use it

  • Auditing custom Cosmos SDK modules.
  • Reviewing CosmWasm smart contracts for security flaws.
  • Conducting pre-launch security assessments for Cosmos-based chains.
  • Investigating potential causes of chain halt incidents.

Advantages

  • Reduces manual audit time through parallelized scanning.
  • Focuses on high-impact consensus-critical bugs.
  • Provides structured, actionable findings for remediation.
  • Adapts to specific Cosmos SDK version requirements.

Usage examples

“Run a security scan on the current directory.”
The agent performs a discovery phase, identifies the platform and SDK version, executes parallel scans, and generates a report in the .bughunt_cosmos/ directory.
“Audit my custom x/ modules and save findings to /tmp/audit_results.”
The agent scans the codebase for consensus-critical vulnerabilities and outputs individual markdown files containing findings to the specified directory.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/trailofbits/cosmos-vulnerability-scanner/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip cosmos-vulnerability-scanner.zip -d .agents/skills

Documentation by Trail of Bits

Cosmos Vulnerability Scanner

Purpose

Scan Cosmos SDK modules and CosmWasm contracts for vulnerabilities that cause chain halts, consensus failures, or fund loss. Spawns parallel scanning agents — each specializing in a vulnerability category — that return findings to the main skill, which then writes them as individual markdown files to an output directory.

Output directory: defaults to .bughunt_cosmos/. If the user specifies a different directory in their prompt, use that instead.

When to Use

  • Auditing Cosmos SDK modules (custom x/ modules)

  • Reviewing CosmWasm smart contracts

  • Pre-launch security assessment of Cosmos chains

  • Investigating chain halt incidents

When NOT to Use

  • Pure Solidity/EVM audits without Cosmos SDK — use Solidity-specific tools

  • CometBFT consensus engine internals — this covers SDK modules, not the consensus layer itself

  • General Go code review with no blockchain context

  • Cosmos SDK application logic that is not consensus-critical (e.g., CLI commands, REST endpoints)

  • CosmWasm contract-only audits on chains without custom SDK modules — use the CosmWasm checklist items alone

Essential Principles

  1. Consensus path is king — A bug only matters for chain halt/fund loss if it's on the consensus-critical execution path (BeginBlock, EndBlock, FinalizeBlock, msg_server handlers, AnteHandler). Always verify a finding is reachable from consensus before reporting it.

  2. State divergence = chain halt — Any non-determinism that causes validators to compute different state roots will halt the chain. This is the highest-severity class because it affects all validators simultaneously.

  3. Check the version — Cosmos SDK has breaking changes across major versions (v0.47 removed GetSigners, v0.50 added ABCI 2.0, v0.53 deprecated ValidateBasic). Always check go.mod versions before applying patterns.

  4. False positives waste audit time — A map iteration in a CLI command is not a consensus bug. A panic in a query handler does not halt the chain. Verify the execution context before flagging.

  5. Cross-module interactions are where bugs hide — The most severe findings (IBC reentrancy, EVM/Cosmos state desync, authz escalation) involve interactions between modules, not bugs within a single module.

Scanning Workflow

Phase 1: Discovery (synchronous)

Entry: Target codebase path provided by user. Codebase contains Go source (e.g., x/ modules, go.mod) or Rust contracts with cosmwasm_std.

Run a synchronous subagent (Agent tool) with the full contents of DISCOVERY.md as its prompt. The agent must:

  1. Follow the Discovery workflow to explore the target codebase

  2. Return the full CLAUDE.md content (the technical inventory and threat model) in its response

  3. Return a structured summary with exactly these fields:

PLATFORM: pure-cosmos | evm | wasm        (pick one; if multiple, comma-separated)
IBC_ENABLED: true | false
SDK_VERSION: <version from go.mod>
IBC_GO_VERSION: <version from go.mod, or "n/a">
CUSTOM_MODULES: <comma-separated list of x/* modules>

After the subagent returns, you (the main skill) Write the CLAUDE.md to the target repo root. Save its path and the discovery values — these feed into Phase 2.

Exit: CLAUDE.md written by main skill. PLATFORM, IBC_ENABLED, SDK_VERSION, IBC_GO_VERSION, and CUSTOM_MODULES captured.

Phase 2: Parallel Vulnerability Scan

Spawn scanning agents in a single message for maximum parallelism. Use the Agent Prompt Template below, filling in the reference file for each agent. Subagents only need read access (Grep, Glob, Read) — they return findings in their response and the main skill writes the files.

Always spawn these 3 agents:

Agent NameReference FileScope
core-scannerVULNERABILITY_PATTERNS.md§1-9: non-determinism, ABCI, signers, validation, handlers, ante security
state-scannerSTATE_VULNERABILITY_PATTERNS.md§11-23: bookkeeping, bank, pagination, events, tx replay, governance, arithmetic, encoding, deprecated modules
advanced-scannerADVANCED_VULNERABILITY_PATTERNS.md§24-27: storage keys, consensus validation, circuit breaker, crypto

Spawn conditionally (in the same parallel message):

Agent NameConditionReference File
evm-scannerPLATFORM includes evmEVM_VULNERABILITY_PATTERNS.md
ibc-scannerIBC_ENABLED is trueIBC_VULNERABILITY_PATTERNS.md
cosmwasm-scannerPLATFORM includes wasmCOSMWASM_VULNERABILITY_PATTERNS.md
Agent Prompt Template

Construct each agent's prompt by replacing {REFERENCE_FILE_PATH} with the full path to the reference file (under {baseDir}/resources/) and {CLAUDE_MD_PATH} with the path to the CLAUDE.md written in Phase 1:

Perform a very thorough security scan of a Cosmos SDK codebase for specific vulnerability patterns.

CONTEXT:
Read {CLAUDE_MD_PATH} for codebase context (SDK version, modules, threat model, key files).

PATTERNS:
Read {REFERENCE_FILE_PATH} — it contains numbered vulnerability patterns. For EACH pattern:
1. Read the detection patterns and "What to Check" items
2. Use Grep and Glob to search the target codebase for each pattern
3. When a match is found, Read surrounding code to verify it's on a consensus-critical path (BeginBlock, EndBlock, FinalizeBlock, msg_server handlers, AnteHandler)
4. Classify severity per the guidelines below

RULES:
- Consensus path only: Only flag code reachable from consensus-critical execution. CLI/query/test code is NOT a finding.
- Check SDK version in go.mod before applying patterns (v0.47 removed GetSigners, v0.50 added ABCI 2.0, v0.53 deprecated ValidateBasic).
- Always use the Grep tool for searches, not bash grep. The reference file contains search patterns — use them directly with the Grep tool.
- Ignore cross-references to other resource files (e.g., links to IBC or COSMWASM patterns). Those patterns are covered by other scanning agents.
- Reject these rationalizations:
  - "ValidateBasic catches this" — deprecated and facultative since SDK v0.53
  - "Behind governance, so safe" — governance proposals can be malicious
  - "IBC counterparty is trusted" — any chain can open a channel
  - "Panic can't happen, input is validated" — trace the full call chain
  - "Rounding error is only a few tokens" — compounds over time, can be looped
  - "EVM precompile handles rollback" — many have incomplete rollback

SEVERITY:
- Critical (fund loss): signer mismatch, broken bookkeeping, AnteHandler bypass, bank keeper misuse, IBC token inflation, EVM/Cosmos desync, Merkle proof forgery, arithmetic overflow
- High (chain halt): non-determinism, ABCI panics, slow ABCI, non-deterministic IBC acks, consensus gaps, CacheContext event leak
- Medium (DoS): unbounded pagination, tx replay, missing validation, governance spam, rate limiting, circuit breaker bypass, storage key collisions
- Low (logic): rounding errors, stub handlers, event override, module ordering

OUTPUT — RETURN FORMAT:
Do NOT write any files. Return ALL findings and the summary in your response.

For each pattern, return one of:
  §NUM PATTERN_NAME: Not applicable — [one-line reason]
  §NUM PATTERN_NAME: FINDING (followed by the finding block below)

For each finding, include the full content using this template:

FINDING_FILE: {SEVERITY}-s{SECTION_NUM}-{kebab-description}.md
## [SEVERITY] Title
**Location**: `file:line`
**Description**: What the bug is and why it matters
**Vulnerable Code**: [snippet]
**Attack Scenario**: [numbered steps]
**Recommendation**: How to fix
**References**: [links to relevant advisories or building-secure-contracts]

You MUST report on ALL patterns in the reference file — do not skip any.

Exit: All scanning agents returned. Each reported on every pattern in their reference file.

Phase 3: Write Findings

After all scanning agents return, write finding files to the output directory (default .bughunt_cosmos/):

  1. Parse each agent's response for FINDING_FILE: blocks

  2. For each finding, Write the content to {OUTPUT_DIR}/{filename} using the filename from FINDING_FILE:

  3. Create the output directory first if it doesn't exist

Phase 4: Verify Completeness

After writing all findings, verify every pattern was assessed:

  1. Collect the summary lines (§NUM entries) returned by each agent

  2. Check pattern counts against expected totals:

    • core-scanner: 8 patterns (§1-9, excluding §8 legacy-only)

    • state-scanner: 13 patterns (§11-23)

    • advanced-scanner: 4 patterns (§24-27)

    • evm-scanner (if spawned): 10 patterns (§1-10)

    • ibc-scanner (if spawned): 16 patterns (§1-16)

    • cosmwasm-scanner (if spawned): 3 patterns (§1-3)

  3. If any pattern is missing from a summary, flag it and re-prompt that agent

  4. List all finding files written to the output directory with a Glob for *.md

Exit: All patterns accounted for. Finding files listed for the user.


Success Criteria

  • [ ] Discovery CLAUDE.md written with complete technical inventory and threat model

  • [ ] All scanning agents completed and reported on every pattern in their reference file

  • [ ] Pattern counts verified against expected totals (no patterns skipped)

  • [ ] All findings written to output directory as individual markdown files

  • [ ] Each finding file includes: severity, location, vulnerable code, attack scenario, recommendation


Resources

Files

resources/STATE_VULNERABILITY_PATTERNS.md

State & Module Vulnerability Patterns

Updated for Cosmos SDK v0.53.x. State management, module interaction, and data handling patterns (§11-23) covering bookkeeping, bank keeper, pagination, events, transaction replay, governance, arithmetic, encoding, and deprecated modules. Referenced by SKILL.md checklist items via § anchors.

11 BROKEN BOOKKEEPING

Description: Custom internal accounting alongside x/bank module becomes inconsistent when direct token transfers bypass internal bookkeeping.

Detection Patterns:

// VULNERABLE: Internal bookkeeping separate from x/bank
type Keeper struct {
    // Internal tracking of balances
    userBalances map[string]sdk.Coins  // NOT synchronized with x/bank!
}

func (k Keeper) Deposit(ctx context.Context, user string, amount sdk.Coins) {
    // Updates internal bookkeeping
    k.userBalances[user] = k.userBalances[user].Add(amount...)

    // Also updates x/bank
    k.bankKeeper.SendCoins(ctx, sender, moduleAccount, amount)
}

// PROBLEM: Direct IBC transfer bypasses internal bookkeeping!
// User receives tokens via IBC -> x/bank updated but userBalances not updated
// Invariant violated: sum(userBalances) != bankKeeper.GetSupply()

What to Check:

  • [ ] No custom balance tracking alongside x/bank

  • [ ] OR custom tracking uses blocklist to prevent unexpected transfers

  • [ ] Invariant checks compare internal accounting to x/bank

  • [ ] Module accounts use SendEnabled parameter

Mitigation:

// OPTION 1: Use blocklist to prevent unexpected transfers
func (k Keeper) BeforeTokenTransfer(ctx context.Context, from, to string) error {
    if !k.IsAuthorizedTransfer(ctx, from, to) {
        return errors.New("direct transfers blocked")
    }
    return nil
}

// OPTION 2: Use SendEnabled parameter
// In x/bank params, set SendEnabled = false for your token

// OPTION 3: Don't maintain separate bookkeeping
// Use x/bank as source of truth, query when needed
func (k Keeper) GetUserBalance(ctx context.Context, user string) sdk.Coins {
    addr := sdk.AccAddress(user)
    return k.bankKeeper.GetAllBalances(ctx, addr)
}

Invariant Testing:

func (k Keeper) InvariantCheck(ctx sdk.Context) error {
    internalTotal := sdk.NewCoins()
    k.IterateBalances(ctx, func(addr string, balance sdk.Coins) bool {
        internalTotal = internalTotal.Add(balance...)
        return false
    })

    moduleBalance := k.bankKeeper.GetAllBalances(ctx, k.moduleAccount)
    if !internalTotal.Equal(moduleBalance) {
        return fmt.Errorf("bookkeeping mismatch: internal=%v bank=%v",
            internalTotal, moduleBalance)
    }
    return nil
}

References: building-secure-contracts/not-so-smart-contracts/cosmos/broken_bookkeeping


12 BANK KEEPER & TOKEN SECURITY

Description: Any module with access to x/bank's keeper can mint, burn, and transfer tokens at will. The bank module provides blocklist, SendEnabled, and SendRestriction mechanisms to control this, but they must be configured correctly.

What to Check:

  • [ ] Modules with bank keeper access: all methods are fully exposed — review every usage for unauthorized mint/burn/transfer

  • [ ] Blocklist prevents unexpected transfers to module accounts

    • Addresses blocked during startup via bank keeper's argument. Default blocked addresses come from maccPerms via BlockedAddresses()

    • x/gov module address explicitly removed from blocklist (receives proposal deposits) — verify custom chains make the same intentional exclusions

    • Blocklist explicitly checked whenever new transfer functionality is implemented

  • [ ] SendEnabled prevents unexpected transfers of specific denominations. DefaultSendEnabled is true by default

  • [ ] SendRestriction functions (AppendSendRestriction, PrependSendRestriction) reviewed if used. Order matters (Prepend vs Append). ClearSendRestriction removes ALL restrictions. Since v0.53.0, SendCoins checks restrictions BEFORE deducting coins (#24106, #24053)

  • [ ] SpendableCoins used instead of GetBalance for accounts with locked/vesting funds

  • [ ] Deflationary tokens considered in transfer accounting

  • [ ] If x/mint uses custom MintFn (via keeper.WithMintFn), no unbounded minting or inflation bypass. Old InflationCalculationFn must be nil

References: Desmos blocklist vulnerability


13 UNBOUNDED PAGINATION / QUERY DoS

Description: gRPC query endpoints using types/query.Paginate accept a Limit field from clients with no enforced maximum. A malicious client can set Limit: math.MaxUint64, forcing the node to iterate over the entire store in a single request, causing OOM or extended blocking. The SDK's DefaultLimit is 100, but it is only applied when Limit == 0 — any non-zero value is accepted as-is. Recent fix d9d77304fd addressed a related issue in x/auth/tx and x/bank genesis, but the core types/query/pagination.go still lacks a hard cap.

Detection Patterns:

// VULNERABLE: No max limit enforcement
func (k Keeper) AllRecords(ctx context.Context, req *types.QueryAllRecordsRequest) (*types.QueryAllRecordsResponse, error) {
    store := k.storeService.OpenKVStore(ctx)
    // Client controls req.Pagination.Limit with no upper bound!
    results, pageRes, err := query.CollectionPaginate(ctx, k.Records, req.Pagination,
        func(key string, value types.Record) (types.Record, error) {
            return value, nil
        },
    )
    // ...
}

What to Check:

  • [ ] All gRPC query handlers enforce a maximum pagination limit

  • [ ] Custom query endpoints that iterate stores have bounded iteration

  • [ ] CountTotal = true requests are bounded (counting all records is expensive)

  • [ ] Genesis export with large state is tested

Mitigation:

// SECURE: Enforce maximum pagination limit
const MaxPaginationLimit = 1000

func sanitizePagination(req *query.PageRequest) *query.PageRequest {
    if req == nil {
        req = &query.PageRequest{}
    }
    if req.Limit == 0 || req.Limit > MaxPaginationLimit {
        req.Limit = MaxPaginationLimit
    }
    return req
}

func (k Keeper) AllRecords(ctx context.Context, req *types.QueryAllRecordsRequest) (*types.QueryAllRecordsResponse, error) {
    req.Pagination = sanitizePagination(req.Pagination)
    // ...
}

14 EVENT OVERRIDE / SUPPRESSION

Description: Cosmos SDK v0.53 introduced EventManager.OverrideEvents() which replaces all previously emitted events with a new set. If called in module code (especially in message handlers or ABCI hooks), it can silently suppress events emitted by earlier modules in the same block or by the SDK framework itself. This can break indexers, IBC relayers, and monitoring tools that depend on events.

Detection Patterns:

// VULNERABLE: Overriding events suppresses earlier module events
func (ms msgServer) Execute(ctx context.Context, msg *types.MsgExecute) (*types.MsgExecuteResponse, error) {
    sdkCtx := sdk.UnwrapSDKContext(ctx)
    // This wipes ALL events emitted so far in this transaction/block!
    sdkCtx.EventManager().OverrideEvents(sdk.Events{myEvent})
    // Events from auth, bank, etc. are now gone
}

What to Check:

  • [ ] No OverrideEvents() calls in module message handlers or ABCI methods

  • [ ] If OverrideEvents() is used, verify it only operates on a child context's EventManager, not the parent

  • [ ] Events from prior modules are not silently dropped

  • [ ] IBC relayer and indexer compatibility verified after event changes

Mitigation:

// SECURE: Use EmitTypedEvent to add events, never OverrideEvents
func (ms msgServer) Execute(ctx context.Context, msg *types.MsgExecute) (*types.MsgExecuteResponse, error) {
    sdkCtx := sdk.UnwrapSDKContext(ctx)
    sdkCtx.EventManager().EmitTypedEvent(&types.EventExecute{...})
    // Preserves all existing events
}

15 UNORDERED TRANSACTION REPLAY

Description: Cosmos SDK v0.53 supports unordered transactions (TxBody.Unordered = true) with timeout-timestamp-based replay protection instead of sequence numbers. These transactions use nonce tracking in x/auth with a PreBlocker that cleans up expired nonces. Vulnerabilities arise from: (1) insufficient timeout duration allowing replay after nonce cleanup, (2) chains that don't enable the UnorderedTxDecorator or set maxTxTimeoutDuration too high, (3) nonce storage DoS by flooding with unique unordered transactions.

Detection Patterns:

// VULNERABLE: Unordered tx support enabled without proper timeout limits
func NewAnteHandler(options HandlerOptions) sdk.AnteHandler {
    decorators := []sdk.AnteDecorator{
        ante.NewSigVerificationDecorator(
            options.AccountKeeper,
            options.SignModeHandler,
            // Missing: WithMaxUnorderedTxTimeoutDuration
            // Missing: WithUnorderedTxGasCost
        ),
    }
}

// VULNERABLE: Application logic assumes sequence-based ordering
func (k Keeper) ProcessUserAction(ctx context.Context, msg *types.MsgAction) error {
    // With unordered txs, multiple actions from the same sender
    // can execute in any order within the same block!
    lastAction := k.GetLastAction(ctx, msg.Sender)
    // This ordering assumption breaks with unordered txs
}

What to Check:

  • [ ] WithMaxUnorderedTxTimeoutDuration is set to a reasonable value (default: 10 minutes)

  • [ ] WithUnorderedTxGasCost is configured (default: 2240 gas) to prevent nonce flooding

  • [ ] Application logic doesn't assume transaction ordering within a block

  • [ ] PreBlocker includes RemoveExpiredUnorderedNonces cleanup

  • [ ] State-dependent operations handle concurrent unordered transactions safely

Mitigation:

// SECURE: Configure unordered tx parameters
ante.NewSigVerificationDecorator(
    options.AccountKeeper,
    options.SignModeHandler,
    ante.WithMaxUnorderedTxTimeoutDuration(10 * time.Minute),
    ante.WithUnorderedTxGasCost(2240),
)

// SECURE: Don't rely on transaction ordering
func (k Keeper) ProcessUserAction(ctx context.Context, msg *types.MsgAction) error {
    // Use idempotent operations or explicit ordering fields
    // instead of relying on sequence-based ordering
}

16 MESSAGE PRIORITY / PROPOSAL ORDERING

Description: Missing prioritization of critical messages (oracle updates, emergency pause, governance) allows front-running and censorship during network congestion. In Cosmos SDK v0.50+ with ABCI 2.0, the block proposer controls transaction ordering via PrepareProposal and ProcessProposal, making this even more important.

Detection Patterns:

// VULNERABLE: No priority for critical oracle update in CheckTx
func (app *App) CheckTx(req *abci.CheckTxRequest) (*abci.CheckTxResponse, error) {
    // All messages treated equally, oracle updates can be delayed
    return app.BaseApp.CheckTx(req)
}

// VULNERABLE: PrepareProposal does not prioritize critical messages
func (app *App) PrepareProposal(req *abci.PrepareProposalRequest) (*abci.PrepareProposalResponse, error) {
    // Default ordering - no special treatment for emergency messages
    return app.BaseApp.PrepareProposal(req)
}

What to Check:

  • [ ] Oracle/price feed updates have high priority

  • [ ] Emergency pause/circuit breaker messages prioritized

  • [ ] Critical governance proposals prioritized

  • [ ] PrepareProposal considers message priority when ordering transactions

  • [ ] ProcessProposal validates that critical messages are not censored

  • [ ] High fees required for priority transactions (prevent spam)

Mitigation:

// SECURE: Custom PrepareProposal that prioritizes critical messages
func (app *App) PrepareProposal(req *abci.PrepareProposalRequest) (*abci.PrepareProposalResponse, error) {
    // Sort transactions by priority, putting critical messages first
    prioritized := prioritizeTxs(req.Txs, req.MaxTxBytes)
    return &abci.PrepareProposalResponse{Txs: prioritized}, nil
}

func prioritizeTxs(txs [][]byte, maxBytes int64) [][]byte {
    // Decode and classify transactions
    // Place oracle updates and emergency messages first
    // Then normal transactions by fee
    // ...
}

References: building-secure-contracts/not-so-smart-contracts/cosmos/messages_priority


17 GOVERNANCE SECURITY

Description: Governance mechanisms can be abused for spam, manipulation, or fund locking.

What to Check:

  • [ ] If MsgSubmitProposal is registered, submission cost is non-zero. DefaultMinInitialDepositRatio is zero (x/gov/types/v1/params.go:27) — proposals can be submitted with zero deposit by default, creating spam risk. Check both MinDeposit AND MinInitialDepositRatio

  • [ ] If x/gov uses custom CalculateVoteResultsAndVotingPowerFn (via WithCustomCalculateVoteResultsAndVotingPowerFn), verify the tally function correctly weights votes and handles edge cases (zero voting power, abstentions, validator vs delegator votes). A buggy tally function could manipulate governance outcomes

  • [ ] If x/circuit module is enabled, the circuit breaker authority is reviewed — a compromised authority (or captured governance) can disable critical messages (e.g., unbonding, slashing), effectively locking user funds or preventing security-critical operations


18 MODULE-SPECIFIC CHECKS

Description: Known vulnerabilities, configuration pitfalls, and behavioral changes in specific SDK modules.

What to Check:

x/distribution:

  • [ ] Historical rewards bounded — overflow can halt chain (GHSA-p22h-3m2v-cmgh, fixed v0.53.3). Verify incrementReferenceCount and reward accumulation on older versions/forks

x/staking:

  • [ ] Delegator slashing bypass fixed (GHSA-86h5-xcpx-cfqc, fixed v0.50.5) — verify slashing applies to all delegations including those created between infraction and slash execution

  • [ ] Panics replaced with error returns in v0.53.0 (#24391) — custom code catching panics via recover() must handle returned errors instead

baseapp:

  • [ ] If ValidateVoteExtensions helper is used, verify chain runs >= v0.50.5. Original had bug (GHSA-95rx-m9m5-m94v) where currentHeight/chainID args were misused. In fixed version they're ignored (read from context). In v0.51+ they're removed

  • [ ] If DisableBlockGasMeter is enabled (baseapp.DisableBlockGasMeter()), per-block gas limits removed — only CometBFT's max_bytes applies. Removes DoS protection layer; only use if gas metered externally

  • [ ] If Optimistic Execution enabled (baseapp/oe/), FinalizeBlock logic must not have side effects outside SDK state (external API calls, file writes) — speculative execution of rejected blocks cannot be rolled back

  • [ ] SetCheckTxHandler can bypass standard ante handler validation if misused

  • [ ] Custom PrepareProposal handlers must be deterministic in gas consumption and ordering. ProcessProposal rejections can cause consensus failures. Custom SignerExtractionAdapter must extract signers faithfully

x/epochs:

  • [ ] Epoch hooks (BeforeEpochStart, AfterEpochEnd) must not panic and must handle errors gracefully. InvokeSetHooks had a bug fixed in v0.53.2 (#24770). CurrentEpochStartHeight semantics fixed in v0.53.0 (#24610)

x/protocolpool:

  • [ ] If used alongside x/distribution, verify CommunityPoolSpend/FundCommunityPool/CommunityPool query handlers are NOT called (they return errors). Funds can get stuck if migration from distribution's community pool is incomplete

ABCIListener / StreamingManager:

  • [ ] If SetStreamingManager is used, listeners must not panic (halts chain), must not be excessively slow (synchronous during block processing), and must tolerate rejected blocks — especially with Optimistic Execution

Other:


19 MODULE INITIALIZATION ORDERING

Description: Cosmos SDK modules must be initialized in a specific order. Incorrect ordering can cause panics during genesis, missing state, or security bypasses.

What to Check:

  • [ ] SetOrderPreBlockers: upgrade module must be first. auth must also be in PreBlockers (required for unordered tx cleanup via RemoveExpiredUnorderedNonces)

  • [ ] SetOrderBeginBlockers / SetOrderEndBlockers: x/epochs is in BeginBlockers and fires epoch hooks — modules depending on epoch events must come after epochs. Note: capability module has been removed from cosmos-sdk (moved to ibc-go)

  • [ ] SetOrderInitGenesis: genutils after staking (pool initialization); genutils after auth (param access)

  • [ ] SetOrderExportGenesis: protocolpool must be exported before bank

  • [ ] Keepers passed to other keepers are initialized. Suspicious pattern: keeper initialized → uninitialized copy passed to another → first one assigned to app.TheKeeper


20 ARITHMETIC ERRORS

Description: Arithmetic bugs in financially-critical code — rounding errors, wrapping overflows, wrong operand selection, and missing sign/bound validation. Any deviation directly translates to fund loss. math.LegacyDec (formerly sdk.Dec) has precision issues and lacks associativity.

Detection Patterns:

Pattern 1: Rounding Errors (Precision Loss)
// VULNERABLE: Division before multiplication (loses precision)
sharePrice := totalValue.Quo(totalShares)  // Division first
userValue := sharePrice.Mul(userShares)    // Multiplication second
// User gets less value due to rounding down in division

// VULNERABLE: math.LegacyDec associativity issues
result1 := a.Mul(b).Quo(c)  // (1 * 10) / 100 = 0.1
result2 := a.Quo(c).Mul(b)  // (1 / 100) * 10 = 0.1 (but different precision!)
// result1 != result2 due to precision handling

// VULNERABLE: Repeated rounding favors users
for _, user := range users {
    reward := totalReward.Quo(math.LegacyNewDec(int64(len(users))))
    k.MintReward(ctx, user, reward)
}
// Total minted > totalReward due to rounding up
Pattern 2: Wrong Operand in Calculation
// VULNERABLE: Osmosis LP share miscalculation (Finding 9, $5M loss)
// MaximalExactRatioJoin multiplied shareRatio * userTokens
// instead of shareRatio * totalPoolLiquidity
shareRatio := userTokens.Quo(totalPoolLiquidity)
newShares := shareRatio.Mul(userTokens)  // WRONG: should be shareRatio.Mul(totalShares)
// Users received 50% more LP shares than deserved
Pattern 3: Missing Sign/Bound Validation
// VULNERABLE: Vesting Account Poisoning (Barberry, Finding 19)
func (msg MsgCreatePeriodicVestingAccount) ValidateBasic() error {
    for _, period := range msg.VestingPeriods {
        if period.Length <= 0 {
            return errors.New("period length must be positive")
        }
        // Missing: validation that period.Amount coins are all positive!
        // Negative amounts cause LockedCoins() to panic on withdrawal
    }
}

What to Check:

  • [ ] Multiplication before division pattern used

  • [ ] Rounding direction favors protocol, not users

  • [ ] No repeated rounding in loops

  • [ ] Consistent calculation order across all operations

  • [ ] Consider using integer arithmetic with scaling factor

  • [ ] Operands in share/ratio calculations are verified against the formula (not swapped)

  • [ ] CosmWasm wrapping overflow: see COSMWASM_VULNERABILITY_PATTERNS.md §1

  • [ ] All amount/coin fields validated for positive values (not just positive length/duration) — negative amounts in vesting periods cause permanent fund freeze

  • [ ] Front-running of account creation reviewed — malicious vesting accounts can be created at victim addresses before the victim's first transaction

Mitigation:

// SECURE: Multiply before divide (preserves precision)
userValue := totalValue.Mul(userShares).Quo(totalShares)

// SECURE: Round in favor of system
reward := totalReward.Mul(userShares).QuoTruncate(totalShares)

// SECURE: Round up when calculating fees (users pay slightly more)
fee := amount.Mul(feeRate).QuoRoundUp(math.LegacyNewDec(10000))

// SECURE: Distribute with remainder handling
totalDistributed := math.LegacyZeroDec()
for i, user := range users {
    var reward math.LegacyDec
    if i == len(users)-1 {
        reward = totalReward.Sub(totalDistributed)
    } else {
        reward = totalReward.Quo(math.LegacyNewDec(int64(len(users))))
        totalDistributed = totalDistributed.Add(reward)
    }
    k.MintReward(ctx, user, reward)
}

// SECURE: Validate all coin amounts are positive
for _, period := range msg.VestingPeriods {
    if !period.Amount.IsAllPositive() {
        return errors.New("all period amounts must be positive")
    }
}

References:


21 DATA VALIDATION & ENCODING

Description: Encoding, validation, and type handling issues that can cause panics, state corruption, or chain halts.

What to Check:


22 EVENTS & LOGGING (INCLUDING CacheContext EVENT LEAK)

Description: Events are critical for indexers, relayers, and monitoring tools. Incorrect event handling breaks the ecosystem. A particularly dangerous pattern is the CacheContext event leak: when CacheContext is used to tentatively execute operations, state changes are correctly rolled back on failure, but events are still emitted to the parent context. Off-chain systems (bridges, indexers) trusting events without verifying on-chain state can be exploited.

Detection Patterns:

// VULNERABLE: CacheContext event leak (Huckleberry, Finding 4)
func (k Keeper) OnRecvPacket(ctx sdk.Context, packet channeltypes.Packet) exported.Acknowledgement {
    cacheCtx, writeFn := ctx.CacheContext()

    // Execute in cached context — emits events to cacheCtx
    err := k.processPacket(cacheCtx, packet)
    if err != nil {
        // State is rolled back (writeFn not called)
        // BUT events from cacheCtx are still visible to parent context!
        return channeltypes.NewErrorAcknowledgement(err)
    }
    writeFn()
    return channeltypes.NewResultAcknowledgement([]byte{1})
}
// Bridge monitors see valid deposit events for transfers that never happened
// Attacker gets credited on the bridge without tokens actually moving

What to Check:

  • [ ] Enough event logs to debug on-chain transactions

  • [ ] Events NOT generated for failed transactions — Huckleberry: IBC Event Hallucinations

  • [ ] CacheContext usage reviewed: events emitted in cached context are visible even if writeFn is never called — state rolls back but events persist

  • [ ] Off-chain systems (bridges, indexers, relayers) do NOT rely solely on events without verifying on-chain state

  • [ ] Custom IBC OnRecvPacket that returns error ack does not emit misleading deposit/transfer events

  • [ ] No log injection possible

References:


23 DEPRECATED MODULES

As of v0.53.5+. Flag usage and advise migration.

  • x/crisis (Invariant type) — deprecated, will be removed. Use simulations (simsx framework in v0.53+) and property-based tests instead

  • x/nft — deprecated (#24575), moving to cosmos-legacy

  • x/group — deprecated (#24571), moving to cosmos-legacy. EndBlocker had chain-halt bugs (GHSA-47ww-ff84-4jrg, GHSA-x5vx-95h7-rv4p, fixed v0.50.12/v0.53.0)

  • x/params — deprecated, modules use direct keeper param storage via collections

  • ValidateBasic() — deprecated and facultative (via HasValidateBasic interface). Validation belongs in msg server handlers

  • ed25519 tx signing — added in v0.53.0 (#24030). Chains should explicitly decide which key types to permit. Unlike secp256k1, ed25519 is not compatible with Ledger hardware wallets without custom apps


Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
Trail of Bits
Publisher
Trail of Bits (Official)
Source
trailofbits/skills · plugins/building-secure-contracts/skills/cosmos-vulnerability-scanner · commit e4dd0bb
Licence
Creative Commons Attribution-ShareAlike 4.0 International (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Trail of Bits.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download cosmos-vulnerability-scanner.zip