diff --git a/.github/images/scan_all.png b/.github/images/scan_all.png index 06711ed..455f2d6 100644 Binary files a/.github/images/scan_all.png and b/.github/images/scan_all.png differ diff --git a/.github/images/scan_all_images.gif b/.github/images/scan_all_images.gif index 123b916..9e3403d 100644 Binary files a/.github/images/scan_all_images.gif and b/.github/images/scan_all_images.gif differ diff --git a/.github/images/scan_aws_account.gif b/.github/images/scan_aws_account.gif index 6d4df42..076dc69 100644 Binary files a/.github/images/scan_aws_account.gif and b/.github/images/scan_aws_account.gif differ diff --git a/.github/images/scan_filesystem.gif b/.github/images/scan_filesystem.gif index eedcf71..4a55cd2 100644 Binary files a/.github/images/scan_filesystem.gif and b/.github/images/scan_filesystem.gif differ diff --git a/.github/images/scan_individual_images.gif b/.github/images/scan_individual_images.gif index 8f68927..271f47c 100644 Binary files a/.github/images/scan_individual_images.gif and b/.github/images/scan_individual_images.gif differ diff --git a/.github/images/scan_remote_image.gif b/.github/images/scan_remote_image.gif index d134cdc..6b18261 100644 Binary files a/.github/images/scan_remote_image.gif and b/.github/images/scan_remote_image.gif differ diff --git a/.github/images/settings.gif b/.github/images/settings.gif new file mode 100644 index 0000000..3e01eac Binary files /dev/null and b/.github/images/settings.gif differ diff --git a/README.md b/README.md index 29c85a6..eec4679 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,16 @@ lazydocker, lazynpm, lazygit) - File System Scanning - [Scan a filesystem for vulnerabilities and misconfigurations](#scanning-a-filesystem) +## What does it do + +lazytrivy will run Trivy in a docker container and display the results in a terminal UI, the intention is that this will make it more stable across all platforms. + +When running a Filesystem scan, lazytrivy will mount the target dir in the container and run Trivy against it. + +Trivy intermittently downloads the latest DB - while lazytrivy maintains a cache, if you experience a delay in the scanning of an image or filesystem, it is likely trivy is running a download. + +If you're interested in seeing what's happening behind the scenes in Docker, I'd thoroughly recommend using [LazyDocker](https://github.com/jesseduffield/lazydocker). + ## Installation @@ -49,13 +59,24 @@ A config file can be added to `~/.config/lazytrivy/config.yml` to set default op ```yaml aws: - accountno: "1234567890981" - region: eu-west-1 + accountno: "464897523927" + region: us-east-1 vulnerability: - ignoreunfixed: false -cachedirectory: /home/owen/.cache/trivy -debug: false + ignoreunfixed: false +filesystem: + scansecrets: true + scanmisconfiguration: true + scanvulnerabilities: true +cachedirectory: ~/.cache +debug: true +trace: false + ``` +#### Config via UI + +Settings can be adjusted via the UI by pressing the `,` key at any time. + +![Settings](./.github/images/settings.gif) By setting `debug` to true, additional logs will be generated in `/tmp/lazytrivy.log` @@ -64,9 +85,41 @@ By setting `debug` to true, additional logs will be generated in `/tmp/lazytrivy `lazytrivy` is super easy to use, just run it with the following command: ```bash -lazytrivy +lazytrivy --help + +Usage: + lazytrivy [command] + +Available Commands: + aws Launch lazytrivy in aws scanning mode + filesystem Launch lazytrivy in filesystem scanning mode + help Help about any command + image Launch lazytrivy in image scanning mode + +Flags: + --debug Launch with debug logging + --docker-host string Docker host to connect to (default "unix:///var/run/docker.sock") + -h, --help help for lazytrivy + --trace Launch with trace logging + +Use "lazytrivy [command] --help" for more information about a command. + ``` +### Viewing logs + +Logs are generated in `$HOME/.lazytrivy/logs/lazytrivy.log` with the default level at `info`. You can change the log level by setting the `--debug` flag. + +To get even more information (more than you need), you can set the `--trace` flag. This will generate a lot of logs, so be careful and most of it is for tracking the position of the cursor, Docker events etc. + +### Setting the docker host + +By default, lazytrivy will connect to the docker daemon on the local machine by looking at the current context. + +The default docker host is `unix:///var/run/docker.sock`. If you are running Docker on a remote host, you can set the docker host with the `--docker-host` flag. + +```bash + ### Starting in a specific mode You can start `lazytrivy` in a specific mode using `aws`, `images` or `filesystem`: @@ -77,7 +130,7 @@ For example, to scan a specific filesystem folder, you could run: lazytrivy fs --path /home/owen/code/github/owenrumney/example ``` -This will start in that mode. +This will start in filesystem mode pointing to the specified path. If no path is provided it will point to the current working directory. ### Scanning all local images diff --git a/internal/cmd/flags.go b/internal/cmd/flags.go index 37acfaf..f138b14 100644 --- a/internal/cmd/flags.go +++ b/internal/cmd/flags.go @@ -26,8 +26,6 @@ func createGeneralFlags() *pflag.FlagSet { func createFilesystemFlags() *pflag.FlagSet { filesystemFlags := pflag.NewFlagSet("filesystem", pflag.ExitOnError) - filesystemFlags.StringVar(&scanPath, "path", "", "Path to scan") - return filesystemFlags } diff --git a/internal/cmd/root.go b/internal/cmd/root.go index 839c68d..981958f 100644 --- a/internal/cmd/root.go +++ b/internal/cmd/root.go @@ -22,8 +22,9 @@ var cmdAWS = &cobra.Command{ } var cmdFS = &cobra.Command{ - Use: "filesystem", - Short: "Launch lazytrivy in filesystem scanning mode", + Use: "filesystem", + Aliases: []string{"fs"}, + Short: "Launch lazytrivy in filesystem scanning mode", RunE: func(cmd *cobra.Command, args []string) error { return startGUI(widgets.FileSystemTab) }, @@ -43,9 +44,14 @@ func GetRootCmd() *cobra.Command { rootCmd := &cobra.Command{ Use: "lazytrivy", } + rootCmd.AddCommand(cmdImage) rootCmd.AddCommand(cmdAWS) rootCmd.AddCommand(cmdFS) + rootCmd.Flags().AddFlagSet(generalFlags) + + rootCmd.CompletionOptions.HiddenDefaultCmd = true + return rootCmd } diff --git a/internal/cmd/start.go b/internal/cmd/start.go index 7eafc9c..d0ad813 100644 --- a/internal/cmd/start.go +++ b/internal/cmd/start.go @@ -1,7 +1,6 @@ package cmd import ( - "fmt" "os" "github.com/owenrumney/lazytrivy/pkg/config" @@ -11,9 +10,11 @@ import ( ) func startGUI(tab widgets.Tab) error { + logger.Configure() + workingDir, err := os.Getwd() if err != nil { - fail(err) + return err } cfg, err := config.Load() @@ -40,15 +41,14 @@ func startGUI(tab widgets.Tab) error { control, err := gui.New(tab, cfg) if err != nil { - fail(err) - + return err } defer control.Close() // create the widgets if err := control.CreateWidgets(); err != nil { - fail(err) + return err } @@ -57,20 +57,6 @@ func startGUI(tab widgets.Tab) error { return err } - if control.IsDockerDesktop() { - control.ShowDockerDesktopWarning() - } - // Enter the run loop - it's all in the gui from this point on - if err := control.Run(); err != nil { - fail(err) - - } - - return nil -} - -func fail(err error) { - _, _ = fmt.Fprintf(os.Stderr, "Error: %s", err) - os.Exit(1) + return control.Run() } diff --git a/main.go b/main.go index 7b8325c..34a783b 100644 --- a/main.go +++ b/main.go @@ -4,15 +4,22 @@ import ( "os" "github.com/owenrumney/lazytrivy/internal/cmd" + "github.com/owenrumney/lazytrivy/pkg/logger" ) func main() { + // configure the logger + logger.Configure() // if no args are passed, open in image mode if len(os.Args[1:]) == 0 { + logger.Infof("No arguments passed, opening in image mode") os.Args = append(os.Args, "image") } rootCmd := cmd.GetRootCmd() - _ = rootCmd.Execute() + if err := rootCmd.Execute(); err != nil { + logger.Errorf("Error executing command: %v", err) + os.Exit(1) + } } diff --git a/pkg/config/config.go b/pkg/config/config.go index 9ab1188..95fdab2 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -39,9 +39,11 @@ var defaultConfig *Config var configPath string -func init() { +func createDefaultConfig() error { + logger.Debugf("Creating default config") homeDir, err := os.UserHomeDir() if err != nil { + logger.Errorf("Error getting user home directory: %s", err) homeDir = os.TempDir() } trivyCacheDir := filepath.Join(homeDir, ".cache", "trivy") @@ -64,17 +66,26 @@ func init() { } configDir, err := os.UserConfigDir() if err != nil { - return + return err } lazyTrivyConfigDir := filepath.Join(configDir, "lazytrivy") - _ = os.MkdirAll(lazyTrivyConfigDir, os.ModePerm) + if err := os.MkdirAll(lazyTrivyConfigDir, os.ModePerm); err != nil { + return err + } configPath = filepath.Join(lazyTrivyConfigDir, "config.yaml") + + return nil } func Load() (*Config, error) { + + if err := createDefaultConfig(); err != nil { + return nil, err + } + logger.Debugf("Attempting to load config from %s", configPath) if _, err := os.Stat(configPath); err != nil { logger.Debugf("No config file found, using defaults") @@ -91,12 +102,7 @@ func Load() (*Config, error) { logger.Errorf("Error parsing config file: %s", err) return defaultConfig, err } - - cwd, err := os.Getwd() - if err != nil { - return nil, err - } - defaultConfig.Filesystem.WorkingDirectory = cwd + logger.Infof("Loaded config from %s", configPath) return defaultConfig, nil } @@ -114,6 +120,7 @@ func Save(config *Config) error { return err } + logger.Infof("Saved config to %s", configPath) return nil } diff --git a/pkg/controllers/aws/aws.go b/pkg/controllers/aws/aws.go index c5e0534..fba8b23 100644 --- a/pkg/controllers/aws/aws.go +++ b/pkg/controllers/aws/aws.go @@ -97,42 +97,6 @@ func (c *Controller) Initialise() error { return outerErr } -func (c *Controller) refreshServices() error { - logger.Debugf("getting caches services") - services, err := c.accountRegionCacheServices(c.Config.AWS.AccountNo, c.Config.AWS.Region) - if err != nil { - return err - } - - logger.Debugf("Updating the services view with the identified services") - if v, ok := c.Views[widgets.Services].(*widgets.ServicesWidget); ok { - if err := v.RefreshServices(services, 20); err != nil { - return err - } - } - return nil -} - -func (c *Controller) UpdateAccount(account string) error { - logger.Debugf("Updating the AWS account details in the config") - c.Config.AWS.AccountNo = account - c.Config.AWS.Region = "us-east-1" - if err := c.Config.Save(); err != nil { - return err - } - - return c.update() -} - -func (c *Controller) UpdateRegion(region string) error { - logger.Debugf("Updating the AWS region details in the config") - c.Config.AWS.Region = region - if err := c.Config.Save(); err != nil { - return err - } - return c.update() -} - func (c *Controller) update() error { if v, ok := c.Views[widgets.Account]; ok { if a, ok := v.(*widgets.AccountWidget); ok { @@ -235,7 +199,7 @@ func (c *Controller) discoverAccount(region string) (string, string, error) { } if regionEnv, ok := os.LookupEnv("AWS_REGION"); ok { - logger.Debugf("Using AWS_REGION environment variable") + logger.Infof("Using AWS_REGION environment variable") cfg.Region = regionEnv } @@ -259,7 +223,6 @@ func (c *Controller) scanAccount(gui *gocui.Gui, _ *gocui.View) error { if err != nil { if strings.HasPrefix(err.Error(), "failed to discover AWS caller identity") { c.UpdateStatus("Failed to discover AWS credentials.") - logger.Errorf("failed to discover AWS credentials: %v", err) return NewErrNoValidCredentials() } return err @@ -268,7 +231,6 @@ func (c *Controller) scanAccount(gui *gocui.Gui, _ *gocui.View) error { c.UpdateStatus("Checking credentials for account...") if account != c.Config.AWS.AccountNo && c.Config.AWS.AccountNo != "" { c.UpdateStatus("Account number does not match credentials.") - logger.Errorf("Account number does not match credentials.") return fmt.Errorf("account number mismatch: %s != %s", account, c.Config.AWS.AccountNo) } diff --git a/pkg/controllers/aws/help.go b/pkg/controllers/aws/help.go index af8ba9d..6f0c1cf 100644 --- a/pkg/controllers/aws/help.go +++ b/pkg/controllers/aws/help.go @@ -17,10 +17,7 @@ var helpCommands = []string{ } func help(gui *gocui.Gui, _ *gocui.View) error { - - w, h := gui.Size() - - v := widgets.NewAnnouncementWidget("help", "Help", w, h, helpCommands, gui) + v := widgets.NewAnnouncementWidget("help", "Help", helpCommands, gui) if err := gui.SetKeybinding("help", gocui.KeyEsc, gocui.ModNone, func(gui *gocui.Gui, _ *gocui.View) error { if _, err := gui.SetCurrentView("services"); err != nil { diff --git a/pkg/controllers/aws/service.go b/pkg/controllers/aws/service.go index e578e87..7237bf7 100644 --- a/pkg/controllers/aws/service.go +++ b/pkg/controllers/aws/service.go @@ -47,6 +47,26 @@ func (c *Controller) ScanService(_ context.Context) { }) } +func (c *Controller) UpdateAccount(account string) error { + logger.Debugf("Updating the AWS account details in the config") + c.Config.AWS.AccountNo = account + c.Config.AWS.Region = "us-east-1" + if err := c.Config.Save(); err != nil { + return err + } + + return c.update() +} + +func (c *Controller) UpdateRegion(region string) error { + logger.Debugf("Updating the AWS region details in the config") + c.Config.AWS.Region = region + if err := c.Config.Save(); err != nil { + return err + } + return c.update() +} + func (c *Controller) CancelCurrentScan(_ *gocui.Gui, _ *gocui.View) error { c.Lock() defer c.Unlock() @@ -58,3 +78,19 @@ func (c *Controller) CancelCurrentScan(_ *gocui.Gui, _ *gocui.View) error { } return nil } + +func (c *Controller) refreshServices() error { + logger.Debugf("getting caches services") + services, err := c.accountRegionCacheServices(c.Config.AWS.AccountNo, c.Config.AWS.Region) + if err != nil { + return err + } + + logger.Debugf("Updating the services view with the identified services") + if v, ok := c.Views[widgets.Services].(*widgets.ServicesWidget); ok { + if err := v.RefreshServices(services, 20); err != nil { + return err + } + } + return nil +} diff --git a/pkg/controllers/base/controller.go b/pkg/controllers/base/controller.go index 71b1269..8308e76 100644 --- a/pkg/controllers/base/controller.go +++ b/pkg/controllers/base/controller.go @@ -8,6 +8,7 @@ import ( "github.com/awesome-gocui/gocui" "github.com/owenrumney/lazytrivy/pkg/config" "github.com/owenrumney/lazytrivy/pkg/dockerClient" + "github.com/owenrumney/lazytrivy/pkg/logger" "github.com/owenrumney/lazytrivy/pkg/widgets" ) @@ -50,6 +51,10 @@ func (c *Controller) RefreshView(viewName string) { } func (c *Controller) UpdateStatus(status string) { + if status != "" { + logger.Infof(status) + } + if v, ok := c.Views[widgets.Status].(*widgets.StatusWidget); ok { v.UpdateStatus(status) c.Cui.Update(func(_ *gocui.Gui) error { diff --git a/pkg/controllers/filesystem/fs.go b/pkg/controllers/filesystem/fs.go index c6ac5de..318b3b2 100644 --- a/pkg/controllers/filesystem/fs.go +++ b/pkg/controllers/filesystem/fs.go @@ -121,7 +121,6 @@ func (c *Controller) RenderFilesystemFileReport() error { return fmt.Errorf("failed to set current view: %w", err) } } else { - width, height := c.Cui.Size() lines := []string{ "Great News!", @@ -129,7 +128,7 @@ func (c *Controller) RenderFilesystemFileReport() error { "No Issues found!", } - announcement := widgets.NewAnnouncementWidget(widgets.Announcement, "No Results", width, height, lines, c.Cui) + announcement := widgets.NewAnnouncementWidget(widgets.Announcement, "No Results", lines, c.Cui) _ = announcement.Layout(c.Cui) _, _ = c.Cui.SetCurrentView(widgets.Announcement) diff --git a/pkg/controllers/filesystem/help.go b/pkg/controllers/filesystem/help.go index 2c22c21..a56617e 100644 --- a/pkg/controllers/filesystem/help.go +++ b/pkg/controllers/filesystem/help.go @@ -17,9 +17,7 @@ var helpCommands = []string{ func help(gui *gocui.Gui, _ *gocui.View) error { - w, h := gui.Size() - - v := widgets.NewAnnouncementWidget("help", "Help", w, h, helpCommands, gui) + v := widgets.NewAnnouncementWidget("help", "Help", helpCommands, gui) if err := gui.SetKeybinding("help", gocui.KeyEsc, gocui.ModNone, func(gui *gocui.Gui, _ *gocui.View) error { if _, err := gui.SetCurrentView("services"); err != nil { diff --git a/pkg/controllers/gui/gui.go b/pkg/controllers/gui/gui.go index ea2e3b0..2c1ecf0 100644 --- a/pkg/controllers/gui/gui.go +++ b/pkg/controllers/gui/gui.go @@ -34,13 +34,15 @@ func New(tab widgets.Tab, cfg *config.Config) (*Controller, error) { return nil, fmt.Errorf("failed to create gui: %w", err) } - dkrClient := dockerClient.NewClient(cfg) + dkrClient, err := dockerClient.NewClient(cfg) + if err != nil { + return nil, err + } mainController := &Controller{ cui: cui, dockerClient: dkrClient, - - config: cfg, + config: cfg, } switch tab { @@ -66,7 +68,7 @@ func (c *Controller) CreateWidgets() error { func (c *Controller) Initialise() error { if c.config.Debug == true { - logger.EnableDebugging() + logger.Configure() } if c.config.Trace == true { logger.EnableTracing() @@ -161,26 +163,6 @@ func (c *Controller) switchMode(gui *gocui.Gui, _ *gocui.View) error { } -func (c *Controller) IsDockerDesktop() bool { - return c.dockerClient.IsDockerDesktop() -} - -func (c *Controller) ShowDockerDesktopWarning() { - lines := []string{ - "", - "It looks like you're using Docker Desktop!", - "Most functionality works, but scanning of locally built images is not supported.", - "", - } - - x, y := c.cui.Size() - announcement := widgets.NewAnnouncementWidget(widgets.Announcement, "Warning", x, y, lines, c.cui) - if err := announcement.Layout(c.cui); err != nil { - logger.Errorf("failed to create announcement widget: %v", err) - } - -} - func (c *Controller) UpdateStatus(status string) { // TODO implement me panic("implement me") diff --git a/pkg/controllers/vulnerabilities/help.go b/pkg/controllers/vulnerabilities/help.go index 47b29e8..cae73b3 100644 --- a/pkg/controllers/vulnerabilities/help.go +++ b/pkg/controllers/vulnerabilities/help.go @@ -17,9 +17,7 @@ var helpCommands = []string{ func help(gui *gocui.Gui, _ *gocui.View) error { - w, h := gui.Size() - - v := widgets.NewAnnouncementWidget("help", "Help", w, h, helpCommands, gui) + v := widgets.NewAnnouncementWidget("help", "Help", helpCommands, gui) gui.Update(func(g *gocui.Gui) error { if err := v.Layout(g); err != nil { diff --git a/pkg/controllers/vulnerabilities/image.go b/pkg/controllers/vulnerabilities/image.go index f97bb44..b2c625f 100644 --- a/pkg/controllers/vulnerabilities/image.go +++ b/pkg/controllers/vulnerabilities/image.go @@ -13,7 +13,10 @@ import ( ) func (c *Controller) SetSelected(selected string) { - logger.Debugf("Setting selected image to %s", selected) + if selected == "" { + return + } + c.UpdateStatus(fmt.Sprintf("Selected: %s", selected)) c.setSelected(strings.TrimSpace(selected)) } @@ -73,9 +76,7 @@ func (c *Controller) ScanAllImages(gui *gocui.Gui, _ *gocui.View) error { "Press 't' to terminate if you get bored", "", } - x, y := gui.Size() - - announce := widgets.NewAnnouncementWidget(widgets.Announcement, "Caution", x, y, lines, c.Cui, widgets.Status) + announce := widgets.NewAnnouncementWidget(widgets.Announcement, "Caution", lines, c.Cui, widgets.Status) if err := announce.Layout(gui); err != nil { return err } @@ -123,13 +124,14 @@ func (c *Controller) returnToResults() { } func (c *Controller) RefreshImages() error { - logger.Debugf("refreshing images") c.UpdateStatus("Refreshing images") defer c.ClearStatus() images := c.DockerClient.ListImages() - logger.Debugf("found %d images", len(images)) - c.updateImages(images) + + w, _ := c.Cui.Size() + + c.updateImages(images, w/4) if v, ok := c.Views[widgets.Images].(*widgets.ImagesWidget); ok { return v.RefreshImages(c.images, c.imageWidth) diff --git a/pkg/controllers/vulnerabilities/state.go b/pkg/controllers/vulnerabilities/state.go index 85e992c..560e99e 100644 --- a/pkg/controllers/vulnerabilities/state.go +++ b/pkg/controllers/vulnerabilities/state.go @@ -9,12 +9,12 @@ type state struct { imageWidth int } -func (s *state) updateImages(images []string) { +func (s *state) updateImages(images []string, maxWidth int) { s.stateLock.Lock() defer s.stateLock.Unlock() s.images = images - s.imageWidth = getLongestImageName(images) + s.imageWidth = getLongestImageName(images, maxWidth) s.selectedImage = "" } @@ -24,10 +24,10 @@ func (s *state) setSelected(selectedImage string) { s.selectedImage = selectedImage } -func getLongestImageName(images []string) int { +func getLongestImageName(images []string, maxWidth int) int { imageWidth := 0 for _, image := range images { - if len(image) > imageWidth { + if len(image) > imageWidth && len(image) < maxWidth { imageWidth = len(image) } } diff --git a/pkg/dockerClient/client.go b/pkg/dockerClient/client.go index bbb516f..fe91c6f 100644 --- a/pkg/dockerClient/client.go +++ b/pkg/dockerClient/client.go @@ -33,11 +33,12 @@ type Client struct { lazyTrivyImagePresent bool } -func NewClient(cfg *config.Config) *Client { +func NewClient(cfg *config.Config) (*Client, error) { endpoint, _, err := getHostEndpoint() if err != nil { logger.Errorf("Error getting docker context: %s", err) + return nil, err } if cfg.DockerEndpoint != "" { @@ -48,7 +49,9 @@ func NewClient(cfg *config.Config) *Client { cli, err := client.NewClientWithOpts(client.WithHost(endpoint), client.WithAPIVersionNegotiation()) if err != nil { - fmt.Printf("Error creating docker client: %s", err) + logger.Errorf("Error creating docker client: %s", err) + return nil, err + } if _, err := cli.ContainerList(context.Background(), types.ContainerListOptions{}); err != nil { @@ -64,11 +67,7 @@ func NewClient(cfg *config.Config) *Client { client: cli, endpoint: endpoint, socketPath: socketPath, - } -} - -func (c *Client) IsDockerDesktop() bool { - return strings.Contains(strings.ToLower(c.endpoint), "desktop") + }, nil } func (c *Client) scan(ctx context.Context, command []string, scanTarget string, env []string, progress Progress, scanImageName string, additionalBinds ...string) (*output.Report, error) { @@ -93,11 +92,11 @@ func (c *Client) scan(ctx context.Context, command []string, scanTarget string, _, _ = io.Copy(io.Discard, resp) } } - logger.Debugf("Running trivy scan with command %s", command) + logger.Tracef("Running trivy scan with command %s", command) userHomeDir, err := os.UserHomeDir() if err != nil { - logger.Debugf("Error getting user home dir: %s", err) + logger.Errorf("Error getting user home dir: %s", err) userHomeDir = os.TempDir() } @@ -132,7 +131,7 @@ func (c *Client) scan(ctx context.Context, command []string, scanTarget string, // make sure we kill the container defer func() { - logger.Debugf("Removing container %s", cont.ID) + logger.Tracef("Removing container %s", cont.ID) _ = c.client.ContainerRemove(ctx, cont.ID, types.ContainerRemoveOptions{}) }() diff --git a/pkg/dockerClient/fs.go b/pkg/dockerClient/fs.go index c64c536..c689073 100644 --- a/pkg/dockerClient/fs.go +++ b/pkg/dockerClient/fs.go @@ -5,14 +5,11 @@ import ( "fmt" "strings" - "github.com/owenrumney/lazytrivy/pkg/logger" "github.com/owenrumney/lazytrivy/pkg/output" ) func (c *Client) ScanFilesystem(ctx context.Context, path string, requiredChecks []string, progress Progress) (*output.Report, error) { - logger.Debugf("Scanning filesystem %s", path) checks := strings.Join(requiredChecks, ",") - progress.UpdateStatus(fmt.Sprintf("Scanning filesystem %s...", path)) command := []string{"fs", "--quiet", "--security-checks", checks, "-f=json", "/target"} diff --git a/pkg/dockerClient/images.go b/pkg/dockerClient/images.go index e4e337d..4d468c9 100644 --- a/pkg/dockerClient/images.go +++ b/pkg/dockerClient/images.go @@ -54,15 +54,10 @@ func (c *Client) ListImages() []string { func (c *Client) ScanAllImages(ctx context.Context, progress Progress, reportComplete func(report *output.Report) error) error { for _, imageName := range c.imageNames { - progress.UpdateStatus(fmt.Sprintf("Scanning image %s...", imageName)) - logger.Debugf("Scanning image %s", imageName) - report, err := c.ScanImage(ctx, imageName, progress) if err != nil { return err } - progress.UpdateStatus(fmt.Sprintf("Scanning image %s...done", imageName)) - logger.Debugf("Scanning image %s...done", imageName) if err := reportComplete(report); err != nil { logger.Errorf("Error reporting scan results: %s", err) ctx.Done() @@ -79,7 +74,6 @@ func (c *Client) ScanAllImages(ctx context.Context, progress Progress, reportCom } func (c *Client) ScanImage(ctx context.Context, imageName string, progress Progress) (*output.Report, error) { - logger.Debugf("Scanning image %s", imageName) progress.UpdateStatus(fmt.Sprintf("Scanning image %s...", imageName)) command := []string{"image", "-f=json", imageName} diff --git a/pkg/logger/debug.go b/pkg/logger/log.go similarity index 66% rename from pkg/logger/debug.go rename to pkg/logger/log.go index ef275c4..58aca91 100644 --- a/pkg/logger/debug.go +++ b/pkg/logger/log.go @@ -9,12 +9,11 @@ import ( var ( debugEnabled bool + traceEnabled bool debugFile *os.File ) -func EnableDebugging() { - debugEnabled = true - +func Configure() { home, err := os.UserHomeDir() if err != nil { home = "/" @@ -26,16 +25,29 @@ func EnableDebugging() { debugFile, _ = os.OpenFile(logFile, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) // nolint: nosnakecase } +func EnableDebugging() { + debugEnabled = true +} + func EnableTracing() { - EnableDebugging() + traceEnabled = true } func Tracef(format string, args ...interface{}) { - logf("TRACE", format, args...) + if traceEnabled { + logf("TRACE", format, args...) + } + } func Debugf(format string, args ...interface{}) { - logf("DEBUG", format, args...) + if debugEnabled { + logf("DEBUG", format, args...) + } +} + +func Infof(format string, args ...interface{}) { + logf("INFO", format, args...) } func Errorf(format string, args ...interface{}) { @@ -43,8 +55,6 @@ func Errorf(format string, args ...interface{}) { } func logf(level string, format string, args ...interface{}) { - if debugEnabled { - _, _ = fmt.Fprintf(debugFile, fmt.Sprintf("%s [%s] ", time.Now().Format(time.RFC3339), level)+fmt.Sprintf(format, args...)) - _, _ = fmt.Fprintln(debugFile) - } + _, _ = fmt.Fprintf(debugFile, fmt.Sprintf("%s\t[%s]\t", time.Now().Format(time.RFC3339), level)+fmt.Sprintf(format, args...)) + _, _ = fmt.Fprintln(debugFile) } diff --git a/pkg/widgets/announce.go b/pkg/widgets/announce.go index ad36a1b..02ef737 100644 --- a/pkg/widgets/announce.go +++ b/pkg/widgets/announce.go @@ -23,7 +23,10 @@ func (w *AnnouncementWidget) RefreshView() { panic("unimplemented") } -func NewAnnouncementWidget(name, title string, width, height int, lines []string, ctx *gocui.Gui, nextView ...string) *AnnouncementWidget { +func NewAnnouncementWidget(name, title string, lines []string, ctx *gocui.Gui, nextView ...string) *AnnouncementWidget { + + width, height := ctx.Size() + nextViewName := Results if len(nextView) > 0 { nextViewName = nextView[0] diff --git a/pkg/widgets/aws_results.go b/pkg/widgets/aws_results.go index ef80417..9a09988 100644 --- a/pkg/widgets/aws_results.go +++ b/pkg/widgets/aws_results.go @@ -148,8 +148,9 @@ func (w *AWSResultWidget) UpdateResultsTable(reports []*output.Report, g *gocui. w.v.Clear() w.body = []string{} + w.results = []*output.Result{} + if w.currentReport == nil || !w.currentReport.HasIssues() { - width, height := w.v.Size() lines := []string{ "Great News!", @@ -157,7 +158,7 @@ func (w *AWSResultWidget) UpdateResultsTable(reports []*output.Report, g *gocui. "No misconfigurations found!", } - announcement := NewAnnouncementWidget(Announcement, "No Results", width, height, lines, g, Services) + announcement := NewAnnouncementWidget(Announcement, "No Results", lines, g, Services) _ = announcement.Layout(g) _, _ = g.SetCurrentView(Announcement) @@ -216,7 +217,6 @@ func (w *AWSResultWidget) RenderReport(report *output.Report, severity string) { func (w *AWSResultWidget) GenerateFilteredReport(severity string, g *gocui.Gui) { if w.currentResult == nil || len(w.currentResult.Issues) == 0 { - width, height := w.v.Size() lines := []string{ "Great News!", @@ -224,7 +224,7 @@ func (w *AWSResultWidget) GenerateFilteredReport(severity string, g *gocui.Gui) "No misconfigurations found!", } - announcement := NewAnnouncementWidget(Announcement, "No Results", width, height, lines, g) + announcement := NewAnnouncementWidget(Announcement, "No Results", lines, g) _ = announcement.Layout(g) _, _ = g.SetCurrentView(Announcement) @@ -252,7 +252,7 @@ func (w *AWSResultWidget) GenerateFilteredReport(severity string, g *gocui.Gui) width, _ := w.v.Size() - var bodyContent []string //nolint:prealloc + var bodyContent []string //nolint:preallocb headers := []string{ fmt.Sprintf(" %s", w.currentResult.Target), diff --git a/pkg/widgets/files.go b/pkg/widgets/files.go index 28e6f3e..fc0e565 100644 --- a/pkg/widgets/files.go +++ b/pkg/widgets/files.go @@ -109,6 +109,8 @@ func (w *FilesWidget) RefreshFiles(files []string, fileWidth int) error { w.v.Highlight = true w.ctx.RefreshView(Files) _ = w.v.SetCursor(0, 0) + w.ctx.SetSelected(w.SelectTarget()) + w.ctx.ShowTarget(context.Background()) return nil } diff --git a/pkg/widgets/fs_result.go b/pkg/widgets/fs_result.go index 3b75d45..7dbfc23 100644 --- a/pkg/widgets/fs_result.go +++ b/pkg/widgets/fs_result.go @@ -149,7 +149,6 @@ func (w *FSResultWidget) UpdateResultsTable(reports []*output.Report, g *gocui.G w.body = []string{} if w.currentReport == nil || !w.currentReport.HasIssues() { - width, height := w.v.Size() lines := []string{ "Great News!", @@ -157,7 +156,7 @@ func (w *FSResultWidget) UpdateResultsTable(reports []*output.Report, g *gocui.G "No misconfigurations found!", } - announcement := NewAnnouncementWidget(Announcement, "No Results", width, height, lines, g) + announcement := NewAnnouncementWidget(Announcement, "No Results", lines, g) _ = announcement.Layout(g) _, _ = g.SetCurrentView(Announcement) diff --git a/pkg/widgets/image_results.go b/pkg/widgets/image_results.go index d4c6021..1d41c81 100644 --- a/pkg/widgets/image_results.go +++ b/pkg/widgets/image_results.go @@ -157,7 +157,6 @@ func (w *ImageResultWidget) RenderReport(report *output.Report, severity string, w.body = []string{} if w.currentReport == nil || !w.currentReport.HasIssues() { - width, height := w.v.Size() lines := []string{ "Great News!", @@ -165,7 +164,7 @@ func (w *ImageResultWidget) RenderReport(report *output.Report, severity string, "No vulnerabilities found!", } - announcement := NewAnnouncementWidget(Announcement, "No Results", width, height, lines, cui, Images) + announcement := NewAnnouncementWidget(Announcement, "No Results", lines, cui, Images) _ = announcement.Layout(cui) _, _ = cui.SetCurrentView(Announcement)