Skip to content

Commit

Permalink
adding filesystem scanning (#15)
Browse files Browse the repository at this point in the history
* chore: Updating documentation and license


* feat: Add filesystem scanning


Signed-off-by: Owen Rumney <[email protected]>
  • Loading branch information
owenrumney authored Sep 12, 2022
1 parent aac5164 commit 2f8b40c
Show file tree
Hide file tree
Showing 33 changed files with 1,798 additions and 361 deletions.
Binary file added .github/images/scan_filesystem.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ lazydocker, lazynpm, lazygit)

![Scan All Images](./.github/images/scan_all.png)

## Features

- Image Scanning
- [Scan all images on your system](#scanning-all-local-images)
- [Scan a single image](#scanning-a-specific-image)
- [Scan a remote image](#scanning-a-remote-image)
- AWS Scanning
- [Scan your cloud account](#scanning-an-aws-account)
- File System Scanning
- [Scan a filesystem for vulnerabilities and misconfigurations](#scanning-a-filesystem)


## Installation

### Prerequisites
Expand Down Expand Up @@ -54,6 +66,19 @@ By setting `debug` to true, additional logs will be generated in `/tmp/lazytrivy
lazytrivy
```

### Starting in a specific mode

You can start `lazytrivy` in a specific mode using `aws`, `images` or `filesystem`:

For example, to scan a specific filesystem folder, you could run:

```bash
lazytrivy fs /home/owen/code/github/owenrumney/example
```

This will start in that mode.


### Scanning all local images

Pressing `a` will scan all the images that are shown in the left hand pane. On completion, you will be shown a
Expand Down Expand Up @@ -85,3 +110,9 @@ To scan an AWS account, you can use the `w` key to switch to AWS mode, from ther
![Scanning an AWS account](./.github/images/scan_aws_account.gif)

By pressing `r` you can switch region in results you already have.

### Scanning a filesystem

To scan a filessystem, you can use the `w` key to switch to Filesystem mode, from there you will get all of the vulnerabilities, misconfigurations and secrets from the current working directory

![Scanning a filesystem](./.github/images/scan_filesystem.gif)
53 changes: 47 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,74 @@ import (
"os"

"github.com/owenrumney/lazytrivy/pkg/controllers/gui"
"github.com/owenrumney/lazytrivy/pkg/widgets"
)

func main() {
control, err := gui.New()

if len(os.Args) == 1 {
if err := startGUI(widgets.VulnerabilitiesTab, ""); err != nil {
fail(err)
}
return
} else if os.Args[1] == "aws" {
if err := startGUI(widgets.AWSTab, ""); err != nil {
fail(err)
}
return
} else if os.Args[1] == "fs" {
cwd, err := os.Getwd()
if err != nil {
fail(err)
}
if len(os.Args) == 3 {
if _, err := os.Stat(os.Args[2]); err == nil {
cwd = os.Args[2]
}
}
if err := startGUI(widgets.FileSystemTab, cwd); err != nil {
fail(err)
}
return
} else {
if err := startGUI(widgets.VulnerabilitiesTab, ""); err != nil {
fail(err)
}
return
}

}

func fail(err error) {
_, _ = fmt.Fprintf(os.Stderr, "Error: %s", err)
os.Exit(1)
}

func startGUI(tab widgets.Tab, workingDir string) error {
control, err := gui.New(tab, workingDir)
if err != nil {
fail(err)

}

defer control.Close()

// create the widgets
if err := control.CreateWidgets(); err != nil {
fail(err)

}

// set up the initial view to be the images widget
if err := control.Initialise(); err != nil {
fail(err)
return err
}

// Enter the run loop - it's all in the gui from this point on
if err := control.Run(); err != nil {
fail(err)

}
}

func fail(err error) {
_, _ = fmt.Fprintf(os.Stderr, "Error: %s", err)
os.Exit(1)
return nil
}
48 changes: 22 additions & 26 deletions pkg/controllers/aws/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@ func NewAWSController(cui *gocui.Gui, dockerClient *docker.Client, cfg *config.C
}
}

func (c *Controller) CreateWidgets(manager base.Manager) error {
logger.Debugf("Creating AWS view widgets")

maxX, maxY := c.Cui.Size()
c.Views[widgets.Services] = widgets.NewServicesWidget(widgets.Services, c)
c.Views[widgets.Results] = widgets.NewAWSResultWidget(widgets.Results, c)
c.Views[widgets.Menu] = widgets.NewMenuWidget(widgets.Menu, 0, maxY-3, maxX-1, maxY-1)
c.Views[widgets.Status] = widgets.NewStatusWidget(widgets.Status)
c.Views[widgets.Account] = widgets.NewAccountWidget(widgets.Account, c.Config.AWS.AccountNo, c.Config.AWS.Region)

for _, v := range c.Views {
_ = v.Layout(c.Cui)
manager.AddViews(v)
}
manager.AddViews(gocui.ManagerFunc(c.LayoutFunc))
c.SetManager()

return nil
}

func (c *Controller) Initialise() error {
logger.Debugf("Initialising AWS controller")
var outerErr error
Expand Down Expand Up @@ -93,30 +113,6 @@ func (c *Controller) refreshServices() error {
return nil
}

func (c *Controller) CreateWidgets(manager base.Manager) error {
logger.Debugf("Creating AWS view widgets")
menuItems := []string{
"<blue>[?]</blue> help", "s<blue>[w]</blue>itch mode", "<red>[t]</red>erminate scan", "<red>[q]</red>uit",
"\n\n<yellow>Navigation: Use arrow keys to navigate and ESC to exit screens</yellow>",
}

maxX, maxY := c.Cui.Size()
c.Views[widgets.Services] = widgets.NewServicesWidget(widgets.Services, c)
c.Views[widgets.Results] = widgets.NewAWSResultWidget(widgets.Results, c)
c.Views[widgets.Menu] = widgets.NewMenuWidget(widgets.Menu, 0, maxY-3, maxX-1, maxY-1, menuItems)
c.Views[widgets.Status] = widgets.NewStatusWidget(widgets.Status)
c.Views[widgets.Account] = widgets.NewAccountWidget(widgets.Account, c.Config.AWS.AccountNo, c.Config.AWS.Region)

for _, v := range c.Views {
_ = v.Layout(c.Cui)
manager.AddViews(v)
}
manager.AddViews(gocui.ManagerFunc(c.LayoutFunc))
c.SetManager()

return nil
}

func (c *Controller) UpdateAccount(account string) error {
logger.Debugf("Updating the AWS account details in the config")
c.Config.AWS.AccountNo = account
Expand Down Expand Up @@ -189,7 +185,7 @@ func (c *Controller) switchAccount(gui *gocui.Gui, _ *gocui.View) error {

x, y := gui.Size()

accountChoices := widgets.NewChoiceWidget("choice", x/2-10, y/2-2, x/2+10, y/2+2, " Choose or ESC ", accounts, c.UpdateAccount, c)
accountChoices := widgets.NewChoiceWidget("choice", x, y, " Choose or ESC ", accounts, c.UpdateAccount, c)
if err := accountChoices.Layout(gui); err != nil {
logger.Errorf("Failed to create account choice widget. %s", err)
return fmt.Errorf("error when rendering account choices: %w", err)
Expand All @@ -211,7 +207,7 @@ func (c *Controller) switchRegion(gui *gocui.Gui, _ *gocui.View) error {
}

x, y := gui.Size()
regionChoices := widgets.NewChoiceWidget("choice", x/2-10, y/2-2, x/2+10, y/2+len(regions), " Choose or ESC ", regions, c.UpdateRegion, c)
regionChoices := widgets.NewChoiceWidget("choice", x, y, " Choose or ESC ", regions, c.UpdateRegion, c)

if err := regionChoices.Layout(gui); err != nil {
logger.Errorf("Failed to create region choice widget. %s", err)
Expand Down
1 change: 0 additions & 1 deletion pkg/controllers/base/gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ import (

type Manager interface {
AddViews(views ...gocui.Manager)
RefreshManager()
}
97 changes: 97 additions & 0 deletions pkg/controllers/filesystem/dir_utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package filesystem

import (
"fmt"
"os"
"sort"
"strings"
)

type dir struct {
dirs map[string]*dir
dirNames []string
files map[string]struct{}
fileIndex int
}

func newDir() *dir {
return &dir{
dirs: make(map[string]*dir),
files: make(map[string]struct{}),
}
}

func (d *dir) addFile(target, original string) {
sep := fmt.Sprintf("%c", os.PathSeparator)
if strings.Contains(target, ":") {

parts := strings.SplitN(target, ":", 2)
root := parts[0]
remaining := parts[1]

if _, ok := d.dirs[root]; !ok {
d.dirs[root] = newDir()
d.dirNames = append(d.dirNames, root)
}

nd := d.dirs[root]
nd.addFile(remaining, original)

} else if strings.Contains(target, sep) {
parts := strings.SplitN(target, sep, 2)
root := parts[0]
remaining := parts[1]

if _, ok := d.dirs[root]; !ok {
d.dirs[root] = newDir()
d.dirNames = append(d.dirNames, root)
}

nd := d.dirs[root]
nd.addFile(remaining, original)
} else {
fileID := fmt.Sprintf("%s|%s", target, original)

if _, ok := d.files[fileID]; !ok {
d.files[fileID] = struct{}{}
}
}
}

func (d *dir) generateTree(lines []string, depth int) []string {

sort.Strings(d.dirNames)

prefix := ""
if depth >= 0 {
prefix = fmt.Sprintf("%s└─ ", strings.Repeat(" ", depth))
depth += 3
}
for _, dirName := range d.dirNames {
if depth == -1 {
depth = 0
}
children := d.dirs[dirName]
lines = append(lines, fmt.Sprintf("%s%s", prefix, dirName))
lines = children.generateTree(lines, depth)
}

for file, _ := range d.files {
lines = append(lines, fmt.Sprintf("%s%s", prefix, file))
}

return lines
}

func createRootDir(targets []string) *dir {
sort.Slice(targets, func(i, j int) bool {
return strings.ToLower(targets[i]) < strings.ToLower(targets[j])
})

root := newDir()
for _, target := range targets {
root.addFile(target, target)
}

return root
}
Loading

0 comments on commit 2f8b40c

Please sign in to comment.