Files
trivy/pkg/run.go
2019-10-23 16:00:04 +03:00

210 lines
5.7 KiB
Go

package pkg
import (
l "log"
"os"
"strings"
"text/template"
"github.com/aquasecurity/fanal/cache"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/aquasecurity/trivy/pkg/vulnsrc"
"github.com/aquasecurity/trivy/pkg/vulnsrc/vulnerability"
"github.com/genuinetools/reg/registry"
"github.com/urfave/cli"
"golang.org/x/xerrors"
)
func Run(c *cli.Context) (err error) {
if c.Bool("quiet") || c.Bool("no-progress") {
utils.Quiet = true
}
debug := c.Bool("debug")
if err = log.InitLogger(debug, c.Bool("quiet")); err != nil {
l.Fatal(err)
}
utils.SetCacheDir(c.String("cache-dir"))
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
reset := c.Bool("reset")
if reset {
log.Logger.Info("Resetting...")
if err = cache.Clear(); err != nil {
return xerrors.New("failed to remove image layer cache")
}
if err = os.RemoveAll(utils.CacheDir()); err != nil {
return xerrors.New("failed to remove cache")
}
return nil
}
clearCache := c.Bool("clear-cache")
if clearCache {
log.Logger.Info("Removing image caches...")
if err = cache.Clear(); err != nil {
return xerrors.New("failed to remove image layer cache")
}
}
refresh := c.Bool("refresh")
args := c.Args()
var noTarget bool
filePath := c.String("input")
if filePath == "" && len(args) == 0 {
noTarget = true
if !reset && !clearCache && !refresh {
log.Logger.Info(`trivy requires at least 1 argument or --input option.`)
cli.ShowAppHelpAndExit(c, 1)
}
}
autoRefresh := c.Bool("auto-refresh")
skipUpdate := c.Bool("skip-update")
onlyUpdate := c.String("only-update")
if refresh || autoRefresh {
if skipUpdate {
return xerrors.New("The --skip-update option can not be specified with the --refresh or --auto-refresh option")
}
if onlyUpdate != "" {
return xerrors.New("The --only-update option can not be specified with the --refresh or --auto-refresh option")
}
}
if skipUpdate && onlyUpdate != "" {
return xerrors.New("The --skip-update and --only-update option can not be specified both")
}
if err = db.Init(); err != nil {
return xerrors.Errorf("error in vulnerability DB initialize: %w", err)
}
needRefresh := false
dbVersion := db.GetVersion()
if 0 < dbVersion && dbVersion < db.SchemaVersion {
if !refresh && !autoRefresh {
return xerrors.New("Detected version update of trivy. Please try again with --refresh or --auto-refresh option")
}
needRefresh = true
}
if refresh || needRefresh {
log.Logger.Info("Refreshing DB...")
if err = db.Reset(); err != nil {
return xerrors.Errorf("error in refresh DB: %w", err)
}
}
updateTargets := vulnsrc.UpdateList
if onlyUpdate != "" {
log.Logger.Warn("The --only-update option may cause the vulnerability details such as severity and title not to be displayed")
updateTargets = strings.Split(onlyUpdate, ",")
}
if !skipUpdate {
if err = vulnsrc.Update(updateTargets); err != nil {
return xerrors.Errorf("error in vulnerability DB update: %w", err)
}
}
dbc := db.Config{}
if err = dbc.SetVersion(db.SchemaVersion); err != nil {
return xerrors.Errorf("unexpected error: %w", err)
}
// When specifying no image name and file name
if noTarget {
return nil
}
o := c.String("output")
output := os.Stdout
if o != "" {
if output, err = os.Create(o); err != nil {
return xerrors.Errorf("failed to create an output file: %w", err)
}
}
var severities []vulnerability.Severity
for _, s := range strings.Split(c.String("severity"), ",") {
severity, err := vulnerability.NewSeverity(s)
if err != nil {
log.Logger.Infof("error in severity option: %s", err)
cli.ShowAppHelpAndExit(c, 1)
}
severities = append(severities, severity)
}
var imageName string
if filePath == "" {
imageName = args[0]
}
// Check whether 'latest' tag is used
if imageName != "" {
image, err := registry.ParseImage(imageName)
if err != nil {
return xerrors.Errorf("invalid image: %w", err)
}
if image.Tag == "latest" && !clearCache {
log.Logger.Warn("You should avoid using the :latest tag as it is cached. You need to specify '--clear-cache' option when :latest image is changed")
}
}
timeout := c.Duration("timeout")
scanOptions := types.ScanOptions{
VulnType: strings.Split(c.String("vuln-type"), ","),
Timeout: timeout,
}
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
results, err := scanner.ScanImage(imageName, filePath, scanOptions)
if err != nil {
return xerrors.Errorf("error in image scan: %w", err)
}
ignoreFile := c.String("ignorefile")
ignoreUnfixed := c.Bool("ignore-unfixed")
for i := range results {
results[i].Vulnerabilities = vulnerability.FillAndFilter(results[i].Vulnerabilities, severities, ignoreUnfixed, ignoreFile)
}
var writer report.Writer
switch format := c.String("format"); format {
case "table":
writer = &report.TableWriter{Output: output}
case "json":
writer = &report.JsonWriter{Output: output}
case "template":
outputTemplate := c.String("template")
tmpl, err := template.New("output template").Parse(outputTemplate)
if err != nil {
return xerrors.Errorf("error parsing template: %w", err)
}
writer = &report.TemplateWriter{Output: output, Template: tmpl}
default:
return xerrors.Errorf("unknown format: %v", format)
}
if err = writer.Write(results); err != nil {
return xerrors.Errorf("failed to write results: %w", err)
}
exitCode := c.Int("exit-code")
if exitCode != 0 {
for _, result := range results {
if len(result.Vulnerabilities) > 0 {
os.Exit(exitCode)
}
}
}
return nil
}