mirror of
https://github.com/anchore/grype
synced 2024-11-10 06:34:13 +00:00
fix: take VEX docs into account when --fail-on is set (#1657)
* Take VEX docs into account when --fail-on is set Previously, VEX documents provided to Grype when --fail-on was set were not taken into account. That led to inconsistent behaviour where a vulnerability would be ignored when only `--vex` was specified, but would be included in Grype output when both `--vex` and `--fail-on` were specified. This change fixes that by moving the failure severity check to after the VEX documents provided are tested. I have also added a unit test to check that the combination of VEX docs and failure severity checks works as expected. Signed-off-by: Feroz Salam <feroz.salam@isovalent.com> * Fix typos Signed-off-by: Feroz Salam <feroz.salam@isovalent.com> --------- Signed-off-by: Feroz Salam <feroz.salam@isovalent.com>
This commit is contained in:
parent
5e1ba46fb8
commit
a3ade4242b
6 changed files with 119 additions and 14 deletions
|
@ -175,7 +175,7 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs
|
|||
Store: *str,
|
||||
IgnoreRules: opts.Ignore,
|
||||
NormalizeByCVE: opts.ByCVE,
|
||||
FailSeverity: opts.FailOnServerity(),
|
||||
FailSeverity: opts.FailOnSeverity(),
|
||||
Matchers: getMatchers(opts),
|
||||
VexProcessor: vex.NewProcessor(vex.ProcessorOptions{
|
||||
Documents: opts.VexDocuments,
|
||||
|
|
|
@ -137,7 +137,7 @@ func (o *Grype) AddFlags(flags clio.FlagSet) {
|
|||
|
||||
func (o *Grype) PostLoad() error {
|
||||
if o.FailOn != "" {
|
||||
failOnSeverity := *o.FailOnServerity()
|
||||
failOnSeverity := *o.FailOnSeverity()
|
||||
if failOnSeverity == vulnerability.UnknownSeverity {
|
||||
return fmt.Errorf("bad --fail-on severity value '%s'", o.FailOn)
|
||||
}
|
||||
|
@ -145,7 +145,7 @@ func (o *Grype) PostLoad() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (o Grype) FailOnServerity() *vulnerability.Severity {
|
||||
func (o Grype) FailOnSeverity() *vulnerability.Severity {
|
||||
severity := vulnerability.ParseSeverity(o.FailOn)
|
||||
return &severity
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
|
|||
return &s
|
||||
}
|
||||
|
||||
metchesRef := func(ms ...match.Match) *match.Matches {
|
||||
matchesRef := func(ms ...match.Match) *match.Matches {
|
||||
m := match.NewMatches(ms...)
|
||||
return &m
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
|
|||
pkgContext: pkgContext,
|
||||
matches: getSubject(),
|
||||
},
|
||||
wantMatches: metchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
|
||||
wantMatches: matchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
|
||||
wantIgnoredMatches: []match.IgnoredMatch{
|
||||
{
|
||||
Match: libCryptoCVE_2023_1255,
|
||||
|
@ -157,7 +157,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
|
|||
pkgContext: pkgContext,
|
||||
matches: getSubject(),
|
||||
},
|
||||
wantMatches: metchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
|
||||
wantMatches: matchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
|
||||
wantIgnoredMatches: []match.IgnoredMatch{
|
||||
{
|
||||
Match: libCryptoCVE_2023_1255,
|
||||
|
@ -187,7 +187,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
|
|||
pkgContext: pkgContext,
|
||||
matches: getSubject(),
|
||||
},
|
||||
wantMatches: metchesRef(libCryptoCVE_2023_3817),
|
||||
wantMatches: matchesRef(libCryptoCVE_2023_3817),
|
||||
wantIgnoredMatches: []match.IgnoredMatch{
|
||||
{
|
||||
Match: libCryptoCVE_2023_1255,
|
||||
|
@ -226,7 +226,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
|
|||
pkgContext: pkgContext,
|
||||
matches: getSubject(),
|
||||
},
|
||||
wantMatches: metchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
|
||||
wantMatches: matchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
|
||||
wantIgnoredMatches: []match.IgnoredMatch{
|
||||
{
|
||||
Match: libCryptoCVE_2023_1255,
|
||||
|
@ -258,7 +258,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
|
|||
matches: getSubject(),
|
||||
},
|
||||
// nothing gets ignored!
|
||||
wantMatches: metchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975, libCryptoCVE_2023_1255),
|
||||
wantMatches: matchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975, libCryptoCVE_2023_1255),
|
||||
wantIgnoredMatches: []match.IgnoredMatch{},
|
||||
},
|
||||
{
|
||||
|
@ -278,7 +278,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
|
|||
pkgContext: pkgContext,
|
||||
matches: getSubject(),
|
||||
},
|
||||
wantMatches: metchesRef(libCryptoCVE_2023_2975, libCryptoCVE_2023_1255),
|
||||
wantMatches: matchesRef(libCryptoCVE_2023_2975, libCryptoCVE_2023_1255),
|
||||
wantIgnoredMatches: []match.IgnoredMatch{
|
||||
{
|
||||
Match: libCryptoCVE_2023_3817,
|
||||
|
|
20
grype/vex/testdata/vex-docs/openvex-debian.json
vendored
Normal file
20
grype/vex/testdata/vex-docs/openvex-debian.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"@context": "https://openvex.dev/ns/v0.2.0",
|
||||
"@id": "https://openvex.dev/docs/public/vex-d4e9020b6d0d26f131d535e055902dd6ccf3e2088bce3079a8cd3588a4b14c78",
|
||||
"author": "The OpenVEX Project <openvex@openssf.org>",
|
||||
"timestamp": "2023-07-17T18:28:47.696004345-06:00",
|
||||
"version": 1,
|
||||
"statements": [
|
||||
{
|
||||
"vulnerability": {
|
||||
"name": "CVE-2014-fake-1"
|
||||
},
|
||||
"products": [
|
||||
{
|
||||
"@id": "pkg:oci/debian@sha256%3A124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126?repository_url=index.docker.io/library"
|
||||
}
|
||||
],
|
||||
"status": "fixed"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -83,6 +83,11 @@ func (m *VulnerabilityMatcher) FindMatches(pkgs []pkg.Package, context pkg.Conte
|
|||
return remainingMatches, ignoredMatches, err
|
||||
}
|
||||
|
||||
if m.FailSeverity != nil && HasSeverityAtOrAbove(m.Store, *m.FailSeverity, *remainingMatches) {
|
||||
err = grypeerr.ErrAboveSeverityThreshold
|
||||
return remainingMatches, ignoredMatches, err
|
||||
}
|
||||
|
||||
logListSummary(progressMonitor)
|
||||
|
||||
logIgnoredMatches(ignoredMatches)
|
||||
|
@ -114,10 +119,6 @@ func (m *VulnerabilityMatcher) findDBMatches(pkgs []pkg.Package, context pkg.Con
|
|||
matches, ignoredMatches = m.applyIgnoreRules(normalizedMatches)
|
||||
}
|
||||
|
||||
if m.FailSeverity != nil && HasSeverityAtOrAbove(m.Store, *m.FailSeverity, matches) {
|
||||
err = grypeerr.ErrAboveSeverityThreshold
|
||||
}
|
||||
|
||||
return &matches, ignoredMatches, err
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,13 @@ import (
|
|||
"github.com/anchore/grype/grype/search"
|
||||
"github.com/anchore/grype/grype/store"
|
||||
"github.com/anchore/grype/grype/version"
|
||||
"github.com/anchore/grype/grype/vex"
|
||||
"github.com/anchore/grype/grype/vulnerability"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
syftPkg "github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type ack interface {
|
||||
|
@ -327,6 +329,7 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
IgnoreRules []match.IgnoreRule
|
||||
FailSeverity *vulnerability.Severity
|
||||
NormalizeByCVE bool
|
||||
VexProcessor *vex.Processor
|
||||
}
|
||||
type args struct {
|
||||
pkgs []pkg.Package
|
||||
|
@ -466,6 +469,86 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
wantIgnoredMatches: nil,
|
||||
wantErr: grypeerr.ErrAboveSeverityThreshold,
|
||||
},
|
||||
{
|
||||
name: "pass on severity threshold with VEX",
|
||||
fields: fields{
|
||||
Store: str,
|
||||
Matchers: matcher.NewDefaultMatchers(matcher.Config{}),
|
||||
FailSeverity: func() *vulnerability.Severity {
|
||||
x := vulnerability.LowSeverity
|
||||
return &x
|
||||
}(),
|
||||
VexProcessor: vex.NewProcessor(vex.ProcessorOptions{
|
||||
Documents: []string{
|
||||
"vex/testdata/vex-docs/openvex-debian.json",
|
||||
},
|
||||
IgnoreRules: []match.IgnoreRule{
|
||||
{
|
||||
VexStatus: "fixed",
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
args: args{
|
||||
pkgs: []pkg.Package{
|
||||
neutron2013Pkg,
|
||||
},
|
||||
context: pkg.Context{
|
||||
Source: &source.Description{
|
||||
Name: "debian",
|
||||
Version: "2013.1.1-1",
|
||||
Metadata: source.StereoscopeImageSourceMetadata{
|
||||
RepoDigests: []string{
|
||||
"debian@sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126",
|
||||
},
|
||||
},
|
||||
},
|
||||
Distro: &linux.Release{
|
||||
ID: "debian",
|
||||
VersionID: "8",
|
||||
},
|
||||
},
|
||||
},
|
||||
wantMatches: match.NewMatches(),
|
||||
wantIgnoredMatches: []match.IgnoredMatch{
|
||||
{
|
||||
AppliedIgnoreRules: []match.IgnoreRule{
|
||||
{
|
||||
Namespace: "vex",
|
||||
VexStatus: "fixed",
|
||||
},
|
||||
},
|
||||
Match: match.Match{
|
||||
Vulnerability: vulnerability.Vulnerability{
|
||||
Constraint: version.MustGetConstraint("< 2014.1.3-6", version.DebFormat),
|
||||
ID: "CVE-2014-fake-1",
|
||||
Namespace: "debian:distro:debian:8",
|
||||
PackageQualifiers: []qualifier.Qualifier{},
|
||||
CPEs: []cpe.CPE{},
|
||||
Advisories: []vulnerability.Advisory{},
|
||||
},
|
||||
Package: neutron2013Pkg,
|
||||
Details: match.Details{
|
||||
{
|
||||
Type: match.ExactDirectMatch,
|
||||
SearchedBy: map[string]any{
|
||||
"distro": map[string]string{"type": "debian", "version": "8"},
|
||||
"namespace": "debian:distro:debian:8",
|
||||
"package": map[string]string{"name": "neutron", "version": "2013.1.1-1"},
|
||||
},
|
||||
Found: map[string]any{
|
||||
"versionConstraint": "< 2014.1.3-6 (deb)",
|
||||
"vulnerabilityID": "CVE-2014-fake-1",
|
||||
},
|
||||
Matcher: "dpkg-matcher",
|
||||
Confidence: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
{
|
||||
name: "matches by exact-direct match (language)",
|
||||
fields: fields{
|
||||
|
@ -884,6 +967,7 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
|
|||
IgnoreRules: tt.fields.IgnoreRules,
|
||||
FailSeverity: tt.fields.FailSeverity,
|
||||
NormalizeByCVE: tt.fields.NormalizeByCVE,
|
||||
VexProcessor: tt.fields.VexProcessor,
|
||||
}
|
||||
actualMatches, actualIgnoreMatches, err := m.FindMatches(tt.args.pkgs, tt.args.context)
|
||||
if tt.wantErr != nil {
|
||||
|
|
Loading…
Reference in a new issue