{-# LANGUAGE OverloadedStrings #-}
module Convex.ThreatModel.OutputDatumHashMissing (
outputDatumHashMissingAttack,
outputDatumHashMissingAttackWith,
) where
import Cardano.Api qualified as C
import Convex.ThreatModel
import Convex.Utils.String (unsafeDatumHash)
outputDatumHashMissingAttack :: ThreatModel ()
outputDatumHashMissingAttack :: ThreatModel ()
outputDatumHashMissingAttack =
Hash ScriptData -> ThreatModel ()
outputDatumHashMissingAttackWith
(Text -> Hash ScriptData
unsafeDatumHash Text
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef")
outputDatumHashMissingAttackWith :: C.Hash C.ScriptData -> ThreatModel ()
outputDatumHashMissingAttackWith :: Hash ScriptData -> ThreatModel ()
outputDatumHashMissingAttackWith Hash ScriptData
orphanHash = String -> ThreatModel () -> ThreatModel ()
forall a. String -> ThreatModel a -> ThreatModel a
Named String
"Output Datum Hash Missing Attack" (ThreatModel () -> ThreatModel ())
-> ThreatModel () -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$ do
Input
scriptInput <- (Input -> Bool) -> ThreatModel Input
anyInputSuchThat (Bool -> Bool
not (Bool -> Bool) -> (Input -> Bool) -> Input -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AddressAny -> Bool
isKeyAddressAny (AddressAny -> Bool) -> (Input -> AddressAny) -> Input -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Input -> AddressAny
forall t. IsInputOrOutput t => t -> AddressAny
addressOf)
let scriptAddr :: AddressAny
scriptAddr = Input -> AddressAny
forall t. IsInputOrOutput t => t -> AddressAny
addressOf Input
scriptInput
[Output]
outputs <- ThreatModel [Output]
getTxOutputs
let continuationInlineOutputs :: [Output]
continuationInlineOutputs =
(Output -> Bool) -> [Output] -> [Output]
forall a. (a -> Bool) -> [a] -> [a]
filter
(\Output
o -> Output -> AddressAny
forall t. IsInputOrOutput t => t -> AddressAny
addressOf Output
o AddressAny -> AddressAny -> Bool
forall a. Eq a => a -> a -> Bool
== AddressAny
scriptAddr Bool -> Bool -> Bool
&& Output -> Bool
hasInlineDatum Output
o)
[Output]
outputs
ThreatModel () -> ThreatModel ()
forall a. ThreatModel a -> ThreatModel a
threatPrecondition (ThreatModel () -> ThreatModel ())
-> ThreatModel () -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$ Bool -> ThreatModel ()
ensure (Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [Output] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Output]
continuationInlineOutputs)
Output
target <- [Output] -> ThreatModel Output
forall a. Show a => [a] -> ThreatModel a
pickAny [Output]
continuationInlineOutputs
String -> ThreatModel ()
counterexampleTM (String -> ThreatModel ()) -> String -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$
[String] -> String
paragraph
[ String
"The transaction spends a script input at"
, Doc -> String
forall a. Show a => a -> String
show (AddressAny -> Doc
prettyAddress AddressAny
scriptAddr)
, String
"and creates a continuation output with inline datum."
]
String -> ThreatModel ()
counterexampleTM (String -> ThreatModel ()) -> String -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$
[String] -> String
paragraph
[ String
"Testing if the output datum at index"
, TxIx -> String
forall a. Show a => a -> String
show (Output -> TxIx
outputIx Output
target)
, String
"can be converted to TxOutDatumHash with an orphaned hash"
, String
"that is not present in txInfoData."
]
String -> ThreatModel ()
counterexampleTM (String -> ThreatModel ()) -> String -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$
[String] -> String
paragraph
[ String
"If this validates, the script may accept output datum hashes without"
, String
"ensuring the hash resolves in the datum map, which can break state"
, String
"reconstruction and lead to locked funds."
]
TxModifier -> ThreatModel ()
shouldNotValidate (TxModifier -> ThreatModel ()) -> TxModifier -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$ Output -> Datum -> TxModifier
forall t. IsInputOrOutput t => t -> Datum -> TxModifier
changeDatumOf Output
target (AlonzoEraOnwards Era -> Hash ScriptData -> Datum
forall era ctx.
AlonzoEraOnwards era -> Hash ScriptData -> TxOutDatum ctx era
TxOutDatumHash AlonzoEraOnwards Era
forall era. IsAlonzoBasedEra era => AlonzoEraOnwards era
C.alonzoBasedEra Hash ScriptData
orphanHash)
hasInlineDatum :: Output -> Bool
hasInlineDatum :: Output -> Bool
hasInlineDatum Output
output =
case TxOut CtxTx Era -> Datum
forall ctx. TxOut ctx Era -> TxOutDatum ctx Era
datumOfTxOut (Output -> TxOut CtxTx Era
outputTxOut Output
output) of
TxOutDatumInline{} -> Bool
True
Datum
_ -> Bool
False