Skip to content

Commit

Permalink
rhel: update RHEL matcher to account for CPE subset matching
Browse files Browse the repository at this point in the history
Start matching repository CPEs based on the CPE subset relation.
This change interprets VEX CPEs identifying Red Hat repositories as CPE
matching expressions and looks for a subset relation with the record's
repositoty CPE.

Signed-off-by: crozzy <joseph.crosland@gmail.com>
  • Loading branch information
crozzy committed May 1, 2024
1 parent 230c585 commit 49217ca
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 3 deletions.
23 changes: 22 additions & 1 deletion rhel/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/quay/claircore"
"github.com/quay/claircore/libvuln/driver"
"github.com/quay/claircore/toolkit/types/cpe"
)

// Matcher implements driver.Matcher.
Expand All @@ -28,12 +29,32 @@ func (*Matcher) Filter(record *claircore.IndexRecord) bool {
func (*Matcher) Query() []driver.MatchConstraint {
return []driver.MatchConstraint{
driver.PackageModule,
driver.RepositoryName,
}
}

// Vulnerable implements driver.Matcher.
//
// Vulnerable() will interpret the claircore.Vulnerability.Repo.CPE
// as a CPE match expression and in order to be considered vulnerable
// the relationship between claircore.IndexRecord.Repository.CPE and
// the claircore.Vulnerability.Repo.CPE needs to be a CPE Name Comparison
// Relation of SUBSET(⊂)(source is a subset of, or equal to the target).
// https://nvlpubs.nist.gov/nistpubs/Legacy/IR/nistir7696.pdf Section 6.2.
func (m *Matcher) Vulnerable(_ context.Context, record *claircore.IndexRecord, vuln *claircore.Vulnerability) (bool, error) {
if vuln.Repo == nil || record.Repository == nil {
return false, nil

Check warning on line 45 in rhel/matcher.go

View check run for this annotation

Codecov / codecov/patch

rhel/matcher.go#L45

Added line #L45 was not covered by tests
}
var err error
// This conversion has to be done because our current data-structure doesn't
// support the claircore.Vulnerability.Repo.CPE field.
vuln.Repo.CPE, err = cpe.Unbind(vuln.Repo.Name)
if err != nil {
return false, nil
}
if !cpe.Compare(record.Repository.CPE, vuln.Repo.CPE).IsSubset() {
return false, nil

Check warning on line 55 in rhel/matcher.go

View check run for this annotation

Codecov / codecov/patch

rhel/matcher.go#L55

Added line #L55 was not covered by tests
}

pkgVer := version.NewVersion(record.Package.Version)
var vulnVer version.Version
// Assume the vulnerability record we have is for the last known vulnerable
Expand Down
44 changes: 44 additions & 0 deletions rhel/matcher_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/quay/claircore/pkg/ctxlock"
"github.com/quay/claircore/test/integration"
pgtest "github.com/quay/claircore/test/postgres"
"github.com/quay/claircore/toolkit/types/cpe"
)

func TestMain(m *testing.M) {
Expand Down Expand Up @@ -118,37 +119,80 @@ func TestVulnerable(t *testing.T) {
Package: &claircore.Package{
Version: "0.33.0-6.el8",
},
Repository: &claircore.Repository{
CPE: cpe.MustUnbind("cpe:/o:redhat:enterprise_linux:8::baseos"),
Name: "cpe:/o:redhat:enterprise_linux:8::baseos",
Key: "rhel-cpe-repository",
},
}
fixedVulnPast := &claircore.Vulnerability{
Package: &claircore.Package{
Version: "",
},
FixedInVersion: "0.33.0-5.el8",
Repo: &claircore.Repository{
Name: "cpe:/o:redhat:enterprise_linux:8::baseos",
Key: "rhel-cpe-repository",
},
}
fixedVulnCurrent := &claircore.Vulnerability{
Package: &claircore.Package{
Version: "",
},
FixedInVersion: "0.33.0-6.el8",
Repo: &claircore.Repository{
Name: "cpe:/o:redhat:enterprise_linux:8::baseos",
Key: "rhel-cpe-repository",
},
}
fixedVulnFuture := &claircore.Vulnerability{
Package: &claircore.Package{
Version: "",
},
FixedInVersion: "0.33.0-7.el8",
Repo: &claircore.Repository{
Name: "cpe:/o:redhat:enterprise_linux:8::baseos",
Key: "rhel-cpe-repository",
},
}
unfixedVuln := &claircore.Vulnerability{
Package: &claircore.Package{
Version: "",
},
FixedInVersion: "",
Repo: &claircore.Repository{
Name: "cpe:/o:redhat:enterprise_linux:8::baseos",
Key: "rhel-cpe-repository",
},
}
unfixedVulnBadCPE := &claircore.Vulnerability{
Package: &claircore.Package{
Version: "",
},
FixedInVersion: "",
Repo: &claircore.Repository{
Name: "cep:o:redhat:enterprise_linux:8::baseos",
Key: "rhel-cpe-repository",
},
}
unfixedVulnRepoNotSubset := &claircore.Vulnerability{
Package: &claircore.Package{
Version: "",
},
FixedInVersion: "",
Repo: &claircore.Repository{
Name: "cep:o:redhat:enterprise_linux:8::appstream",
Key: "rhel-cpe-repository",
},
}

testCases := []vulnerableTestCase{
{ir: record, v: fixedVulnPast, want: false, name: "vuln fixed in past version"},
{ir: record, v: fixedVulnCurrent, want: false, name: "vuln fixed in current version"},
{ir: record, v: fixedVulnFuture, want: true, name: "outdated package"},
{ir: record, v: unfixedVuln, want: true, name: "unfixed vuln"},
{ir: record, v: unfixedVulnBadCPE, want: false, name: "unfixed vuln, invalid CPE"},
{ir: record, v: unfixedVulnRepoNotSubset, want: false, name: "unfixed vuln, Repo not a subset"},
}

m := &Matcher{}
Expand Down
2 changes: 0 additions & 2 deletions rhel/vex/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,6 @@ func (c *creator) fixedVulnerabilities(ctx context.Context, v csaf.Vulnerability
return nil, fmt.Errorf("could not unbind cpe: %s %w", cpeHelper, err)

Check warning on line 296 in rhel/vex/parser.go

View check run for this annotation

Codecov / codecov/patch

rhel/vex/parser.go#L296

Added line #L296 was not covered by tests
}
vuln.Repo = &claircore.Repository{
// It _feels_ more correct to match on the CPE
// field here, double check the matcher logic.
CPE: wfn,
Name: cpeHelper,
Key: repoKey,
Expand Down

0 comments on commit 49217ca

Please sign in to comment.