diff --git a/cmd/grype/cli/commands/root.go b/cmd/grype/cli/commands/root.go index 17be3561088..e3dfc167a7c 100644 --- a/cmd/grype/cli/commands/root.go +++ b/cmd/grype/cli/commands/root.go @@ -30,6 +30,7 @@ import ( "github.com/anchore/grype/grype/pkg" "github.com/anchore/grype/grype/presenter/models" "github.com/anchore/grype/grype/store" + "github.com/anchore/grype/grype/vex" "github.com/anchore/grype/internal" "github.com/anchore/grype/internal/bus" "github.com/anchore/grype/internal/format" @@ -94,6 +95,11 @@ var ignoreFixedMatches = []match.IgnoreRule{ {FixState: string(grypeDb.FixedState)}, } +var ignoreVEXFixedNotAffected = []match.IgnoreRule{ + {VexStatus: string(vex.StatusNotAffected)}, + {VexStatus: string(vex.StatusFixed)}, +} + //nolint:funlen func runGrype(app clio.Application, opts *options.Grype, userInput string) error { errs := make(chan error) @@ -166,6 +172,11 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) error opts.Ignore = append(opts.Ignore, ignoreFixedMatches...) } + if err := applyVexRules(opts); err != nil { + errs <- fmt.Errorf("applying vex rules: %w", err) + return + } + applyDistroHint(packages, &pkgContext, opts) vulnMatcher := grype.VulnerabilityMatcher{ @@ -175,8 +186,8 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) error FailSeverity: opts.FailOnServerity(), Matchers: getMatchers(opts), VexProcessor: vex.NewProcessor(vex.ProcessorOptions{ - Documents: appConfig.VexDocuments, - IgnoreRules: appConfig.Ignore, + Documents: opts.VexDocuments, + IgnoreRules: opts.Ignore, }), } @@ -346,3 +357,26 @@ func validateRootArgs(cmd *cobra.Command, args []string) error { return cobra.MaximumNArgs(1)(cmd, args) } + +func applyVexRules(opts *options.Grype) error { + if len(opts.Ignore) == 0 && len(opts.VexDocuments) > 0 { + opts.Ignore = append(opts.Ignore, ignoreVEXFixedNotAffected...) + } + + for _, vexStatus := range opts.VexAdd { + switch vexStatus { + case string(vex.StatusAffected): + opts.Ignore = append( + opts.Ignore, match.IgnoreRule{VexStatus: string(vex.StatusAffected)}, + ) + case string(vex.StatusUnderInvestigation): + opts.Ignore = append( + opts.Ignore, match.IgnoreRule{VexStatus: string(vex.StatusUnderInvestigation)}, + ) + default: + return fmt.Errorf("invalid VEX status in vex-add setting: %s", vexStatus) + } + } + + return nil +} diff --git a/cmd/grype/cli/options/grype.go b/cmd/grype/cli/options/grype.go index 165f296cacc..3d139e8f109 100644 --- a/cmd/grype/cli/options/grype.go +++ b/cmd/grype/cli/options/grype.go @@ -32,6 +32,8 @@ type Grype struct { ByCVE bool `yaml:"by-cve" json:"by-cve" mapstructure:"by-cve"` // --by-cve, indicates if the original match vulnerability IDs should be preserved or the CVE should be used instead Name string `yaml:"name" json:"name" mapstructure:"name"` DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` + VexDocuments []string `yaml:"vex-documents" json:"vex-documents" mapstructure:"vex-documents"` + VexAdd []string `yaml:"vex-add" json:"vex-add" mapstructure:"vex-add"` // GRYPE_VEX_ADD } var _ interface { @@ -46,6 +48,7 @@ func DefaultGrype(id clio.Identification) *Grype { Match: defaultMatchConfig(), ExternalSources: defaultExternalSources(), CheckForAppUpdate: true, + VexAdd: []string{}, } } @@ -118,6 +121,11 @@ func (o *Grype) AddFlags(flags clio.FlagSet) { "platform", "", "an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')", ) + + flags.StringArrayVarP(&o.VexDocuments, + "vex", "", + "a list of VEX documents to consider when producing scanning results", + ) } func (o *Grype) PostLoad() error {