diff --git a/cmd/fuzz_flags.go b/cmd/fuzz_flags.go index 02300ecb..e1999804 100644 --- a/cmd/fuzz_flags.go +++ b/cmd/fuzz_flags.go @@ -68,6 +68,10 @@ func addFuzzFlags() error { // Trace all fuzzCmd.Flags().Bool("trace-all", false, fmt.Sprintf("print the execution trace for every element in a shrunken call sequence instead of only the last element (unless a config file is provided, default is %t)", defaultConfig.Fuzzing.Testing.TraceAll)) + + // Logging color + fuzzCmd.Flags().Bool("no-color", false, "disabled colored terminal output") + return nil } @@ -176,5 +180,13 @@ func updateProjectConfigWithFuzzFlags(cmd *cobra.Command, projectConfig *config. return err } } + + // Update logging color mode + if cmd.Flags().Changed("no-color") { + projectConfig.Logging.NoColor, err = cmd.Flags().GetBool("no-color") + if err != nil { + return err + } + } return nil } diff --git a/fuzzing/config/config.go b/fuzzing/config/config.go index 283e5329..eac6c371 100644 --- a/fuzzing/config/config.go +++ b/fuzzing/config/config.go @@ -3,9 +3,10 @@ package config import ( "encoding/json" "errors" + "os" + "github.com/crytic/medusa/chain/config" "github.com/rs/zerolog" - "os" "github.com/crytic/medusa/compilation" "github.com/crytic/medusa/utils" @@ -189,6 +190,9 @@ type LoggingConfig struct { // LogDirectory describes what directory log files should be outputted in/ LogDirectory being a non-empty string is // equivalent to enabling file logging. LogDirectory string `json:"logDirectory"` + + // NoColor indicates whether or not log messages should be displayed with colored formatting. + NoColor bool `json:"noColor"` } // ConsoleLoggingConfig describes the configuration options for logging to console. Note that this not being used right now diff --git a/fuzzing/config/config_defaults.go b/fuzzing/config/config_defaults.go index 2a6e92f0..e82f1a1a 100644 --- a/fuzzing/config/config_defaults.go +++ b/fuzzing/config/config_defaults.go @@ -83,6 +83,7 @@ func GetDefaultProjectConfig(platform string) (*ProjectConfig, error) { Logging: LoggingConfig{ Level: zerolog.InfoLevel, LogDirectory: "", + NoColor: false, }, } diff --git a/fuzzing/fuzzer.go b/fuzzing/fuzzer.go index b69df568..4686eba8 100644 --- a/fuzzing/fuzzer.go +++ b/fuzzing/fuzzer.go @@ -3,10 +3,6 @@ package fuzzing import ( "context" "fmt" - "github.com/crytic/medusa/fuzzing/coverage" - "github.com/crytic/medusa/logging" - "github.com/crytic/medusa/logging/colors" - "github.com/rs/zerolog" "math/big" "math/rand" "os" @@ -18,6 +14,11 @@ import ( "sync" "time" + "github.com/crytic/medusa/fuzzing/coverage" + "github.com/crytic/medusa/logging" + "github.com/crytic/medusa/logging/colors" + "github.com/rs/zerolog" + "github.com/crytic/medusa/fuzzing/calls" "github.com/crytic/medusa/utils/randomutils" "github.com/ethereum/go-ethereum/core/types" @@ -88,9 +89,13 @@ type Fuzzer struct { // NewFuzzer returns an instance of a new Fuzzer provided a project configuration, or an error if one is encountered // while initializing the code. func NewFuzzer(config config.ProjectConfig) (*Fuzzer, error) { - // Create the global logger and add stdout as an unstructured, colored output stream + // Disable colors if requested + if config.Logging.NoColor { + colors.DisableColor() + } + // Create the global logger and add stdout as an unstructured output stream logging.GlobalLogger = logging.NewLogger(config.Logging.Level) - logging.GlobalLogger.AddWriter(os.Stdout, logging.UNSTRUCTURED, true) + logging.GlobalLogger.AddWriter(os.Stdout, logging.UNSTRUCTURED, !config.Logging.NoColor) // If the log directory is a non-empty string, create a file for unstructured, un-colorized file logging if config.Logging.LogDirectory != "" { diff --git a/logging/colors/colorize_unix.go b/logging/colors/colorize_unix.go index 89ea510d..ba404662 100644 --- a/logging/colors/colorize_unix.go +++ b/logging/colors/colorize_unix.go @@ -5,11 +5,21 @@ package colors import "fmt" -// EnableColor is a no-op function for non-windows systems because we know that they support ANSI escape codes -func EnableColor() {} +var enabled = true + +// EnableColor enables the use of colors for non-windows systems. +func EnableColor() { enabled = true } + +// DisableColor disables the use of colors for non-windows systems. +func DisableColor() { enabled = false } // Colorize returns the string s wrapped in ANSI code c for non-windows systems // Source: https://github.com/rs/zerolog/blob/4fff5db29c3403bc26dee9895e12a108aacc0203/console.go func Colorize(s any, c Color) string { + // Return original string if explicitly disabled + if !enabled { + return fmt.Sprintf("%v", s) + } + return fmt.Sprintf("\x1b[%dm%v\x1b[0m", c, s) } diff --git a/logging/colors/colorize_windows.go b/logging/colors/colorize_windows.go index 87266688..4e57c269 100644 --- a/logging/colors/colorize_windows.go +++ b/logging/colors/colorize_windows.go @@ -5,8 +5,9 @@ package colors import ( "fmt" - "golang.org/x/sys/windows" "os" + + "golang.org/x/sys/windows" ) var enabled bool @@ -53,6 +54,9 @@ func EnableColor() { } } +// DisableColor will disable colors +func DisableColor() { enabled = false } + // Colorize returns the string s wrapped in ANSI code c assuming that ANSI is supported on the Windows version // Source: https://github.com/rs/zerolog/blob/4fff5db29c3403bc26dee9895e12a108aacc0203/console.go func Colorize(s any, c Color) string { diff --git a/logging/logger_test.go b/logging/logger_test.go index 15404b83..540e78e3 100644 --- a/logging/logger_test.go +++ b/logging/logger_test.go @@ -1,10 +1,15 @@ package logging import ( - "github.com/rs/zerolog" - "github.com/stretchr/testify/assert" + "bytes" + "fmt" "os" + "strings" "testing" + + "github.com/crytic/medusa/logging/colors" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" ) // TestAddAndRemoveWriter will test to Logger.AddWriter and Logger.RemoveWriter functions to ensure that they work as expected. @@ -45,3 +50,26 @@ func TestAddAndRemoveWriter(t *testing.T) { assert.Equal(t, len(logger.unstructuredColorWriters), 0) assert.Equal(t, len(logger.structuredWriters), 0) } + +// TestDisabledColors verifies the behavior of the unstructured colored logger when colors are disabled, +// ensuring that it does not output colors when the color feature is turned off. +func TestDisabledColors(t *testing.T) { + // Create a base logger + logger := NewLogger(zerolog.InfoLevel) + + // Add colorized logger + var buf bytes.Buffer + logger.AddWriter(&buf, UNSTRUCTURED, true) + + // We should expect the underlying data structures are correctly updated + assert.Equal(t, len(logger.unstructuredColorWriters), 1) + + // Disable colors and log msg + colors.DisableColor() + logger.Info("foo") + + // Ensure that msg doesn't include colors afterwards + prefix := fmt.Sprintf("%s %s", colors.LEFT_ARROW, "foo") + _, ok := strings.CutPrefix(buf.String(), prefix) + assert.True(t, ok) +}