github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/commands/app.go (about)

     1  package commands
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"sort"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/spf13/cobra"
    14  	"github.com/spf13/viper"
    15  	"golang.org/x/xerrors"
    16  
    17  	awsScanner "github.com/aquasecurity/trivy-aws/pkg/scanner"
    18  	awscommands "github.com/devseccon/trivy/pkg/cloud/aws/commands"
    19  	"github.com/devseccon/trivy/pkg/commands/artifact"
    20  	"github.com/devseccon/trivy/pkg/commands/convert"
    21  	"github.com/devseccon/trivy/pkg/commands/server"
    22  	"github.com/devseccon/trivy/pkg/fanal/analyzer"
    23  	"github.com/devseccon/trivy/pkg/flag"
    24  	k8scommands "github.com/devseccon/trivy/pkg/k8s/commands"
    25  	"github.com/devseccon/trivy/pkg/log"
    26  	"github.com/devseccon/trivy/pkg/module"
    27  	"github.com/devseccon/trivy/pkg/plugin"
    28  	"github.com/devseccon/trivy/pkg/types"
    29  	"github.com/devseccon/trivy/pkg/version"
    30  	xstrings "github.com/devseccon/trivy/pkg/x/strings"
    31  )
    32  
    33  const (
    34  	usageTemplate = `Usage:{{if .Runnable}}
    35    {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}
    36    {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}
    37  
    38  Aliases:
    39    {{.NameAndAliases}}{{end}}{{if .HasExample}}
    40  
    41  Examples:
    42  {{.Example}}{{end}}{{if .HasAvailableSubCommands}}
    43  
    44  Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
    45    {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
    46  
    47  %s
    48  
    49  Global Flags:
    50  {{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}
    51  
    52  Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
    53    {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
    54  
    55  Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
    56  `
    57  
    58  	groupScanning   = "scanning"
    59  	groupManagement = "management"
    60  	groupUtility    = "utility"
    61  	groupPlugin     = "plugin"
    62  )
    63  
    64  // NewApp is the factory method to return Trivy CLI
    65  func NewApp() *cobra.Command {
    66  	globalFlags := flag.NewGlobalFlagGroup()
    67  	rootCmd := NewRootCommand(globalFlags)
    68  	rootCmd.AddGroup(
    69  		&cobra.Group{
    70  			ID:    groupScanning,
    71  			Title: "Scanning Commands",
    72  		},
    73  		&cobra.Group{
    74  			ID:    groupManagement,
    75  			Title: "Management Commands",
    76  		},
    77  		&cobra.Group{
    78  			ID:    groupUtility,
    79  			Title: "Utility Commands",
    80  		},
    81  	)
    82  	rootCmd.SetCompletionCommandGroupID(groupUtility)
    83  	rootCmd.SetHelpCommandGroupID(groupUtility)
    84  	rootCmd.AddCommand(
    85  		NewImageCommand(globalFlags),
    86  		NewFilesystemCommand(globalFlags),
    87  		NewRootfsCommand(globalFlags),
    88  		NewRepositoryCommand(globalFlags),
    89  		NewClientCommand(globalFlags),
    90  		NewServerCommand(globalFlags),
    91  		NewConfigCommand(globalFlags),
    92  		NewConvertCommand(globalFlags),
    93  		NewPluginCommand(),
    94  		NewModuleCommand(globalFlags),
    95  		NewKubernetesCommand(globalFlags),
    96  		NewSBOMCommand(globalFlags),
    97  		NewVersionCommand(globalFlags),
    98  		NewAWSCommand(globalFlags),
    99  		NewVMCommand(globalFlags),
   100  	)
   101  
   102  	if plugins := loadPluginCommands(); len(plugins) > 0 {
   103  		rootCmd.AddGroup(&cobra.Group{
   104  			ID:    groupPlugin,
   105  			Title: "Plugin Commands",
   106  		})
   107  		rootCmd.AddCommand(plugins...)
   108  	}
   109  
   110  	return rootCmd
   111  }
   112  
   113  func loadPluginCommands() []*cobra.Command {
   114  	var commands []*cobra.Command
   115  	plugins, err := plugin.LoadAll()
   116  	if err != nil {
   117  		log.Logger.Debugf("no plugins were loaded")
   118  		return nil
   119  	}
   120  	for _, p := range plugins {
   121  		p := p
   122  		cmd := &cobra.Command{
   123  			Use:     fmt.Sprintf("%s [flags]", p.Name),
   124  			Short:   p.Usage,
   125  			GroupID: groupPlugin,
   126  			RunE: func(cmd *cobra.Command, args []string) error {
   127  				if err = p.Run(cmd.Context(), args); err != nil {
   128  					return xerrors.Errorf("plugin error: %w", err)
   129  				}
   130  				return nil
   131  			},
   132  			DisableFlagParsing: true,
   133  		}
   134  		commands = append(commands, cmd)
   135  	}
   136  	return commands
   137  }
   138  
   139  func initConfig(configFile string) error {
   140  	// Read from config
   141  	viper.SetConfigFile(configFile)
   142  	viper.SetConfigType("yaml")
   143  	if err := viper.ReadInConfig(); err != nil {
   144  		if errors.Is(err, os.ErrNotExist) {
   145  			log.Logger.Debugf("config file %q not found", configFile)
   146  			return nil
   147  		}
   148  		return xerrors.Errorf("config file %q loading error: %s", configFile, err)
   149  	}
   150  	log.Logger.Infof("Loaded %s", configFile)
   151  	return nil
   152  }
   153  
   154  func NewRootCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   155  	var versionFormat string
   156  	cmd := &cobra.Command{
   157  		Use:   "trivy [global flags] command [flags] target",
   158  		Short: "Unified security scanner",
   159  		Long:  "Scanner for vulnerabilities in container images, file systems, and Git repositories, as well as for configuration issues and hard-coded secrets",
   160  		Example: `  # Scan a container image
   161    $ trivy image python:3.4-alpine
   162  
   163    # Scan a container image from a tar archive
   164    $ trivy image --input ruby-3.1.tar
   165  
   166    # Scan local filesystem
   167    $ trivy fs .
   168  
   169    # Run in server mode
   170    $ trivy server`,
   171  		Args: cobra.NoArgs,
   172  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
   173  			// Set the Trivy version here so that we can override version printer.
   174  			cmd.Version = version.AppVersion()
   175  
   176  			// viper.BindPFlag cannot be called in init().
   177  			// cf. https://github.com/spf13/cobra/issues/875
   178  			//     https://github.com/spf13/viper/issues/233
   179  			if err := globalFlags.Bind(cmd); err != nil {
   180  				return xerrors.Errorf("flag bind error: %w", err)
   181  			}
   182  
   183  			// The config path is needed for config initialization.
   184  			// It needs to be obtained before ToOptions().
   185  			configPath := viper.GetString(flag.ConfigFileFlag.ConfigName)
   186  
   187  			// Configure environment variables and config file
   188  			// It cannot be called in init() because it must be called after viper.BindPFlags.
   189  			if err := initConfig(configPath); err != nil {
   190  				return err
   191  			}
   192  
   193  			globalOptions := globalFlags.ToOptions()
   194  
   195  			// Initialize logger
   196  			if err := log.InitLogger(globalOptions.Debug, globalOptions.Quiet); err != nil {
   197  				return err
   198  			}
   199  
   200  			return nil
   201  		},
   202  		RunE: func(cmd *cobra.Command, args []string) error {
   203  			globalOptions := globalFlags.ToOptions()
   204  			if globalOptions.ShowVersion {
   205  				// Customize version output
   206  				return showVersion(globalOptions.CacheDir, versionFormat, cmd.OutOrStdout())
   207  			} else {
   208  				return cmd.Help()
   209  			}
   210  		},
   211  	}
   212  
   213  	// Add version format flag, only json is supported
   214  	cmd.Flags().StringVarP(&versionFormat, flag.FormatFlag.Name, flag.FormatFlag.Shorthand, "", "version format (json)")
   215  
   216  	globalFlags.AddFlags(cmd)
   217  
   218  	return cmd
   219  }
   220  
   221  func NewImageCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   222  	scanFlagGroup := flag.NewScanFlagGroup()
   223  	scanFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps'
   224  
   225  	reportFlagGroup := flag.NewReportFlagGroup()
   226  	report := flag.ReportFormatFlag
   227  	report.Default = "summary"                                   // override the default value as the summary is preferred for the compliance report
   228  	report.Usage = "specify a format for the compliance report." // "--report" works only with "--compliance"
   229  	reportFlagGroup.ReportFormat = &report
   230  
   231  	compliance := flag.ComplianceFlag
   232  	compliance.Values = []string{types.ComplianceDockerCIS}
   233  	reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand.
   234  
   235  	misconfFlagGroup := flag.NewMisconfFlagGroup()
   236  	misconfFlagGroup.CloudformationParamVars = nil // disable '--cf-params'
   237  	misconfFlagGroup.TerraformTFVars = nil         // disable '--tf-vars'
   238  
   239  	imageFlags := &flag.Flags{
   240  		CacheFlagGroup:         flag.NewCacheFlagGroup(),
   241  		DBFlagGroup:            flag.NewDBFlagGroup(),
   242  		ImageFlagGroup:         flag.NewImageFlagGroup(), // container image specific
   243  		LicenseFlagGroup:       flag.NewLicenseFlagGroup(),
   244  		MisconfFlagGroup:       misconfFlagGroup,
   245  		ModuleFlagGroup:        flag.NewModuleFlagGroup(),
   246  		RemoteFlagGroup:        flag.NewClientFlags(), // for client/server mode
   247  		RegistryFlagGroup:      flag.NewRegistryFlagGroup(),
   248  		RegoFlagGroup:          flag.NewRegoFlagGroup(),
   249  		ReportFlagGroup:        reportFlagGroup,
   250  		ScanFlagGroup:          scanFlagGroup,
   251  		SecretFlagGroup:        flag.NewSecretFlagGroup(),
   252  		VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
   253  	}
   254  
   255  	cmd := &cobra.Command{
   256  		Use:     "image [flags] IMAGE_NAME",
   257  		Aliases: []string{"i"},
   258  		GroupID: groupScanning,
   259  		Short:   "Scan a container image",
   260  		Example: `  # Scan a container image
   261    $ trivy image python:3.4-alpine
   262  
   263    # Scan a container image from a tar archive
   264    $ trivy image --input ruby-3.1.tar
   265  
   266    # Filter by severities
   267    $ trivy image --severity HIGH,CRITICAL alpine:3.15
   268  
   269    # Ignore unfixed/unpatched vulnerabilities
   270    $ trivy image --ignore-unfixed alpine:3.15
   271  
   272    # Scan a container image in client mode
   273    $ trivy image --server http://127.0.0.1:4954 alpine:latest
   274  
   275    # Generate json result
   276    $ trivy image --format json --output result.json alpine:3.15
   277  
   278    # Generate a report in the CycloneDX format
   279    $ trivy image --format cyclonedx --output result.cdx alpine:3.15`,
   280  
   281  		// 'Args' cannot be used since it is called before PreRunE and viper is not configured yet.
   282  		// cmd.Args     -> cannot validate args here
   283  		// cmd.PreRunE  -> configure viper && validate args
   284  		// cmd.RunE     -> run the command
   285  		PreRunE: func(cmd *cobra.Command, args []string) error {
   286  			// viper.BindPFlag cannot be called in init(), so it is called in PreRunE.
   287  			// cf. https://github.com/spf13/cobra/issues/875
   288  			//     https://github.com/spf13/viper/issues/233
   289  			if err := imageFlags.Bind(cmd); err != nil {
   290  				return xerrors.Errorf("flag bind error: %w", err)
   291  			}
   292  			return validateArgs(cmd, args)
   293  		},
   294  		RunE: func(cmd *cobra.Command, args []string) error {
   295  			options, err := imageFlags.ToOptions(args, globalFlags)
   296  			if err != nil {
   297  				return xerrors.Errorf("flag error: %w", err)
   298  			}
   299  			return artifact.Run(cmd.Context(), options, artifact.TargetContainerImage)
   300  		},
   301  		SilenceErrors: true,
   302  		SilenceUsage:  true,
   303  	}
   304  
   305  	imageFlags.AddFlags(cmd)
   306  	cmd.SetFlagErrorFunc(flagErrorFunc)
   307  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, imageFlags.Usages(cmd)))
   308  
   309  	return cmd
   310  }
   311  
   312  func NewFilesystemCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   313  	reportFlagGroup := flag.NewReportFlagGroup()
   314  	reportFormat := flag.ReportFormatFlag
   315  	reportFormat.Usage = "specify a compliance report format for the output" // @TODO: support --report summary for non compliance reports
   316  	reportFlagGroup.ReportFormat = &reportFormat
   317  	reportFlagGroup.ExitOnEOL = nil // disable '--exit-on-eol'
   318  
   319  	fsFlags := &flag.Flags{
   320  		CacheFlagGroup:         flag.NewCacheFlagGroup(),
   321  		DBFlagGroup:            flag.NewDBFlagGroup(),
   322  		LicenseFlagGroup:       flag.NewLicenseFlagGroup(),
   323  		MisconfFlagGroup:       flag.NewMisconfFlagGroup(),
   324  		ModuleFlagGroup:        flag.NewModuleFlagGroup(),
   325  		RemoteFlagGroup:        flag.NewClientFlags(), // for client/server mode
   326  		RegistryFlagGroup:      flag.NewRegistryFlagGroup(),
   327  		RegoFlagGroup:          flag.NewRegoFlagGroup(),
   328  		ReportFlagGroup:        reportFlagGroup,
   329  		ScanFlagGroup:          flag.NewScanFlagGroup(),
   330  		SecretFlagGroup:        flag.NewSecretFlagGroup(),
   331  		VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
   332  	}
   333  
   334  	cmd := &cobra.Command{
   335  		Use:     "filesystem [flags] PATH",
   336  		Aliases: []string{"fs"},
   337  		GroupID: groupScanning,
   338  		Short:   "Scan local filesystem",
   339  		Example: `  # Scan a local project including language-specific files
   340    $ trivy fs /path/to/your_project
   341  
   342    # Scan a single file
   343    $ trivy fs ./trivy-ci-test/Pipfile.lock`,
   344  		PreRunE: func(cmd *cobra.Command, args []string) error {
   345  			if err := fsFlags.Bind(cmd); err != nil {
   346  				return xerrors.Errorf("flag bind error: %w", err)
   347  			}
   348  			return validateArgs(cmd, args)
   349  		},
   350  		RunE: func(cmd *cobra.Command, args []string) error {
   351  			if err := fsFlags.Bind(cmd); err != nil {
   352  				return xerrors.Errorf("flag bind error: %w", err)
   353  			}
   354  			options, err := fsFlags.ToOptions(args, globalFlags)
   355  			if err != nil {
   356  				return xerrors.Errorf("flag error: %w", err)
   357  			}
   358  			return artifact.Run(cmd.Context(), options, artifact.TargetFilesystem)
   359  		},
   360  		SilenceErrors: true,
   361  		SilenceUsage:  true,
   362  	}
   363  
   364  	cmd.SetFlagErrorFunc(flagErrorFunc)
   365  	fsFlags.AddFlags(cmd)
   366  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, fsFlags.Usages(cmd)))
   367  
   368  	return cmd
   369  }
   370  
   371  func NewRootfsCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   372  	rootfsFlags := &flag.Flags{
   373  		CacheFlagGroup:         flag.NewCacheFlagGroup(),
   374  		DBFlagGroup:            flag.NewDBFlagGroup(),
   375  		LicenseFlagGroup:       flag.NewLicenseFlagGroup(),
   376  		MisconfFlagGroup:       flag.NewMisconfFlagGroup(),
   377  		ModuleFlagGroup:        flag.NewModuleFlagGroup(),
   378  		RemoteFlagGroup:        flag.NewClientFlags(), // for client/server mode
   379  		RegistryFlagGroup:      flag.NewRegistryFlagGroup(),
   380  		RegoFlagGroup:          flag.NewRegoFlagGroup(),
   381  		ReportFlagGroup:        flag.NewReportFlagGroup(),
   382  		ScanFlagGroup:          flag.NewScanFlagGroup(),
   383  		SecretFlagGroup:        flag.NewSecretFlagGroup(),
   384  		VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
   385  	}
   386  	rootfsFlags.ReportFlagGroup.ReportFormat = nil // TODO: support --report summary
   387  	rootfsFlags.ReportFlagGroup.Compliance = nil   // disable '--compliance'
   388  	rootfsFlags.ReportFlagGroup.ReportFormat = nil // disable '--report'
   389  	rootfsFlags.ScanFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps'
   390  
   391  	cmd := &cobra.Command{
   392  		Use:     "rootfs [flags] ROOTDIR",
   393  		Short:   "Scan rootfs",
   394  		GroupID: groupScanning,
   395  		Example: `  # Scan unpacked filesystem
   396    $ docker export $(docker create alpine:3.10.2) | tar -C /tmp/rootfs -xvf -
   397    $ trivy rootfs /tmp/rootfs
   398  
   399    # Scan from inside a container
   400    $ docker run --rm -it alpine:3.11
   401    / # curl -sfL https://raw.githubusercontent.com/devseccon/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
   402    / # trivy rootfs /`,
   403  		PreRunE: func(cmd *cobra.Command, args []string) error {
   404  			if err := rootfsFlags.Bind(cmd); err != nil {
   405  				return xerrors.Errorf("flag bind error: %w", err)
   406  			}
   407  			return validateArgs(cmd, args)
   408  		},
   409  		RunE: func(cmd *cobra.Command, args []string) error {
   410  			if err := rootfsFlags.Bind(cmd); err != nil {
   411  				return xerrors.Errorf("flag bind error: %w", err)
   412  			}
   413  			options, err := rootfsFlags.ToOptions(args, globalFlags)
   414  			if err != nil {
   415  				return xerrors.Errorf("flag error: %w", err)
   416  			}
   417  			return artifact.Run(cmd.Context(), options, artifact.TargetRootfs)
   418  		},
   419  		SilenceErrors: true,
   420  		SilenceUsage:  true,
   421  	}
   422  	cmd.SetFlagErrorFunc(flagErrorFunc)
   423  	rootfsFlags.AddFlags(cmd)
   424  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, rootfsFlags.Usages(cmd)))
   425  
   426  	return cmd
   427  }
   428  
   429  func NewRepositoryCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   430  	repoFlags := &flag.Flags{
   431  		CacheFlagGroup:         flag.NewCacheFlagGroup(),
   432  		DBFlagGroup:            flag.NewDBFlagGroup(),
   433  		LicenseFlagGroup:       flag.NewLicenseFlagGroup(),
   434  		MisconfFlagGroup:       flag.NewMisconfFlagGroup(),
   435  		ModuleFlagGroup:        flag.NewModuleFlagGroup(),
   436  		RegistryFlagGroup:      flag.NewRegistryFlagGroup(),
   437  		RegoFlagGroup:          flag.NewRegoFlagGroup(),
   438  		RemoteFlagGroup:        flag.NewClientFlags(), // for client/server mode
   439  		ReportFlagGroup:        flag.NewReportFlagGroup(),
   440  		ScanFlagGroup:          flag.NewScanFlagGroup(),
   441  		SecretFlagGroup:        flag.NewSecretFlagGroup(),
   442  		VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
   443  		RepoFlagGroup:          flag.NewRepoFlagGroup(),
   444  	}
   445  	repoFlags.ReportFlagGroup.ReportFormat = nil // TODO: support --report summary
   446  	repoFlags.ReportFlagGroup.Compliance = nil   // disable '--compliance'
   447  	repoFlags.ReportFlagGroup.ExitOnEOL = nil    // disable '--exit-on-eol'
   448  
   449  	cmd := &cobra.Command{
   450  		Use:     "repository [flags] (REPO_PATH | REPO_URL)",
   451  		Aliases: []string{"repo"},
   452  		GroupID: groupScanning,
   453  		Short:   "Scan a repository",
   454  		Example: `  # Scan your remote git repository
   455    $ trivy repo https://github.com/knqyf263/trivy-ci-test
   456    # Scan your local git repository
   457    $ trivy repo /path/to/your/repository`,
   458  		PreRunE: func(cmd *cobra.Command, args []string) error {
   459  			if err := repoFlags.Bind(cmd); err != nil {
   460  				return xerrors.Errorf("flag bind error: %w", err)
   461  			}
   462  			return validateArgs(cmd, args)
   463  		},
   464  		RunE: func(cmd *cobra.Command, args []string) error {
   465  			if err := repoFlags.Bind(cmd); err != nil {
   466  				return xerrors.Errorf("flag bind error: %w", err)
   467  			}
   468  			options, err := repoFlags.ToOptions(args, globalFlags)
   469  			if err != nil {
   470  				return xerrors.Errorf("flag error: %w", err)
   471  			}
   472  			return artifact.Run(cmd.Context(), options, artifact.TargetRepository)
   473  		},
   474  		SilenceErrors: true,
   475  		SilenceUsage:  true,
   476  	}
   477  	cmd.SetFlagErrorFunc(flagErrorFunc)
   478  	repoFlags.AddFlags(cmd)
   479  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, repoFlags.Usages(cmd)))
   480  
   481  	return cmd
   482  }
   483  
   484  func NewConvertCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   485  	convertFlags := &flag.Flags{
   486  		ScanFlagGroup:   &flag.ScanFlagGroup{},
   487  		ReportFlagGroup: flag.NewReportFlagGroup(),
   488  	}
   489  	cmd := &cobra.Command{
   490  		Use:     "convert [flags] RESULT_JSON",
   491  		Aliases: []string{"conv"},
   492  		GroupID: groupUtility,
   493  		Short:   "Convert Trivy JSON report into a different format",
   494  		Example: `  # report conversion
   495    $ trivy image --format json --output result.json --list-all-pkgs debian:11
   496    $ trivy convert --format cyclonedx --output result.cdx result.json
   497  `,
   498  		PreRunE: func(cmd *cobra.Command, args []string) error {
   499  			if err := convertFlags.Bind(cmd); err != nil {
   500  				return xerrors.Errorf("flag bind error: %w", err)
   501  			}
   502  			return validateArgs(cmd, args)
   503  		},
   504  		RunE: func(cmd *cobra.Command, args []string) error {
   505  			if err := convertFlags.Bind(cmd); err != nil {
   506  				return xerrors.Errorf("flag bind error: %w", err)
   507  			}
   508  			opts, err := convertFlags.ToOptions(args, globalFlags)
   509  			if err != nil {
   510  				return xerrors.Errorf("flag error: %w", err)
   511  			}
   512  
   513  			return convert.Run(cmd.Context(), opts)
   514  		},
   515  		SilenceErrors: true,
   516  		SilenceUsage:  true,
   517  	}
   518  	cmd.SetFlagErrorFunc(flagErrorFunc)
   519  	convertFlags.AddFlags(cmd)
   520  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, convertFlags.Usages(cmd)))
   521  
   522  	return cmd
   523  }
   524  
   525  // NewClientCommand returns the 'client' subcommand that is deprecated
   526  func NewClientCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   527  	remoteFlags := flag.NewClientFlags()
   528  	remoteAddr := flag.Flag{
   529  		Name:       "remote",
   530  		ConfigName: "server.addr",
   531  		Shorthand:  "",
   532  		Default:    "http://localhost:4954",
   533  		Usage:      "server address",
   534  	}
   535  	remoteFlags.ServerAddr = &remoteAddr // disable '--server' and enable '--remote' instead.
   536  
   537  	clientFlags := &flag.Flags{
   538  		CacheFlagGroup:         flag.NewCacheFlagGroup(),
   539  		DBFlagGroup:            flag.NewDBFlagGroup(),
   540  		MisconfFlagGroup:       flag.NewMisconfFlagGroup(),
   541  		RegistryFlagGroup:      flag.NewRegistryFlagGroup(),
   542  		RegoFlagGroup:          flag.NewRegoFlagGroup(),
   543  		RemoteFlagGroup:        remoteFlags,
   544  		ReportFlagGroup:        flag.NewReportFlagGroup(),
   545  		ScanFlagGroup:          flag.NewScanFlagGroup(),
   546  		VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
   547  	}
   548  
   549  	cmd := &cobra.Command{
   550  		Use:     "client [flags] IMAGE_NAME",
   551  		Aliases: []string{"c"},
   552  		Hidden:  true, // 'client' command is deprecated
   553  		PreRunE: func(cmd *cobra.Command, args []string) error {
   554  			if err := clientFlags.Bind(cmd); err != nil {
   555  				return xerrors.Errorf("flag bind error: %w", err)
   556  			}
   557  			return validateArgs(cmd, args)
   558  		},
   559  		RunE: func(cmd *cobra.Command, args []string) error {
   560  			log.Logger.Warn("'client' subcommand is deprecated now. See https://github.com/devseccon/trivy/discussions/2119")
   561  
   562  			if err := clientFlags.Bind(cmd); err != nil {
   563  				return xerrors.Errorf("flag bind error: %w", err)
   564  			}
   565  			options, err := clientFlags.ToOptions(args, globalFlags)
   566  			if err != nil {
   567  				return xerrors.Errorf("flag error: %w", err)
   568  			}
   569  			return artifact.Run(cmd.Context(), options, artifact.TargetContainerImage)
   570  		},
   571  		SilenceErrors: true,
   572  		SilenceUsage:  true,
   573  	}
   574  	cmd.SetFlagErrorFunc(flagErrorFunc)
   575  	clientFlags.AddFlags(cmd)
   576  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, clientFlags.Usages(cmd)))
   577  
   578  	return cmd
   579  }
   580  
   581  func NewServerCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   582  	serverFlags := &flag.Flags{
   583  		CacheFlagGroup:    flag.NewCacheFlagGroup(),
   584  		DBFlagGroup:       flag.NewDBFlagGroup(),
   585  		ModuleFlagGroup:   flag.NewModuleFlagGroup(),
   586  		RemoteFlagGroup:   flag.NewServerFlags(),
   587  		RegistryFlagGroup: flag.NewRegistryFlagGroup(),
   588  	}
   589  
   590  	// java-db only works on client side.
   591  	serverFlags.DBFlagGroup.DownloadJavaDBOnly = nil // disable '--download-java-db-only'
   592  	serverFlags.DBFlagGroup.SkipJavaDBUpdate = nil   // disable '--skip-java-db-update'
   593  	serverFlags.DBFlagGroup.JavaDBRepository = nil   // disable '--java-db-repository'
   594  
   595  	cmd := &cobra.Command{
   596  		Use:     "server [flags]",
   597  		Aliases: []string{"s"},
   598  		GroupID: groupUtility,
   599  		Short:   "Server mode",
   600  		Example: `  # Run a server
   601    $ trivy server
   602  
   603    # Listen on 0.0.0.0:10000
   604    $ trivy server --listen 0.0.0.0:10000
   605  `,
   606  		Args: cobra.ExactArgs(0),
   607  		RunE: func(cmd *cobra.Command, args []string) error {
   608  			if err := serverFlags.Bind(cmd); err != nil {
   609  				return xerrors.Errorf("flag bind error: %w", err)
   610  			}
   611  			options, err := serverFlags.ToOptions(args, globalFlags)
   612  			if err != nil {
   613  				return xerrors.Errorf("flag error: %w", err)
   614  			}
   615  			return server.Run(cmd.Context(), options)
   616  		},
   617  		SilenceErrors: true,
   618  		SilenceUsage:  true,
   619  	}
   620  	cmd.SetFlagErrorFunc(flagErrorFunc)
   621  	serverFlags.AddFlags(cmd)
   622  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, serverFlags.Usages(cmd)))
   623  
   624  	return cmd
   625  }
   626  
   627  func NewConfigCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   628  	reportFlagGroup := flag.NewReportFlagGroup()
   629  	reportFlagGroup.DependencyTree = nil // disable '--dependency-tree'
   630  	reportFlagGroup.ListAllPkgs = nil    // disable '--list-all-pkgs'
   631  	reportFlagGroup.ExitOnEOL = nil      // disable '--exit-on-eol'
   632  	reportFormat := flag.ReportFormatFlag
   633  	reportFormat.Usage = "specify a compliance report format for the output" // @TODO: support --report summary for non compliance reports
   634  	reportFlagGroup.ReportFormat = &reportFormat
   635  
   636  	scanFlags := &flag.ScanFlagGroup{
   637  		// Enable only '--skip-dirs' and '--skip-files' and disable other flags
   638  		SkipDirs:     &flag.SkipDirsFlag,
   639  		SkipFiles:    &flag.SkipFilesFlag,
   640  		FilePatterns: &flag.FilePatternsFlag,
   641  	}
   642  
   643  	configFlags := &flag.Flags{
   644  		CacheFlagGroup:    flag.NewCacheFlagGroup(),
   645  		MisconfFlagGroup:  flag.NewMisconfFlagGroup(),
   646  		ModuleFlagGroup:   flag.NewModuleFlagGroup(),
   647  		RegistryFlagGroup: flag.NewRegistryFlagGroup(),
   648  		RegoFlagGroup:     flag.NewRegoFlagGroup(),
   649  		K8sFlagGroup: &flag.K8sFlagGroup{
   650  			// disable unneeded flags
   651  			K8sVersion: &flag.K8sVersionFlag,
   652  		},
   653  		ReportFlagGroup: reportFlagGroup,
   654  		ScanFlagGroup:   scanFlags,
   655  	}
   656  
   657  	cmd := &cobra.Command{
   658  		Use:     "config [flags] DIR",
   659  		Aliases: []string{"conf"},
   660  		GroupID: groupScanning,
   661  		Short:   "Scan config files for misconfigurations",
   662  		PreRunE: func(cmd *cobra.Command, args []string) error {
   663  			if err := configFlags.Bind(cmd); err != nil {
   664  				return xerrors.Errorf("flag bind error: %w", err)
   665  			}
   666  			return validateArgs(cmd, args)
   667  		},
   668  		RunE: func(cmd *cobra.Command, args []string) error {
   669  			if err := configFlags.Bind(cmd); err != nil {
   670  				return xerrors.Errorf("flag bind error: %w", err)
   671  			}
   672  			options, err := configFlags.ToOptions(args, globalFlags)
   673  			if err != nil {
   674  				return xerrors.Errorf("flag error: %w", err)
   675  			}
   676  
   677  			// Disable OS and language analyzers
   678  			options.DisabledAnalyzers = append(analyzer.TypeOSes, analyzer.TypeLanguages...)
   679  
   680  			// Scan only for misconfigurations
   681  			options.Scanners = types.Scanners{types.MisconfigScanner}
   682  
   683  			return artifact.Run(cmd.Context(), options, artifact.TargetFilesystem)
   684  		},
   685  		SilenceErrors: true,
   686  		SilenceUsage:  true,
   687  	}
   688  	cmd.SetFlagErrorFunc(flagErrorFunc)
   689  	configFlags.AddFlags(cmd)
   690  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, configFlags.Usages(cmd)))
   691  
   692  	return cmd
   693  }
   694  
   695  func NewPluginCommand() *cobra.Command {
   696  	cmd := &cobra.Command{
   697  		Use:           "plugin subcommand",
   698  		Aliases:       []string{"p"},
   699  		GroupID:       groupManagement,
   700  		Short:         "Manage plugins",
   701  		SilenceErrors: true,
   702  		SilenceUsage:  true,
   703  	}
   704  	cmd.AddCommand(
   705  		&cobra.Command{
   706  			Use:                   "install URL | FILE_PATH",
   707  			Aliases:               []string{"i"},
   708  			Short:                 "Install a plugin",
   709  			SilenceErrors:         true,
   710  			DisableFlagsInUseLine: true,
   711  			Args:                  cobra.ExactArgs(1),
   712  			RunE: func(cmd *cobra.Command, args []string) error {
   713  				if _, err := plugin.Install(cmd.Context(), args[0], true); err != nil {
   714  					return xerrors.Errorf("plugin install error: %w", err)
   715  				}
   716  				return nil
   717  			},
   718  		},
   719  		&cobra.Command{
   720  			Use:                   "uninstall PLUGIN_NAME",
   721  			Aliases:               []string{"u"},
   722  			SilenceErrors:         true,
   723  			DisableFlagsInUseLine: true,
   724  			Short:                 "Uninstall a plugin",
   725  			Args:                  cobra.ExactArgs(1),
   726  			RunE: func(_ *cobra.Command, args []string) error {
   727  				if err := plugin.Uninstall(args[0]); err != nil {
   728  					return xerrors.Errorf("plugin uninstall error: %w", err)
   729  				}
   730  				return nil
   731  			},
   732  		},
   733  		&cobra.Command{
   734  			Use:                   "list",
   735  			Aliases:               []string{"l"},
   736  			SilenceErrors:         true,
   737  			DisableFlagsInUseLine: true,
   738  			Short:                 "List installed plugin",
   739  			Args:                  cobra.NoArgs,
   740  			RunE: func(cmd *cobra.Command, args []string) error {
   741  				info, err := plugin.List()
   742  				if err != nil {
   743  					return xerrors.Errorf("plugin list display error: %w", err)
   744  				}
   745  				if _, err := fmt.Fprint(os.Stdout, info); err != nil {
   746  					return xerrors.Errorf("print error: %w", err)
   747  				}
   748  				return nil
   749  			},
   750  		},
   751  		&cobra.Command{
   752  			Use:                   "info PLUGIN_NAME",
   753  			Short:                 "Show information about the specified plugin",
   754  			SilenceErrors:         true,
   755  			DisableFlagsInUseLine: true,
   756  			Args:                  cobra.ExactArgs(1),
   757  			RunE: func(_ *cobra.Command, args []string) error {
   758  				info, err := plugin.Information(args[0])
   759  				if err != nil {
   760  					return xerrors.Errorf("plugin information display error: %w", err)
   761  				}
   762  				if _, err := fmt.Fprint(os.Stdout, info); err != nil {
   763  					return xerrors.Errorf("print error: %w", err)
   764  				}
   765  				return nil
   766  			},
   767  		},
   768  		&cobra.Command{
   769  			Use:                   "run URL | FILE_PATH",
   770  			Aliases:               []string{"r"},
   771  			SilenceErrors:         true,
   772  			DisableFlagsInUseLine: true,
   773  			Short:                 "Run a plugin on the fly",
   774  			Args:                  cobra.MinimumNArgs(1),
   775  			RunE: func(cmd *cobra.Command, args []string) error {
   776  				return plugin.RunWithArgs(cmd.Context(), args[0], args[1:])
   777  			},
   778  		},
   779  		&cobra.Command{
   780  			Use:                   "update PLUGIN_NAME",
   781  			Short:                 "Update an existing plugin",
   782  			SilenceErrors:         true,
   783  			DisableFlagsInUseLine: true,
   784  			Args:                  cobra.ExactArgs(1),
   785  			RunE: func(_ *cobra.Command, args []string) error {
   786  				if err := plugin.Update(args[0]); err != nil {
   787  					return xerrors.Errorf("plugin update error: %w", err)
   788  				}
   789  				return nil
   790  			},
   791  		},
   792  	)
   793  	cmd.SetFlagErrorFunc(flagErrorFunc)
   794  	return cmd
   795  }
   796  
   797  func NewModuleCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   798  	moduleFlags := &flag.Flags{
   799  		ModuleFlagGroup: flag.NewModuleFlagGroup(),
   800  	}
   801  
   802  	cmd := &cobra.Command{
   803  		Use:           "module subcommand",
   804  		Aliases:       []string{"m"},
   805  		GroupID:       groupManagement,
   806  		Short:         "Manage modules",
   807  		SilenceErrors: true,
   808  		SilenceUsage:  true,
   809  	}
   810  
   811  	// Add subcommands
   812  	cmd.AddCommand(
   813  		&cobra.Command{
   814  			Use:     "install [flags] REPOSITORY",
   815  			Aliases: []string{"i"},
   816  			Short:   "Install a module",
   817  			Args:    cobra.ExactArgs(1),
   818  			PreRunE: func(cmd *cobra.Command, args []string) error {
   819  				if err := moduleFlags.Bind(cmd); err != nil {
   820  					return xerrors.Errorf("flag bind error: %w", err)
   821  				}
   822  				return nil
   823  			},
   824  			RunE: func(cmd *cobra.Command, args []string) error {
   825  				if len(args) != 1 {
   826  					return cmd.Help()
   827  				}
   828  
   829  				repo := args[0]
   830  				opts, err := moduleFlags.ToOptions(args, globalFlags)
   831  				if err != nil {
   832  					return xerrors.Errorf("flag error: %w", err)
   833  				}
   834  				return module.Install(cmd.Context(), opts.ModuleDir, repo, opts.Quiet, opts.RegistryOpts())
   835  			},
   836  		},
   837  		&cobra.Command{
   838  			Use:     "uninstall [flags] REPOSITORY",
   839  			Aliases: []string{"u"},
   840  			Short:   "Uninstall a module",
   841  			Args:    cobra.ExactArgs(1),
   842  			PreRunE: func(cmd *cobra.Command, args []string) error {
   843  				if err := moduleFlags.Bind(cmd); err != nil {
   844  					return xerrors.Errorf("flag bind error: %w", err)
   845  				}
   846  				return nil
   847  			},
   848  			RunE: func(cmd *cobra.Command, args []string) error {
   849  				if len(args) != 1 {
   850  					return cmd.Help()
   851  				}
   852  
   853  				repo := args[0]
   854  				opts, err := moduleFlags.ToOptions(args, globalFlags)
   855  				if err != nil {
   856  					return xerrors.Errorf("flag error: %w", err)
   857  				}
   858  				return module.Uninstall(cmd.Context(), opts.ModuleDir, repo)
   859  			},
   860  		},
   861  	)
   862  	moduleFlags.AddFlags(cmd)
   863  	cmd.SetFlagErrorFunc(flagErrorFunc)
   864  	return cmd
   865  }
   866  
   867  func NewKubernetesCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   868  	scanFlags := flag.NewScanFlagGroup()
   869  	scanners := flag.ScannersFlag
   870  	// overwrite the default scanners
   871  	scanners.Values = xstrings.ToStringSlice(types.Scanners{
   872  		types.VulnerabilityScanner,
   873  		types.MisconfigScanner,
   874  		types.SecretScanner,
   875  		types.RBACScanner,
   876  	})
   877  	scanners.Default = scanners.Values
   878  	scanFlags.Scanners = &scanners
   879  	scanFlags.IncludeDevDeps = nil // disable '--include-dev-deps'
   880  
   881  	// required only SourceFlag
   882  	imageFlags := &flag.ImageFlagGroup{ImageSources: &flag.SourceFlag}
   883  
   884  	reportFlagGroup := flag.NewReportFlagGroup()
   885  	compliance := flag.ComplianceFlag
   886  	compliance.Values = []string{
   887  		types.ComplianceK8sNsa,
   888  		types.ComplianceK8sCIS,
   889  		types.ComplianceK8sPSSBaseline,
   890  		types.ComplianceK8sPSSRestricted,
   891  	}
   892  	reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand.
   893  	reportFlagGroup.ExitOnEOL = nil          // disable '--exit-on-eol'
   894  
   895  	formatFlag := flag.FormatFlag
   896  	formatFlag.Values = xstrings.ToStringSlice([]types.Format{
   897  		types.FormatTable,
   898  		types.FormatJSON,
   899  		types.FormatCycloneDX,
   900  	})
   901  	reportFlagGroup.Format = &formatFlag
   902  
   903  	misconfFlagGroup := flag.NewMisconfFlagGroup()
   904  	misconfFlagGroup.CloudformationParamVars = nil // disable '--cf-params'
   905  	misconfFlagGroup.TerraformTFVars = nil         // disable '--tf-vars'
   906  
   907  	k8sFlags := &flag.Flags{
   908  		CacheFlagGroup:         flag.NewCacheFlagGroup(),
   909  		DBFlagGroup:            flag.NewDBFlagGroup(),
   910  		ImageFlagGroup:         imageFlags,
   911  		K8sFlagGroup:           flag.NewK8sFlagGroup(), // kubernetes-specific flags
   912  		MisconfFlagGroup:       misconfFlagGroup,
   913  		RegoFlagGroup:          flag.NewRegoFlagGroup(),
   914  		ReportFlagGroup:        reportFlagGroup,
   915  		ScanFlagGroup:          scanFlags,
   916  		SecretFlagGroup:        flag.NewSecretFlagGroup(),
   917  		RegistryFlagGroup:      flag.NewRegistryFlagGroup(),
   918  		VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
   919  	}
   920  	cmd := &cobra.Command{
   921  		Use:     "kubernetes [flags] { cluster | all | specific resources like kubectl. eg: pods, pod/NAME }",
   922  		Aliases: []string{"k8s"},
   923  		GroupID: groupScanning,
   924  		Short:   "[EXPERIMENTAL] Scan kubernetes cluster",
   925  		Example: `  # cluster scanning
   926    $ trivy k8s --report summary cluster
   927  
   928    # namespace scanning:
   929    $ trivy k8s -n kube-system --report summary all
   930  
   931    # resources scanning:
   932    $ trivy k8s --report=summary deploy
   933    $ trivy k8s --namespace=kube-system --report=summary deploy,configmaps
   934  
   935    # resource scanning:
   936    $ trivy k8s deployment/orion
   937  `,
   938  		PreRunE: func(cmd *cobra.Command, args []string) error {
   939  			if err := k8sFlags.Bind(cmd); err != nil {
   940  				return xerrors.Errorf("flag bind error: %w", err)
   941  			}
   942  			return validateArgs(cmd, args)
   943  		},
   944  		RunE: func(cmd *cobra.Command, args []string) error {
   945  			if err := k8sFlags.Bind(cmd); err != nil {
   946  				return xerrors.Errorf("flag bind error: %w", err)
   947  			}
   948  			opts, err := k8sFlags.ToOptions(args, globalFlags)
   949  			if err != nil {
   950  				return xerrors.Errorf("flag error: %w", err)
   951  			}
   952  
   953  			return k8scommands.Run(cmd.Context(), args, opts)
   954  		},
   955  		SilenceErrors: true,
   956  		SilenceUsage:  true,
   957  	}
   958  	cmd.SetFlagErrorFunc(flagErrorFunc)
   959  	k8sFlags.AddFlags(cmd)
   960  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, k8sFlags.Usages(cmd)))
   961  
   962  	return cmd
   963  }
   964  
   965  func NewAWSCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
   966  	reportFlagGroup := flag.NewReportFlagGroup()
   967  	compliance := flag.ComplianceFlag
   968  	compliance.Values = []string{
   969  		types.ComplianceAWSCIS12,
   970  		types.ComplianceAWSCIS14,
   971  	}
   972  	reportFlagGroup.Compliance = &compliance // override usage as the accepted values differ for each subcommand.
   973  	reportFlagGroup.ExitOnEOL = nil          // disable '--exit-on-eol'
   974  
   975  	awsFlags := &flag.Flags{
   976  		AWSFlagGroup:     flag.NewAWSFlagGroup(),
   977  		CloudFlagGroup:   flag.NewCloudFlagGroup(),
   978  		MisconfFlagGroup: flag.NewMisconfFlagGroup(),
   979  		RegoFlagGroup:    flag.NewRegoFlagGroup(),
   980  		ReportFlagGroup:  reportFlagGroup,
   981  	}
   982  
   983  	services := awsScanner.AllSupportedServices()
   984  	sort.Strings(services)
   985  
   986  	cmd := &cobra.Command{
   987  		Use:     "aws [flags]",
   988  		Aliases: []string{},
   989  		GroupID: groupScanning,
   990  		Args:    cobra.ExactArgs(0),
   991  		Short:   "[EXPERIMENTAL] Scan AWS account",
   992  		Long: fmt.Sprintf(`Scan an AWS account for misconfigurations. Trivy uses the same authentication methods as the AWS CLI. See https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html
   993  
   994  The following services are supported:
   995  
   996  - %s
   997  `, strings.Join(services, "\n- ")),
   998  		Example: `  # basic scanning
   999    $ trivy aws --region us-east-1
  1000  
  1001    # limit scan to a single service:
  1002    $ trivy aws --region us-east-1 --service s3
  1003  
  1004    # limit scan to multiple services:
  1005    $ trivy aws --region us-east-1 --service s3 --service ec2
  1006  
  1007    # force refresh of cache for fresh results
  1008    $ trivy aws --region us-east-1 --update-cache
  1009  `,
  1010  		PreRunE: func(cmd *cobra.Command, args []string) error {
  1011  			if err := awsFlags.Bind(cmd); err != nil {
  1012  				return xerrors.Errorf("flag bind error: %w", err)
  1013  			}
  1014  			return nil
  1015  		},
  1016  		RunE: func(cmd *cobra.Command, args []string) error {
  1017  			opts, err := awsFlags.ToOptions(args, globalFlags)
  1018  			if err != nil {
  1019  				return xerrors.Errorf("flag error: %w", err)
  1020  			}
  1021  			if opts.Timeout < time.Hour {
  1022  				opts.Timeout = time.Hour
  1023  				log.Logger.Debug("Timeout is set to less than 1 hour - upgrading to 1 hour for this command.")
  1024  			}
  1025  			return awscommands.Run(cmd.Context(), opts)
  1026  		},
  1027  		SilenceErrors: true,
  1028  		SilenceUsage:  true,
  1029  	}
  1030  	cmd.SetFlagErrorFunc(flagErrorFunc)
  1031  	awsFlags.AddFlags(cmd)
  1032  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, awsFlags.Usages(cmd)))
  1033  
  1034  	return cmd
  1035  }
  1036  
  1037  func NewVMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
  1038  	vmFlags := &flag.Flags{
  1039  		CacheFlagGroup:         flag.NewCacheFlagGroup(),
  1040  		DBFlagGroup:            flag.NewDBFlagGroup(),
  1041  		MisconfFlagGroup:       flag.NewMisconfFlagGroup(),
  1042  		ModuleFlagGroup:        flag.NewModuleFlagGroup(),
  1043  		RemoteFlagGroup:        flag.NewClientFlags(), // for client/server mode
  1044  		ReportFlagGroup:        flag.NewReportFlagGroup(),
  1045  		ScanFlagGroup:          flag.NewScanFlagGroup(),
  1046  		SecretFlagGroup:        flag.NewSecretFlagGroup(),
  1047  		VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
  1048  		AWSFlagGroup: &flag.AWSFlagGroup{
  1049  			Region: &flag.Flag{
  1050  				Name:       "aws-region",
  1051  				ConfigName: "aws.region",
  1052  				Default:    "",
  1053  				Usage:      "AWS region to scan",
  1054  			},
  1055  		},
  1056  	}
  1057  	vmFlags.ReportFlagGroup.ReportFormat = nil             // disable '--report'
  1058  	vmFlags.ScanFlagGroup.IncludeDevDeps = nil             // disable '--include-dev-deps'
  1059  	vmFlags.MisconfFlagGroup.CloudformationParamVars = nil // disable '--cf-params'
  1060  	vmFlags.MisconfFlagGroup.TerraformTFVars = nil         // disable '--tf-vars'
  1061  
  1062  	cmd := &cobra.Command{
  1063  		Use:     "vm [flags] VM_IMAGE",
  1064  		Aliases: []string{},
  1065  		GroupID: groupScanning,
  1066  		Short:   "[EXPERIMENTAL] Scan a virtual machine image",
  1067  		Example: `  # Scan your AWS AMI
  1068    $ trivy vm --scanners vuln ami:${your_ami_id}
  1069  
  1070    # Scan your AWS EBS snapshot
  1071    $ trivy vm ebs:${your_ebs_snapshot_id}
  1072  `,
  1073  		PreRunE: func(cmd *cobra.Command, args []string) error {
  1074  			if err := vmFlags.Bind(cmd); err != nil {
  1075  				return xerrors.Errorf("flag bind error: %w", err)
  1076  			}
  1077  			return validateArgs(cmd, args)
  1078  		},
  1079  		RunE: func(cmd *cobra.Command, args []string) error {
  1080  			if err := vmFlags.Bind(cmd); err != nil {
  1081  				return xerrors.Errorf("flag bind error: %w", err)
  1082  			}
  1083  			options, err := vmFlags.ToOptions(args, globalFlags)
  1084  			if err != nil {
  1085  				return xerrors.Errorf("flag error: %w", err)
  1086  			}
  1087  			if options.Timeout < time.Minute*30 {
  1088  				options.Timeout = time.Minute * 30
  1089  				log.Logger.Debug("Timeout is set to less than 30 min - upgrading to 30 min for this command.")
  1090  			}
  1091  			return artifact.Run(cmd.Context(), options, artifact.TargetVM)
  1092  		},
  1093  		SilenceErrors: true,
  1094  		SilenceUsage:  true,
  1095  	}
  1096  	cmd.SetFlagErrorFunc(flagErrorFunc)
  1097  	vmFlags.AddFlags(cmd)
  1098  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, vmFlags.Usages(cmd)))
  1099  
  1100  	return cmd
  1101  }
  1102  
  1103  func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
  1104  	reportFlagGroup := flag.NewReportFlagGroup()
  1105  	reportFlagGroup.DependencyTree = nil // disable '--dependency-tree'
  1106  	reportFlagGroup.ReportFormat = nil   // TODO: support --report summary
  1107  
  1108  	scanFlagGroup := flag.NewScanFlagGroup()
  1109  	scanFlagGroup.Scanners = nil       // disable '--scanners' as it always scans for vulnerabilities
  1110  	scanFlagGroup.IncludeDevDeps = nil // disable '--include-dev-deps'
  1111  	scanFlagGroup.Parallel = nil       // disable '--parallel'
  1112  
  1113  	sbomFlags := &flag.Flags{
  1114  		CacheFlagGroup:         flag.NewCacheFlagGroup(),
  1115  		DBFlagGroup:            flag.NewDBFlagGroup(),
  1116  		RemoteFlagGroup:        flag.NewClientFlags(), // for client/server mode
  1117  		ReportFlagGroup:        reportFlagGroup,
  1118  		ScanFlagGroup:          scanFlagGroup,
  1119  		SBOMFlagGroup:          flag.NewSBOMFlagGroup(),
  1120  		VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(),
  1121  	}
  1122  
  1123  	cmd := &cobra.Command{
  1124  		Use:     "sbom [flags] SBOM_PATH",
  1125  		Short:   "Scan SBOM for vulnerabilities",
  1126  		GroupID: groupScanning,
  1127  		Example: `  # Scan CycloneDX and show the result in tables
  1128    $ trivy sbom /path/to/report.cdx
  1129  
  1130    # Scan CycloneDX-type attestation and show the result in tables
  1131    $ trivy sbom /path/to/report.cdx.intoto.jsonl
  1132  `,
  1133  		PreRunE: func(cmd *cobra.Command, args []string) error {
  1134  			if err := sbomFlags.Bind(cmd); err != nil {
  1135  				return xerrors.Errorf("flag bind error: %w", err)
  1136  			}
  1137  			return validateArgs(cmd, args)
  1138  		},
  1139  		RunE: func(cmd *cobra.Command, args []string) error {
  1140  			if err := sbomFlags.Bind(cmd); err != nil {
  1141  				return xerrors.Errorf("flag bind error: %w", err)
  1142  			}
  1143  			options, err := sbomFlags.ToOptions(args, globalFlags)
  1144  			if err != nil {
  1145  				return xerrors.Errorf("flag error: %w", err)
  1146  			}
  1147  
  1148  			// Scan vulnerabilities
  1149  			options.Scanners = types.Scanners{types.VulnerabilityScanner}
  1150  
  1151  			return artifact.Run(cmd.Context(), options, artifact.TargetSBOM)
  1152  		},
  1153  		SilenceErrors: true,
  1154  		SilenceUsage:  true,
  1155  	}
  1156  	cmd.SetFlagErrorFunc(flagErrorFunc)
  1157  	sbomFlags.AddFlags(cmd)
  1158  	cmd.SetUsageTemplate(fmt.Sprintf(usageTemplate, sbomFlags.Usages(cmd)))
  1159  
  1160  	return cmd
  1161  }
  1162  
  1163  func NewVersionCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
  1164  	var versionFormat string
  1165  	cmd := &cobra.Command{
  1166  		Use:     "version [flags]",
  1167  		Short:   "Print the version",
  1168  		GroupID: groupUtility,
  1169  		Args:    cobra.NoArgs,
  1170  		RunE: func(cmd *cobra.Command, args []string) error {
  1171  			options := globalFlags.ToOptions()
  1172  			return showVersion(options.CacheDir, versionFormat, cmd.OutOrStdout())
  1173  		},
  1174  		SilenceErrors: true,
  1175  		SilenceUsage:  true,
  1176  	}
  1177  	cmd.SetFlagErrorFunc(flagErrorFunc)
  1178  
  1179  	// Add version format flag, only json is supported
  1180  	cmd.Flags().StringVarP(&versionFormat, flag.FormatFlag.Name, flag.FormatFlag.Shorthand, "", "version format (json)")
  1181  
  1182  	return cmd
  1183  }
  1184  
  1185  func showVersion(cacheDir, outputFormat string, w io.Writer) error {
  1186  	versionInfo := version.NewVersionInfo(cacheDir)
  1187  	switch outputFormat {
  1188  	case "json":
  1189  		if err := json.NewEncoder(w).Encode(versionInfo); err != nil {
  1190  			return xerrors.Errorf("json encode error: %w", err)
  1191  		}
  1192  	default:
  1193  		fmt.Fprint(w, versionInfo.String())
  1194  	}
  1195  	return nil
  1196  }
  1197  
  1198  func validateArgs(cmd *cobra.Command, args []string) error {
  1199  	// '--clear-cache', '--download-db-only', '--download-java-db-only', '--reset' and '--generate-default-config' don't conduct the subsequent scanning
  1200  	if viper.GetBool(flag.ClearCacheFlag.ConfigName) || viper.GetBool(flag.DownloadDBOnlyFlag.ConfigName) ||
  1201  		viper.GetBool(flag.ResetFlag.ConfigName) || viper.GetBool(flag.GenerateDefaultConfigFlag.ConfigName) ||
  1202  		viper.GetBool(flag.DownloadJavaDBOnlyFlag.ConfigName) || viper.GetBool(flag.ResetPolicyBundleFlag.ConfigName) {
  1203  		return nil
  1204  	}
  1205  
  1206  	if len(args) == 0 && viper.GetString(flag.InputFlag.ConfigName) == "" {
  1207  		if err := cmd.Help(); err != nil {
  1208  			return err
  1209  		}
  1210  
  1211  		if f := cmd.Flags().Lookup(flag.InputFlag.ConfigName); f != nil {
  1212  			return xerrors.New(`Require at least 1 argument or --input option`)
  1213  		}
  1214  		return xerrors.New(`Require at least 1 argument`)
  1215  	} else if cmd.Name() != "kubernetes" && len(args) > 1 {
  1216  		if err := cmd.Help(); err != nil {
  1217  			return err
  1218  		}
  1219  		return xerrors.New(`multiple targets cannot be specified`)
  1220  	}
  1221  
  1222  	return nil
  1223  }
  1224  
  1225  // show help on using the command when an invalid flag is encountered
  1226  func flagErrorFunc(command *cobra.Command, err error) error {
  1227  	if err := command.Help(); err != nil {
  1228  		return err
  1229  	}
  1230  	command.Println() // add empty line after list of flags
  1231  	return err
  1232  }