convex-testing-interface
Safe HaskellSafe-Inferred
LanguageHaskell2010

Convex.ThreatModel.DuplicateListEntry

Description

Threat model for detecting duplicate list entry vulnerabilities.

A Duplicate List Entry Attack exploits validators that don't check for uniqueness in list fields. The attack duplicates entries in list fields within the datum, which may reveal that the validator allows duplicate entries where uniqueness should be enforced.

Consequences ==

  1. Signature bypassing: In a multisig contract with signed_users list, an attacker can sign once and duplicate their entry to fill all required signature slots, bypassing the multi-party requirement.
  2. Vote manipulation: In a voting contract, a single voter could have their vote counted multiple times if the voter list isn't checked for duplicates.
  3. Reward gaming: In reward distribution, a single participant could claim multiple reward shares by appearing multiple times in a beneficiary list.

Vulnerable Patterns ==

Pattern: No uniqueness check on signed_users ===

// Vulnerable: only checks length, not uniqueness!
expect list.length(output.signed_users) >= required_signatures

An attacker who is allowed to sign can sign once, then intercept the transaction and duplicate their signature to fill all slots.

Pattern: Prepend-only list update without duplicate check ===

// Vulnerable: just prepends without checking if already in list
let new_signed = list.push(input.signed_users, signer)
expect output.signed_users == new_signed

The validator checks that the signer was prepended correctly, but doesn't check if the signer was already in the list. Multiple Sign transactions with the same signer create duplicates.

Mitigation ==

A secure validator should:

  • Check for uniqueness before adding to lists: !list.has(signed_users, new_signer)
  • Validate that list entries are unique in the output datum
  • Use sets instead of lists where uniqueness is required

This threat model tests if a script output with an inline datum still validates when list entries are duplicated.

Synopsis

Duplicate list entry attack

duplicateListEntryAttack :: ThreatModel () Source #

Check for missing uniqueness checks in list fields.

For a transaction with script outputs containing inline datums:

  • Recursively find all non-empty ScriptDataList fields in the datum
  • Duplicate the first entry of each list
  • If the transaction still validates, the script doesn't enforce uniqueness in list fields.

This catches vulnerabilities where validators allow duplicate entries in lists like signed_users, voters, or beneficiaries where uniqueness should be enforced.

duplicateListEntryAttack  -- Duplicate first entry in all lists

duplicateFirstEntry :: ScriptData -> ScriptData Source #

Recursively duplicate the first entry in all non-empty list fields.

For ScriptDataList (x:xs), returns ScriptDataList (x:x:xs) - duplicating the first entry.

Recursively processes ScriptDataConstructor fields, nested lists, and maps.

For other ScriptData variants (Number, Bytes) and empty lists, returns the value unchanged.

This simulates an attack where: - A user signs a multisig, adding their PKH to signed_users = [pkh] - The attacker duplicates to signed_users = [pkh, pkh], filling 2 slots with 1 signature