From 740e950e76f104d53e80d34d4638a2244f1751ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= <2642849+elopez@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:24:54 +0100 Subject: [PATCH] Allow disabling Slither (#1327) This allows explicitly disabling Slither with a new `--disable-slither` command-line flag or `disableSlither` configuration option. A blinking, red warning will be shown in the UI when Slither is disabled or otherwise failed, to disuade people from fuzzing without it. Closes #1318 --- lib/Echidna/Config.hs | 1 + lib/Echidna/SourceAnalysis/Slither.hs | 14 ++++++++++++-- lib/Echidna/Types/Solidity.hs | 1 + lib/Echidna/UI.hs | 2 ++ lib/Echidna/UI/Widgets.hs | 16 +++++++++++++++- src/Main.hs | 6 +++++- tests/solidity/basic/default.yaml | 2 ++ 7 files changed, 38 insertions(+), 4 deletions(-) diff --git a/lib/Echidna/Config.hs b/lib/Echidna/Config.hs index ed779601..7c363afd 100644 --- a/lib/Echidna/Config.hs +++ b/lib/Echidna/Config.hs @@ -114,6 +114,7 @@ instance FromJSON EConfigWithUsage where <*> v ..:? "balanceContract" ..!= 0 <*> v ..:? "codeSize" ..!= 0xffffffff <*> v ..:? "prefix" ..!= "echidna_" + <*> v ..:? "disableSlither" ..!= False <*> v ..:? "cryticArgs" ..!= [] <*> v ..:? "solcArgs" ..!= "" <*> v ..:? "solcLibs" ..!= [] diff --git a/lib/Echidna/SourceAnalysis/Slither.hs b/lib/Echidna/SourceAnalysis/Slither.hs index 09d5fbdb..9d5a486d 100644 --- a/lib/Echidna/SourceAnalysis/Slither.hs +++ b/lib/Echidna/SourceAnalysis/Slither.hs @@ -145,8 +145,13 @@ instance FromJSON SlitherInfo where -- Slither processing runSlither :: FilePath -> SolConf -> IO SlitherInfo -runSlither fp solConf = do - findExecutable "slither" >>= \case +runSlither fp solConf = if solConf.disableSlither + then do + hPutStrLn stderr $ + "WARNING: Slither was explicitly disabled. Echidna uses Slither (https://github.com/crytic/slither)" + <> " to perform source analysis, which makes fuzzing more effective. You should enable it." + pure emptySlitherInfo + else findExecutable "slither" >>= \case Nothing -> do hPutStrLn stderr $ "WARNING: slither not found. Echidna uses Slither (https://github.com/crytic/slither)" @@ -177,3 +182,8 @@ runSlither fp solConf = do emptySlitherInfo :: SlitherInfo emptySlitherInfo = SlitherInfo mempty mempty mempty mempty mempty [] [] [] + +isEmptySlitherInfo :: Maybe SlitherInfo -> Bool +isEmptySlitherInfo (Just (SlitherInfo _ _ _ _ _ [] [] [])) = True +isEmptySlitherInfo Nothing = True +isEmptySlitherInfo _ = False diff --git a/lib/Echidna/Types/Solidity.hs b/lib/Echidna/Types/Solidity.hs index f9607633..1cb167d3 100644 --- a/lib/Echidna/Types/Solidity.hs +++ b/lib/Echidna/Types/Solidity.hs @@ -66,6 +66,7 @@ data SolConf = SolConf , balanceContract :: Integer -- ^ Initial balance of contract to test , codeSize :: Integer -- ^ Max code size for deployed contratcs (default 0xffffffff) , prefix :: Text -- ^ Function name prefix used to denote tests + , disableSlither :: Bool -- ^ Whether or not to skip running Slither , cryticArgs :: [String] -- ^ Args to pass to crytic , solcArgs :: String -- ^ Args to pass to @solc@ , solcLibs :: [String] -- ^ List of libraries to load, in order. diff --git a/lib/Echidna/UI.hs b/lib/Echidna/UI.hs index 11e6d35e..7c9a3ff3 100644 --- a/lib/Echidna/UI.hs +++ b/lib/Echidna/UI.hs @@ -36,6 +36,7 @@ import Echidna.Campaign (runWorker, spawnListener) import Echidna.Output.Corpus (saveCorpusEvent) import Echidna.Output.JSON qualified import Echidna.Server (runSSEServer) +import Echidna.SourceAnalysis.Slither (isEmptySlitherInfo) import Echidna.Types.Campaign import Echidna.Types.Config import Echidna.Types.Corpus qualified as Corpus @@ -126,6 +127,7 @@ ui vm dict initialCorpus cliSelectedContract = do , timeStarted = now , timeStopped = Nothing , now = now + , slitherSucceeded = not $ isEmptySlitherInfo env.slitherInfo , fetchedContracts = mempty , fetchedSlots = mempty , fetchedDialog = B.dialog (Just $ str " Fetched contracts/slots ") Nothing 80 diff --git a/lib/Echidna/UI/Widgets.hs b/lib/Echidna/UI/Widgets.hs index cfa851bf..0387a609 100644 --- a/lib/Echidna/UI/Widgets.hs +++ b/lib/Echidna/UI/Widgets.hs @@ -42,6 +42,7 @@ data UIState = UIState , timeStarted :: LocalTime , timeStopped :: Maybe LocalTime , now :: LocalTime + , slitherSucceeded :: Bool , fetchedContracts :: Map Addr (Maybe Contract) , fetchedSlots :: Map Addr (Map W256 (Maybe W256)) , fetchedDialog :: B.Dialog () Name @@ -65,7 +66,8 @@ data UIStateStatus = Uninitialized | Running attrs :: A.AttrMap attrs = A.attrMap (V.white `on` V.black) - [ (attrName "failure", fg V.brightRed) + [ (attrName "alert", fg V.brightRed `V.withStyle` V.blink `V.withStyle` V.bold) + , (attrName "failure", fg V.brightRed) , (attrName "maximum", fg V.brightBlue) , (attrName "bold", fg V.white `V.withStyle` V.bold) , (attrName "tx", fg V.brightWhite) @@ -79,6 +81,9 @@ attrs = A.attrMap (V.white `on` V.black) bold :: Widget n -> Widget n bold = withAttr (attrName "bold") +alert :: Widget n -> Widget n +alert = withAttr (attrName "alert") + failure :: Widget n -> Widget n failure = withAttr (attrName "failure") @@ -184,6 +189,8 @@ summaryWidget env uiState = str ("Corpus size: " <> show uiState.corpusSize <> " seqs") <=> str ("New coverage: " <> timeElapsed uiState uiState.lastNewCov <> " ago") <+> fill ' ' + <=> + slitherWidget uiState.slitherSucceeded rightSide = padLeft (Pad 1) (rpcInfoWidget uiState.fetchedContracts uiState.fetchedSlots env.chainId) @@ -217,6 +224,13 @@ rpcInfoWidget contracts slots chainId = let successful = filter isJust fetches in outOf (length successful) (length fetches) +slitherWidget + :: Bool + -> Widget Name +slitherWidget slitherSucceeded = if slitherSucceeded + then success (str "Slither succeeded") + else alert (str "No Slither in use!") + outOf :: Int -> Int -> Widget n outOf n m = let style = if n == m then success else failure diff --git a/src/Main.hs b/src/Main.hs index e6ea428d..a2d136d8 100644 --- a/src/Main.hs +++ b/src/Main.hs @@ -135,6 +135,7 @@ data Options = Options , cliDeployer :: Maybe Addr , cliSender :: [Addr] , cliSeed :: Maybe Int + , cliDisableSlither :: Bool , cliCryticArgs :: Maybe String , cliSolcArgs :: Maybe String , cliSymExec :: Maybe Bool @@ -210,6 +211,8 @@ options = Options <*> optional (option auto $ long "seed" <> metavar "SEED" <> help "Run with a specific seed.") + <*> switch (long "disable-slither" + <> help "Disable running Slither.") <*> optional (option str $ long "crytic-args" <> metavar "ARGS" <> help "Additional arguments to use in crytic-compile for the compilation of the contract to test.") @@ -276,7 +279,8 @@ overrideConfig config Options{..} = do } overrideSolConf solConf = solConf - { solcArgs = fromMaybe solConf.solcArgs cliSolcArgs + { disableSlither = cliDisableSlither || solConf.disableSlither + , solcArgs = fromMaybe solConf.solcArgs cliSolcArgs , cryticArgs = maybe solConf.cryticArgs words cliCryticArgs , sender = if null cliSender then solConf.sender else Set.fromList cliSender , deployer = fromMaybe solConf.deployer cliDeployer diff --git a/tests/solidity/basic/default.yaml b/tests/solidity/basic/default.yaml index fc52ab1c..10163341 100644 --- a/tests/solidity/basic/default.yaml +++ b/tests/solidity/basic/default.yaml @@ -40,6 +40,8 @@ balanceAddr: 0xffffffff balanceContract: 0 #codeSize max code size for deployed contratcs (default 0xffffffff) codeSize: 0xffffffff +#whether or not to disable running slither (default false, uses slither) +disableSlither: false #solcArgs allows special args to solc solcArgs: "" #solcLibs is solc libraries