Skip to content

Commit

Permalink
feat: support repository and filesystem scan (#503)
Browse files Browse the repository at this point in the history
* refactor: embed config

* refactor: replace image and layer with artifact and blob

* feat(config): add ArtifactConfig

* fix(scanner): use Artifact

* test(scanner): update mocks

* feat: add repo and fs subcommands

* chore(mod): update

* refactor: fix warn message

* feat(cli): add --no-progress to repo and fs

* mod: Update fanal dependency

Signed-off-by: Simarpreet Singh <[email protected]>

Co-authored-by: Simarpreet Singh <[email protected]>
  • Loading branch information
knqyf263 and simar7 authored May 30, 2020
1 parent 03ad8a3 commit 2f2d1a9
Show file tree
Hide file tree
Showing 47 changed files with 1,482 additions and 1,025 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/aquasecurity/trivy
go 1.13

require (
github.com/aquasecurity/fanal v0.0.0-20200505074551-9239a362deca
github.com/aquasecurity/fanal v0.0.0-20200528202907-79693bf4a058
github.com/aquasecurity/go-dep-parser v0.0.0-20190819075924-ea223f0ef24b
github.com/aquasecurity/trivy-db v0.0.0-20200514134639-7e57e3e02470
github.com/caarlos0/env/v6 v6.0.0
Expand Down
64 changes: 55 additions & 9 deletions go.sum

Large diffs are not rendered by default.

62 changes: 59 additions & 3 deletions internal/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import (

"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/internal/artifact"
"github.com/aquasecurity/trivy/internal/client"
"github.com/aquasecurity/trivy/internal/server"
"github.com/aquasecurity/trivy/internal/standalone"
tdb "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/trivy/pkg/vulnerability"
Expand Down Expand Up @@ -240,10 +240,12 @@ func NewApp(version string) *cli.App {
app.Flags = flags
app.Commands = []*cli.Command{
NewImageCommand(),
NewFilesystemCommand(),
NewRepositoryCommand(),
NewClientCommand(),
NewServerCommand(),
}
app.Action = standalone.Run
app.Action = artifact.ImageRun
return app
}

Expand Down Expand Up @@ -320,11 +322,65 @@ func NewImageCommand() *cli.Command {
Name: "image",
Aliases: []string{"i"},
Usage: "scan an image",
Action: standalone.Run,
Action: artifact.ImageRun,
Flags: imageFlags,
}
}

func NewFilesystemCommand() *cli.Command {
return &cli.Command{
Name: "filesystem",
Aliases: []string{"fs"},
Usage: "scan local filesystem",
Action: artifact.FilesystemRun,
Flags: []cli.Flag{
&templateFlag,
&formatFlag,
&inputFlag,
&severityFlag,
&outputFlag,
&exitCodeFlag,
&clearCacheFlag,
&quietFlag,
&ignoreUnfixedFlag,
&debugFlag,
&removedPkgsFlag,
&vulnTypeFlag,
&ignoreFileFlag,
&cacheDirFlag,
&timeoutFlag,
&noProgressFlag,
},
}
}

func NewRepositoryCommand() *cli.Command {
return &cli.Command{
Name: "repository",
Aliases: []string{"repo"},
Usage: "scan remote repository",
Action: artifact.RepositoryRun,
Flags: []cli.Flag{
&templateFlag,
&formatFlag,
&inputFlag,
&severityFlag,
&outputFlag,
&exitCodeFlag,
&clearCacheFlag,
&quietFlag,
&ignoreUnfixedFlag,
&debugFlag,
&removedPkgsFlag,
&vulnTypeFlag,
&ignoreFileFlag,
&cacheDirFlag,
&timeoutFlag,
&noProgressFlag,
},
}
}

func NewClientCommand() *cli.Command {
return &cli.Command{
Name: "client",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import (

type Config struct {
config.GlobalConfig
config.ArtifactConfig
config.DBConfig
config.ImageConfig
config.ReportConfig

NoProgress bool

// deprecated
onlyUpdate string
// deprecated
Expand All @@ -30,20 +29,19 @@ func New(c *cli.Context) (Config, error) {
}

return Config{
GlobalConfig: gc,
DBConfig: config.NewDBConfig(c),
ImageConfig: config.NewImageConfig(c),
ReportConfig: config.NewReportConfig(c),

NoProgress: c.Bool("no-progress"),
GlobalConfig: gc,
ArtifactConfig: config.NewArtifactConfig(c),
DBConfig: config.NewDBConfig(c),
ImageConfig: config.NewImageConfig(c),
ReportConfig: config.NewReportConfig(c),

onlyUpdate: c.String("only-update"),
refresh: c.Bool("refresh"),
autoRefresh: c.Bool("auto-refresh"),
}, nil
}

func (c *Config) Init() error {
func (c *Config) Init(image bool) error {
if err := c.ReportConfig.Init(c.Logger); err != nil {
return err
}
Expand All @@ -59,10 +57,16 @@ func (c *Config) Init() error {
return nil
}

if err := c.ImageConfig.Init(c.Context.Args(), c.Logger); err != nil {
if err := c.ArtifactConfig.Init(c.Context.Args(), c.Logger); err != nil {
cli.ShowAppHelp(c.Context)
return err
}

if image {
if err := c.ImageConfig.Init(c.Context.Args(), c.Logger); err != nil {
return err
}
}

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ func TestConfig_Init(t *testing.T) {
GlobalConfig: config.GlobalConfig{
Quiet: true,
},
ImageConfig: config.ImageConfig{
ImageName: "alpine:3.10",
ArtifactConfig: config.ArtifactConfig{
Target: "alpine:3.10",
},
ReportConfig: config.ReportConfig{
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
Expand Down Expand Up @@ -74,8 +74,8 @@ func TestConfig_Init(t *testing.T) {
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ImageConfig: config.ImageConfig{
ImageName: "centos:7",
ArtifactConfig: config.ArtifactConfig{
Target: "centos:7",
},
},
},
Expand All @@ -91,8 +91,8 @@ func TestConfig_Init(t *testing.T) {
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ImageConfig: config.ImageConfig{
ImageName: "debian:buster",
ArtifactConfig: config.ArtifactConfig{
Target: "debian:buster",
},
onlyUpdate: "alpine",
},
Expand All @@ -110,8 +110,8 @@ func TestConfig_Init(t *testing.T) {
VulnType: []string{"os", "library"},
Template: "@contrib/gitlab.tpl",
},
ImageConfig: config.ImageConfig{
ImageName: "gitlab/gitlab-ce:12.7.2-ce.0",
ArtifactConfig: config.ArtifactConfig{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
},
},
Expand All @@ -129,8 +129,8 @@ func TestConfig_Init(t *testing.T) {
Template: "@contrib/gitlab.tpl",
Format: "json",
},
ImageConfig: config.ImageConfig{
ImageName: "gitlab/gitlab-ce:12.7.2-ce.0",
ArtifactConfig: config.ArtifactConfig{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
},
},
Expand All @@ -147,8 +147,8 @@ func TestConfig_Init(t *testing.T) {
VulnType: []string{"os", "library"},
Format: "template",
},
ImageConfig: config.ImageConfig{
ImageName: "gitlab/gitlab-ce:12.7.2-ce.0",
ArtifactConfig: config.ArtifactConfig{
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
},
},
},
Expand All @@ -165,8 +165,8 @@ func TestConfig_Init(t *testing.T) {
Output: os.Stdout,
VulnType: []string{"os", "library"},
},
ImageConfig: config.ImageConfig{
ImageName: "gcr.io/distroless/base",
ArtifactConfig: config.ArtifactConfig{
Target: "gcr.io/distroless/base",
},
autoRefresh: true,
},
Expand All @@ -180,7 +180,7 @@ func TestConfig_Init(t *testing.T) {
name: "sad: multiple image names",
args: []string{"centos:7", "alpine:3.10"},
logs: []string{
"multiple images cannot be specified",
"multiple targets cannot be specified",
},
wantErr: "arguments error",
},
Expand Down Expand Up @@ -223,7 +223,7 @@ func TestConfig_Init(t *testing.T) {
require.NoError(t, err, err)

c.GlobalConfig.Logger = logger.Sugar()
err = c.Init()
err = c.Init(true)

// tests log messages
var gotMessages []string
Expand Down
36 changes: 36 additions & 0 deletions internal/artifact/fs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package artifact

import (
"context"
"time"

"github.com/urfave/cli/v2"
"golang.org/x/xerrors"

"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/internal/artifact/config"
"github.com/aquasecurity/trivy/pkg/scanner"
)

func filesystemScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache, timeout time.Duration) (
scanner.Scanner, func(), error) {
s, cleanup, err := initializeFilesystemScanner(ctx, dir, ac, lac)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err)
}
return s, cleanup, nil
}

func FilesystemRun(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {
return err
}

// initialize config
if err = c.Init(false); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}

return run(c, filesystemScanner)
}
50 changes: 50 additions & 0 deletions internal/artifact/image.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package artifact

import (
"context"
"time"

"github.com/urfave/cli/v2"
"golang.org/x/xerrors"

"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/internal/artifact/config"
"github.com/aquasecurity/trivy/pkg/scanner"
)

func archiveScanner(ctx context.Context, input string, ac cache.ArtifactCache, lac cache.LocalArtifactCache, timeout time.Duration) (
scanner.Scanner, func(), error) {
s, err := initializeArchiveScanner(ctx, input, ac, lac, timeout)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize the archive scanner: %w", err)
}
return s, func() {}, nil
}

func dockerScanner(ctx context.Context, imageName string, ac cache.ArtifactCache, lac cache.LocalArtifactCache, timeout time.Duration) (
scanner.Scanner, func(), error) {
s, cleanup, err := initializeDockerScanner(ctx, imageName, ac, lac, timeout)
if err != nil {
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a docker scanner: %w", err)
}
return s, cleanup, nil
}

func ImageRun(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {
return err
}

// initialize config
if err = c.Init(true); err != nil {
return xerrors.Errorf("failed to initialize options: %w", err)
}

if c.Input != "" {
// scan tar file
return run(c, archiveScanner)
}

return run(c, dockerScanner)
}
16 changes: 13 additions & 3 deletions internal/standalone/inject.go → internal/artifact/inject.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// +build wireinject

package standalone
package artifact

import (
"context"
Expand All @@ -12,18 +12,28 @@ import (
"github.com/google/wire"
)

func initializeDockerScanner(ctx context.Context, imageName string, layerCache cache.ImageCache, localImageCache cache.LocalImageCache,
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache,
timeout time.Duration) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneDockerSet)
return scanner.Scanner{}, nil, nil
}

func initializeArchiveScanner(ctx context.Context, filePath string, layerCache cache.ImageCache, localImageCache cache.LocalImageCache,
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache,
timeout time.Duration) (scanner.Scanner, error) {
wire.Build(scanner.StandaloneArchiveSet)
return scanner.Scanner{}, nil
}

func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneFilesystemSet)
return scanner.Scanner{}, nil, nil
}

func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache) (scanner.Scanner, func(), error) {
wire.Build(scanner.StandaloneRepositorySet)
return scanner.Scanner{}, nil, nil
}

func initializeVulnerabilityClient() vulnerability.Client {
wire.Build(vulnerability.SuperSet)
return vulnerability.Client{}
Expand Down
Loading

0 comments on commit 2f2d1a9

Please sign in to comment.