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 #

Check for Invalid Datum Index vulnerabilities using constructor index 5.

This is the default configuration, which replaces the constructor index of any inline Constr datum on a script output with 5. Valid PingPong indices are 0 (Pinged), 1 (Ponged), and 2 (Stopped), so 5 is safely out-of-range for all known state machines in this codebase. If the transaction still validates, the validator has a permissive datum index check.

invalidDatumIndexAttackWith :: Integer -> ThreatModel () Source #

Check for Invalid Datum Index vulnerabilities with a configurable constructor index.

For a transaction with script outputs containing inline datums:

  • Replace the Constr index of the datum with invalidIdx
  • Leave the constructor fields unchanged so any field parsing still succeeds
  • If the transaction still validates, the validator does not reject out-of-range constructor indices — it may have a permissive catch-all.

Choose invalidIdx to be outside the range of all valid constructors for the script under test (e.g., 5 for a 3-constructor type, 99 for extra clarity).

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.