convex-testing-interface
Safe HaskellSafe-Inferred
LanguageHaskell2010

Convex.ThreatModel.InvalidDatumIndex

Description

Threat model for detecting Invalid Datum Index vulnerabilities.

An Invalid Datum Index Attack mutates the constructor index of a Constr datum on a script output to a value outside the expected range. If a validator's FromData parser uses a wildcard or permissive otherwise branch when deserialising the constructor index, an attacker can supply an invalid index that still matches a catch-all and is interpreted as a legitimate state.

Consequences ==

  1. State confusion: If the out-of-range index accidentally matches a catch-all or default branch in unsafeFromBuiltinData, the validator may interpret the datum as a valid (but semantically wrong) state, allowing unintended transitions or fund extraction.
  2. Permanent fund locking: If the invalid index triggers a script error at spend time, any UTxO locked with that corrupted datum becomes permanently unspendable.

Root Cause ==

Validators that use unsafeFromBuiltinData or manually pattern-match on constructor indices without explicitly rejecting unexpected values are at risk. For example:

case index of
  0 -> Pinged
  1 -> Ponged
  _ -> Stopped   -- catch-all silently accepts ANY other index!

This means Constr 99 [] would decode as Stopped, bypassing any guard that should have rejected it.

Mitigation ==

A secure validator should explicitly enumerate all valid indices and call P.traceError (or equivalent) for any unexpected constructor index:

case index of
  0 -> Pinged
  1 -> Ponged
  2 -> Stopped
  _ -> P.traceError "PingPongState: invalid index"

This threat model tests whether a script output with an inline datum still validates when the Constr index is replaced with an out-of-range value. If it does, the validator has an Invalid Datum Index vulnerability.

Synopsis

Documentation

invalidDatumIndexAttack :: ThreatModel () Source #

Default invalid-datum-index attack. The replacement constructor index is drawn per transaction from a curated range starting at 3 (above the typical 0, 1, 2 indices of most Plutus sum types), so QuickCheck explores the parameter space without accidentally hitting a valid index.

invalidDatumIndexAttackWith :: Integer -> ThreatModel () Source #

Invalid-datum-index attack with a fixed constructor index. Keep using this for deterministic regression tests and golden seeds.

invalidDatumIndexAttackWithGen :: Gen Integer -> ThreatModel () Source #

Invalid-datum-index attack parameterised by a generator for the replacement constructor index. This is the primitive the other two forms delegate to.

replaceConstrIndex :: Integer -> ScriptData -> ScriptData Source #

Replace the constructor index of a ScriptDataConstructor with newIdx, preserving all fields.

For other ScriptData variants (Map, List, Number, Bytes) the value is returned unchanged and the precondition filter above will have already excluded non-Constr datums.