| Safe Haskell | Safe-Inferred |
|---|---|
| Language | Haskell2010 |
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 ==
- 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. - 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
Constrindex of the datum withinvalidIdx - 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.