convex-testing-interface
Safe HaskellSafe-Inferred
LanguageHaskell2010

Convex.ThreatModel.NegativeInteger

Description

Threat model for detecting missing integer boundary checks.

A Negative Integer Attack exploits validators that don't check integer bounds (e.g., balance >= 0). The attack negates integer fields in output datums, which may reveal that the validator allows negative values where only positive values make semantic sense.

Consequences ==

  1. Logical state corruption: If a validator allows negative balances, users can withdraw more than they deposited, draining funds from a pool.
  2. Protocol invariant violation: Counters, timestamps, or other fields that should be monotonic or non-negative can be corrupted.

Vulnerable Patterns ==

Pattern: Missing balance check ===

// Vulnerable: no check that balance >= 0
let new_balance = input_balance - withdrawal_amount
expect output_datum.balance == new_balance

An attacker with balance = 0 can withdraw funds, creating balance = -100. The validator doesn't reject this because it never checks balance >= 0.

Mitigation ==

A secure validator should:

  • Explicitly check balance >= 0 or appropriate bounds for all integer fields
  • Validate that counters only increase (or decrease within bounds)
  • Use unsigned integers where semantically appropriate (though Plutus uses Integer)

This threat model tests if a script output with an inline datum still validates when integer fields are negated.

Synopsis

Negative integer attack

negativeIntegerAttack :: ThreatModel () Source #

Check for missing integer boundary checks.

For a transaction with script outputs containing inline datums:

  • Recursively negate all ScriptDataNumber fields in the datum
  • If the transaction still validates, the script doesn't enforce proper bounds checking on integer fields.

This catches vulnerabilities where validators allow negative values for fields like balances, counters, or timestamps that should be non-negative.

negativeIntegerAttack  -- Negate all integers in the datum

negateIntegers :: ScriptData -> ScriptData Source #

Recursively negate all integer fields in a ScriptData value.

For ScriptDataNumber n, returns ScriptDataNumber (negate n).

Recursively processes ScriptDataConstructor fields, lists, and maps.

For other ScriptData variants (Bytes), returns the value unchanged.