mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-21 23:00:42 -08:00
210 lines
5.7 KiB
Go
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
|
|
}
|