Skip to content

Latest commit

 

History

History
87 lines (63 loc) · 2.7 KB

File metadata and controls

87 lines (63 loc) · 2.7 KB

S002 — Panic Usage

  • Category: panic_handling
  • Severity: Medium
  • Rule name: panic_detection

What it detects

S002 flags panic!, .unwrap(), and .expect() calls inside contract functions (functions within impl blocks and standalone module-level functions). Code under #[test] attributes and #[cfg(test)] modules is skipped to avoid false positives.

Why it matters

A panic in a Soroban contract aborts the host invocation. There is no recovery path: the transaction reverts, and any state the function intended to repair stays broken. .unwrap() / .expect() on attacker-influenced values let a caller deliberately trigger aborts, locking contract state and turning a recoverable error into a hard failure with no graceful handling.

Vulnerable example

#![no_std]
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env};

#[contracttype]
pub enum DataKey {
    Balance(Address),
}

#[contract]
pub struct Token;

#[contractimpl]
impl Token {
    pub fn balance(env: Env, id: Address) -> i128 {
        // S002: panics if the key is absent — abort with no recovery.
        env.storage().persistent().get(&DataKey::Balance(id)).unwrap()
    }
}

Safe example

#![no_std]
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env};

#[contracttype]
pub enum DataKey {
    Balance(Address),
}

#[contract]
pub struct Token;

#[contractimpl]
impl Token {
    pub fn balance(env: Env, id: Address) -> i128 {
        // Default to zero instead of panicking on a missing entry.
        env.storage()
            .persistent()
            .get(&DataKey::Balance(id))
            .unwrap_or(0)
    }
}

CVSS-style risk rating

  • Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L
  • Base score: 5.3
  • Rating: Medium

A network caller can trigger an abort with low complexity, but the impact is limited to availability of the affected operation (denial of service), with no direct confidentiality or integrity loss.

How to fix

  1. Replace .unwrap() / .expect() with .unwrap_or(), .unwrap_or_default(), or explicit match / if let handling.
  2. Model recoverable conditions as a Result and propagate with ?.
  3. For cross-contract calls, prefer try_invoke_contract so callee failures return a Result instead of aborting.
  4. Reserve panics for genuinely unreachable invariants, and document why.

Related rules

Related rules: S009, S003

References