Skip to content

Commit 7f28b11

Browse files
committed
Check CLI argument bounds
1 parent 18d5ab4 commit 7f28b11

File tree

7 files changed

+99
-84
lines changed

7 files changed

+99
-84
lines changed

cardano-api/src/Cardano/Api.hs

+3
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,9 @@ module Cardano.Api (
751751
txInsExistInUTxO,
752752
notScriptLockedTxIns,
753753
textShow,
754+
755+
-- ** CLI option parsing
756+
bounded,
754757
) where
755758

756759
import Cardano.Api.Address

cardano-api/src/Cardano/Api/Utils.hs

+17-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
{-# LANGUAGE CPP #-}
22
{-# LANGUAGE GADTs #-}
33
{-# LANGUAGE LambdaCase #-}
4+
{-# LANGUAGE RankNTypes #-}
5+
{-# LANGUAGE ScopedTypeVariables #-}
6+
{-# LANGUAGE TypeApplications #-}
47

58
#if !defined(mingw32_HOST_OS)
69
#define UNIX
@@ -22,10 +25,13 @@ module Cardano.Api.Utils
2225
, runParsecParser
2326
, textShow
2427
, writeSecrets
28+
29+
-- ** CLI option parsing
30+
, bounded
2531
) where
2632

2733
import Control.Exception (bracket)
28-
import Control.Monad (forM_)
34+
import Control.Monad (forM_, when)
2935
import qualified Data.Aeson.Types as Aeson
3036
import qualified Data.ByteString as BS
3137
import qualified Data.ByteString.Builder as Builder
@@ -48,6 +54,9 @@ import System.Directory (emptyPermissions, readable, setPermissions)
4854
#endif
4955

5056
import Cardano.Api.Eras
57+
import Options.Applicative (ReadM)
58+
import Options.Applicative.Builder (eitherReader)
59+
import qualified Text.Read as Read
5160

5261
(?!) :: Maybe a -> e -> Either e a
5362
Nothing ?! e = Left e
@@ -131,3 +140,10 @@ renderEra (AnyCardanoEra AllegraEra) = "Allegra"
131140
renderEra (AnyCardanoEra MaryEra) = "Mary"
132141
renderEra (AnyCardanoEra AlonzoEra) = "Alonzo"
133142
renderEra (AnyCardanoEra BabbageEra) = "Babbage"
143+
144+
bounded :: forall a. (Bounded a, Integral a, Show a) => String -> ReadM a
145+
bounded t = eitherReader $ \s -> do
146+
i <- Read.readEither @Integer s
147+
when (i < fromIntegral (minBound @a)) $ Left $ t <> " must not be less than " <> show (minBound @a)
148+
when (i > fromIntegral (maxBound @a)) $ Left $ t <> " must not greater than " <> show (maxBound @a)
149+
pure (fromIntegral i)

cardano-cli/src/Cardano/CLI/Byron/Parsers.hs

+5-6
Original file line numberDiff line numberDiff line change
@@ -664,12 +664,11 @@ pNetworkId =
664664

665665
pTestnetMagic :: Parser NetworkMagic
666666
pTestnetMagic =
667-
NetworkMagic <$>
668-
Opt.option Opt.auto
669-
( Opt.long "testnet-magic"
670-
<> Opt.metavar "NATURAL"
671-
<> Opt.help "Specify a testnet magic id."
672-
)
667+
fmap NetworkMagic $ Opt.option (bounded "TESTNET_MAGIC") $ mconcat
668+
[ Opt.long "testnet-magic"
669+
, Opt.metavar "NATURAL"
670+
, Opt.help "Specify a testnet magic id."
671+
]
673672

674673
parseNewSigningKeyFile :: String -> Parser NewSigningKeyFile
675674
parseNewSigningKeyFile opt =

cardano-cli/src/Cardano/CLI/Helpers.hs

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# LANGUAGE OverloadedStrings #-}
22
{-# LANGUAGE RankNTypes #-}
3+
{-# LANGUAGE ScopedTypeVariables #-}
34

45
module Cardano.CLI.Helpers
56
( HelpersError(..)
@@ -19,12 +20,22 @@ import Cardano.Prelude (ConvertText (..))
1920
import Codec.CBOR.Pretty (prettyHexEnc)
2021
import Codec.CBOR.Read (DeserialiseFailure, deserialiseFromBytes)
2122
import Codec.CBOR.Term (decodeTerm, encodeTerm)
23+
import Control.Exception (Exception (..), IOException)
24+
import Control.Monad (unless, when)
25+
import Control.Monad.IO.Class (MonadIO (..))
26+
import Control.Monad.Trans.Except (ExceptT)
2227
import Control.Monad.Trans.Except.Extra (handleIOExceptT, left)
28+
import Data.Bifunctor (Bifunctor (..))
29+
import Data.ByteString (ByteString)
2330
import qualified Data.ByteString as BS
2431
import qualified Data.ByteString.Lazy as LB
32+
import Data.Functor (void)
33+
import Data.Text (Text)
2534
import qualified Data.Text as Text
35+
import qualified Data.Text.IO as Text
2636
import qualified System.Console.ANSI as ANSI
2737
import System.Console.ANSI
38+
import qualified System.Directory as IO
2839
import qualified System.IO as IO
2940

3041
import Cardano.Binary (Decoder, fromCBOR)
@@ -34,17 +45,6 @@ import qualified Cardano.Chain.Update as Update
3445
import qualified Cardano.Chain.UTxO as UTxO
3546
import Cardano.CLI.Types
3647

37-
import Control.Exception (Exception (..), IOException)
38-
import Control.Monad (unless, when)
39-
import Control.Monad.IO.Class (MonadIO (..))
40-
import Control.Monad.Trans.Except (ExceptT)
41-
import Data.Bifunctor (Bifunctor (..))
42-
import Data.ByteString (ByteString)
43-
import Data.Functor (void)
44-
import Data.Text (Text)
45-
import qualified Data.Text.IO as Text
46-
import qualified System.Directory as IO
47-
4848
data HelpersError
4949
= CBORPrettyPrintError !DeserialiseFailure
5050
| CBORDecodingError !DeserialiseFailure

cardano-cli/src/Cardano/CLI/Shelley/Parsers.hs

+37-43
Original file line numberDiff line numberDiff line change
@@ -1730,31 +1730,28 @@ pSigningKeyFile fdir =
17301730

17311731
pKesPeriod :: Parser KESPeriod
17321732
pKesPeriod =
1733-
KESPeriod <$>
1734-
Opt.option Opt.auto
1735-
( Opt.long "kes-period"
1736-
<> Opt.metavar "NATURAL"
1737-
<> Opt.help "The start of the KES key validity period."
1738-
)
1733+
fmap KESPeriod $ Opt.option (bounded "KES_PERIOD") $ mconcat
1734+
[ Opt.long "kes-period"
1735+
, Opt.metavar "NATURAL"
1736+
, Opt.help "The start of the KES key validity period."
1737+
]
17391738

17401739
pEpochNo :: Parser EpochNo
17411740
pEpochNo =
1742-
EpochNo <$>
1743-
Opt.option Opt.auto
1744-
( Opt.long "epoch"
1745-
<> Opt.metavar "NATURAL"
1746-
<> Opt.help "The epoch number."
1747-
)
1741+
fmap EpochNo $ Opt.option (bounded "EPOCH") $ mconcat
1742+
[ Opt.long "epoch"
1743+
, Opt.metavar "NATURAL"
1744+
, Opt.help "The epoch number."
1745+
]
17481746

17491747

17501748
pEpochNoUpdateProp :: Parser EpochNo
17511749
pEpochNoUpdateProp =
1752-
EpochNo <$>
1753-
Opt.option Opt.auto
1754-
( Opt.long "epoch"
1755-
<> Opt.metavar "NATURAL"
1756-
<> Opt.help "The epoch number in which the update proposal is valid."
1757-
)
1750+
fmap EpochNo $ Opt.option (bounded "EPOCH") $ mconcat
1751+
[ Opt.long "epoch"
1752+
, Opt.metavar "EPOCH"
1753+
, Opt.help "The epoch number in which the update proposal is valid."
1754+
]
17581755

17591756
pGenesisFile :: String -> Parser GenesisFile
17601757
pGenesisFile desc =
@@ -2054,12 +2051,11 @@ pNetworkId =
20542051

20552052
pTestnetMagic :: Parser NetworkMagic
20562053
pTestnetMagic =
2057-
NetworkMagic <$>
2058-
Opt.option Opt.auto
2059-
( Opt.long "testnet-magic"
2060-
<> Opt.metavar "NATURAL"
2061-
<> Opt.help "Specify a testnet magic id."
2062-
)
2054+
fmap NetworkMagic $ Opt.option (bounded "TESTNET_MAGIC") $ mconcat
2055+
[ Opt.long "testnet-magic"
2056+
, Opt.metavar "TESTNET_MAGIC"
2057+
, Opt.help "Specify a testnet magic id."
2058+
]
20632059

20642060
pTxSubmitFile :: Parser FilePath
20652061
pTxSubmitFile =
@@ -2367,12 +2363,12 @@ pPolicyId =
23672363

23682364
pInvalidBefore :: Parser SlotNo
23692365
pInvalidBefore = fmap SlotNo $ asum
2370-
[ Opt.option Opt.auto $ mconcat
2366+
[ Opt.option (bounded "SLOT") $ mconcat
23712367
[ Opt.long "invalid-before"
23722368
, Opt.metavar "SLOT"
23732369
, Opt.help "Time that transaction is valid from (in slots)."
23742370
]
2375-
, Opt.option Opt.auto $ mconcat
2371+
, Opt.option (bounded "SLOT") $ mconcat
23762372
[ Opt.long "lower-bound"
23772373
, Opt.metavar "SLOT"
23782374
, Opt.help $ mconcat
@@ -2386,12 +2382,12 @@ pInvalidBefore = fmap SlotNo $ asum
23862382
pInvalidHereafter :: Parser SlotNo
23872383
pInvalidHereafter =
23882384
fmap SlotNo $ asum
2389-
[ Opt.option Opt.auto $ mconcat
2385+
[ Opt.option (bounded "SLOT") $ mconcat
23902386
[ Opt.long "invalid-hereafter"
23912387
, Opt.metavar "SLOT"
23922388
, Opt.help "Time that transaction is valid until (in slots)."
23932389
]
2394-
, Opt.option Opt.auto $ mconcat
2390+
, Opt.option (bounded "SLOT") $ mconcat
23952391
[ Opt.long "upper-bound"
23962392
, Opt.metavar "SLOT"
23972393
, Opt.help $ mconcat
@@ -2400,7 +2396,7 @@ pInvalidHereafter =
24002396
]
24012397
, Opt.internal
24022398
]
2403-
, Opt.option Opt.auto $ mconcat
2399+
, Opt.option (bounded "SLOT") $ mconcat
24042400
[ Opt.long "ttl"
24052401
, Opt.metavar "SLOT"
24062402
, Opt.help "Time to live (in slots) (deprecated; use --invalid-hereafter instead)."
@@ -3051,12 +3047,11 @@ pPoolDeposit =
30513047

30523048
pEpochBoundRetirement :: Parser EpochNo
30533049
pEpochBoundRetirement =
3054-
EpochNo <$>
3055-
Opt.option Opt.auto
3056-
( Opt.long "pool-retirement-epoch-boundary"
3057-
<> Opt.metavar "INT"
3058-
<> Opt.help "Epoch bound on pool retirement."
3059-
)
3050+
fmap EpochNo $ Opt.option (bounded "EPOCH_BOUNDARY") $ mconcat
3051+
[ Opt.long "pool-retirement-epoch-boundary"
3052+
, Opt.metavar "EPOCH_BOUNDARY"
3053+
, Opt.help "Epoch bound on pool retirement."
3054+
]
30603055

30613056
pNumberOfPools :: Parser Natural
30623057
pNumberOfPools =
@@ -3248,14 +3243,13 @@ defaultByronEpochSlots = 21600
32483243

32493244
pEpochSlots :: Parser EpochSlots
32503245
pEpochSlots =
3251-
EpochSlots <$>
3252-
Opt.option Opt.auto
3253-
( Opt.long "epoch-slots"
3254-
<> Opt.metavar "NATURAL"
3255-
<> Opt.help "The number of slots per epoch for the Byron era."
3256-
<> Opt.value defaultByronEpochSlots -- Default to the mainnet value.
3257-
<> Opt.showDefault
3258-
)
3246+
fmap EpochSlots $ Opt.option (bounded "SLOTS") $ mconcat
3247+
[ Opt.long "epoch-slots"
3248+
, Opt.metavar "SLOTS"
3249+
, Opt.help "The number of slots per epoch for the Byron era."
3250+
, Opt.value defaultByronEpochSlots -- Default to the mainnet value.
3251+
, Opt.showDefault
3252+
]
32593253

32603254
pProtocolVersion :: Parser (Natural, Natural)
32613255
pProtocolVersion =

cardano-node/src/Cardano/Node/Handlers/Shutdown.hs

+17-17
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,12 @@ module Cardano.Node.Handlers.Shutdown
2626
)
2727
where
2828

29-
import Control.Applicative (Alternative (..))
3029
import Control.Concurrent.Async (race_)
3130
import Control.Exception (try)
3231
import Control.Exception.Base (throwIO)
3332
import Control.Monad (void, when)
3433
import Data.Aeson (FromJSON, ToJSON)
34+
import Data.Foldable (asum)
3535
import Data.Text (Text, pack)
3636
import Generic.Data.Orphans ()
3737
import GHC.Generics (Generic)
@@ -42,6 +42,7 @@ import qualified System.IO as IO
4242
import qualified System.IO.Error as IO
4343
import System.Posix.Types (Fd (Fd))
4444

45+
import Cardano.Api (bounded)
4546
import Cardano.Slotting.Slot (WithOrigin (..))
4647
import "contra-tracer" Control.Tracer
4748
import Ouroboros.Consensus.Block (Header)
@@ -50,7 +51,6 @@ import Ouroboros.Consensus.Util.ResourceRegistry (ResourceRegistry)
5051
import Ouroboros.Consensus.Util.STM (Watcher (..), forkLinkedWatcher)
5152
import Ouroboros.Network.Block (BlockNo (..), HasHeader, SlotNo (..), pointSlot)
5253

53-
5454
data ShutdownOn
5555
= ASlot !SlotNo
5656
| ABlock !BlockNo
@@ -61,21 +61,21 @@ deriving instance FromJSON ShutdownOn
6161
deriving instance ToJSON ShutdownOn
6262

6363
parseShutdownOn :: Opt.Parser ShutdownOn
64-
parseShutdownOn =
65-
Opt.option (ASlot . SlotNo <$> Opt.auto) (
66-
Opt.long "shutdown-on-slot-synced"
67-
<> Opt.metavar "SLOT"
68-
<> Opt.help "Shut down the process after ChainDB is synced up to the specified slot"
69-
<> Opt.hidden
70-
)
71-
<|>
72-
Opt.option (ABlock . BlockNo <$> Opt.auto) (
73-
Opt.long "shutdown-on-block-synced"
74-
<> Opt.metavar "BLOCK"
75-
<> Opt.help "Shut down the process after ChainDB is synced up to the specified block"
76-
<> Opt.hidden
77-
)
78-
<|> pure NoShutdown
64+
parseShutdownOn = asum
65+
[ Opt.option (ASlot . SlotNo <$> bounded "SLOT") $ mconcat
66+
[ Opt.long "shutdown-on-slot-synced"
67+
, Opt.metavar "SLOT"
68+
, Opt.help "Shut down the process after ChainDB is synced up to the specified slot"
69+
, Opt.hidden
70+
]
71+
, Opt.option (ABlock . BlockNo <$> bounded "BLOCK") $ mconcat
72+
[ Opt.long "shutdown-on-block-synced"
73+
, Opt.metavar "BLOCK"
74+
, Opt.help "Shut down the process after ChainDB is synced up to the specified block"
75+
, Opt.hidden
76+
]
77+
, pure NoShutdown
78+
]
7979

8080
data ShutdownTrace
8181
= ShutdownRequested

cardano-submit-api/src/Cardano/TxSubmit/CLI/Parsers.hs

+9-6
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ module Cardano.TxSubmit.CLI.Parsers
1010
) where
1111

1212
import Cardano.Api (AnyConsensusModeParams (..), ConsensusModeParams (..),
13-
EpochSlots (..), NetworkId (..), NetworkMagic (..), SocketPath (..))
13+
EpochSlots (..), NetworkId (..), NetworkMagic (..), SocketPath (..), bounded)
14+
1415
import Cardano.TxSubmit.CLI.Types (ConfigFile (..), TxSubmitNodeParams (..))
1516
import Cardano.TxSubmit.Rest.Parsers (pWebserverConfig)
17+
1618
import Control.Applicative (Alternative (..), (<**>))
1719
import Data.Word (Word64)
1820
import Options.Applicative (Parser, ParserInfo)
@@ -56,11 +58,12 @@ pNetworkId = pMainnet <|> fmap Testnet pTestnetMagic
5658
)
5759

5860
pTestnetMagic :: Parser NetworkMagic
59-
pTestnetMagic = NetworkMagic <$> Opt.option Opt.auto
60-
( Opt.long "testnet-magic"
61-
<> Opt.metavar "NATURAL"
62-
<> Opt.help "Specify a testnet magic id."
63-
)
61+
pTestnetMagic =
62+
fmap NetworkMagic $ Opt.option (bounded "TESTNET_MAGIC") $ mconcat
63+
[ Opt.long "testnet-magic"
64+
, Opt.metavar "TESTNET_MAGIC"
65+
, Opt.help "Specify a testnet magic id."
66+
]
6467

6568

6669
-- TODO: This was ripped from `cardano-cli` because, unfortunately, it's not

0 commit comments

Comments
 (0)