{-# LANGUAGE OverloadedStrings #-}
module Convex.ThreatModel.LargeData (
largeDataAttack,
largeDataAttackWith,
bloatData,
) where
import Convex.ThreatModel
largeDataAttack :: ThreatModel ()
largeDataAttack :: ThreatModel ()
largeDataAttack = Int -> ThreatModel ()
largeDataAttackWith Int
1000
largeDataAttackWith :: Int -> ThreatModel ()
largeDataAttackWith :: Int -> ThreatModel ()
largeDataAttackWith Int
n = [Char] -> ThreatModel () -> ThreatModel ()
forall a. [Char] -> ThreatModel a -> ThreatModel a
Named ([Char]
"Large Data Attack (max " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
n [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" fields)") (ThreatModel () -> ThreatModel ())
-> ThreatModel () -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$ do
Input
_ <- (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)
[Output]
outputs <- ThreatModel [Output]
getTxOutputs
let scriptOutputsWithDatum :: [Output]
scriptOutputsWithDatum = (Output -> Bool) -> [Output] -> [Output]
forall a. (a -> Bool) -> [a] -> [a]
filter Output -> Bool
isScriptOutputWithInlineDatum [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]
scriptOutputsWithDatum)
Output
target <- [Output] -> ThreatModel Output
forall a. Show a => [a] -> ThreatModel a
pickAny [Output]
scriptOutputsWithDatum
ScriptData
originalDatum <- case Output -> Maybe ScriptData
getInlineDatum Output
target of
Maybe ScriptData
Nothing -> [Char] -> ThreatModel ScriptData
forall a. [Char] -> ThreatModel a
failPrecondition [Char]
"Script output missing inline datum"
Just ScriptData
originalDatum' -> ScriptData -> ThreatModel ScriptData
forall a. a -> ThreatModel a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ScriptData
originalDatum'
let bloatedDatum :: ScriptData
bloatedDatum = Int -> ScriptData -> ScriptData
bloatData Int
n ScriptData
originalDatum
[Char] -> ThreatModel ()
counterexampleTM ([Char] -> ThreatModel ()) -> [Char] -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$
[[Char]] -> [Char]
paragraph
[ [Char]
"The transaction contains a script output at index"
, TxIx -> [Char]
forall a. Show a => a -> [Char]
show (Output -> TxIx
outputIx Output
target)
, [Char]
"with an inline datum."
]
[Char] -> ThreatModel ()
counterexampleTM ([Char] -> ThreatModel ()) -> [Char] -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$
[[Char]] -> [Char]
paragraph
[ [Char]
"Testing if the datum can be bloated with"
, Int -> [Char]
forall a. Show a => a -> [Char]
show Int
n
, [Char]
"extra fields while still passing validation."
]
[Char] -> ThreatModel ()
counterexampleTM ([Char] -> ThreatModel ()) -> [Char] -> ThreatModel ()
forall a b. (a -> b) -> a -> b
$
[[Char]] -> [Char]
paragraph
[ [Char]
"If this validates, the script's FromData parser is permissive"
, [Char]
"and ignores extra Constr fields. An attacker could exploit this"
, [Char]
"to make a single datum satisfy multiple validators,"
, [Char]
"or to bypass certain datum-based checks."
]
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 (ScriptData -> Datum
toInlineDatum ScriptData
bloatedDatum)
bloatData :: Int -> ScriptData -> ScriptData
bloatData :: Int -> ScriptData -> ScriptData
bloatData Int
n ScriptData
sd = case ScriptData
sd of
ScriptDataConstructor Integer
idx [ScriptData]
fields ->
let extraFields :: [ScriptData]
extraFields = Int -> ScriptData -> [ScriptData]
forall a. Int -> a -> [a]
replicate Int
n (Integer -> ScriptData
ScriptDataNumber Integer
42)
in Integer -> [ScriptData] -> ScriptData
ScriptDataConstructor Integer
idx ([ScriptData]
fields [ScriptData] -> [ScriptData] -> [ScriptData]
forall a. [a] -> [a] -> [a]
++ [ScriptData]
extraFields)
ScriptData
_ -> ScriptData
sd
isScriptOutputWithInlineDatum :: Output -> Bool
isScriptOutputWithInlineDatum :: Output -> Bool
isScriptOutputWithInlineDatum Output
output =
Bool -> Bool
not (AddressAny -> Bool
isKeyAddressAny (Output -> AddressAny
forall t. IsInputOrOutput t => t -> AddressAny
addressOf Output
output)) Bool -> Bool -> Bool
&& Output -> Bool
hasInlineDatum Output
output
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
getInlineDatum :: Output -> Maybe ScriptData
getInlineDatum :: Output -> Maybe ScriptData
getInlineDatum 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 BabbageEraOnwards Era
_ HashableScriptData
hashableData -> ScriptData -> Maybe ScriptData
forall a. a -> Maybe a
Just (HashableScriptData -> ScriptData
getScriptData HashableScriptData
hashableData)
Datum
_ -> Maybe ScriptData
forall a. Maybe a
Nothing
toInlineDatum :: ScriptData -> Datum
toInlineDatum :: ScriptData -> Datum
toInlineDatum ScriptData
sd =
BabbageEraOnwards Era -> HashableScriptData -> Datum
forall era ctx.
BabbageEraOnwards era -> HashableScriptData -> TxOutDatum ctx era
TxOutDatumInline BabbageEraOnwards Era
BabbageEraOnwardsConway (ScriptData -> HashableScriptData
unsafeHashableScriptData ScriptData
sd)