From 595c2ff8fe8933954ed95bc3b5842c911649198a Mon Sep 17 00:00:00 2001 From: Dan Luhring Date: Mon, 13 Nov 2023 16:41:51 -0500 Subject: [PATCH] fix(apk): find secdb entries for origin packages Signed-off-by: Dan Luhring --- grype/matcher/apk/matcher.go | 24 ++++++++----- grype/matcher/apk/matcher_test.go | 58 ++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/grype/matcher/apk/matcher.go b/grype/matcher/apk/matcher.go index f3bf4b4037d5..6822ab6d44e8 100644 --- a/grype/matcher/apk/matcher.go +++ b/grype/matcher/apk/matcher.go @@ -28,15 +28,15 @@ func (m *Matcher) Type() match.MatcherType { func (m *Matcher) Match(store vulnerability.Provider, d *distro.Distro, p pkg.Package) ([]match.Match, error) { var matches = make([]match.Match, 0) - // direct matches with package - directMatches, err := m.findApkPackage(store, d, p) + // direct matches with package itself + directMatches, err := m.findMatchesForPackage(store, d, p) if err != nil { return nil, err } matches = append(matches, directMatches...) - // indirect matches with package source - indirectMatches, err := m.matchBySourceIndirection(store, d, p) + // indirect matches, via package's origin package + indirectMatches, err := m.findMatchesForOriginPackage(store, d, p) if err != nil { return nil, err } @@ -61,6 +61,14 @@ func (m *Matcher) cpeMatchesWithoutSecDBFixes(store vulnerability.Provider, d *d return nil, err } + for _, upstreamPkg := range pkg.UpstreamPackages(p) { + secDBVulnerabilitiesForUpstream, err := store.GetByDistro(d, upstreamPkg) + if err != nil { + return nil, err + } + secDBVulnerabilities = append(secDBVulnerabilities, secDBVulnerabilitiesForUpstream...) + } + secDBVulnerabilitiesByID := vulnerabilitiesByID(secDBVulnerabilities) verObj, err := version.NewVersionFromPkg(p) @@ -139,8 +147,8 @@ func vulnerabilitiesByID(vulns []vulnerability.Vulnerability) map[string][]vulne return results } -func (m *Matcher) findApkPackage(store vulnerability.Provider, d *distro.Distro, p pkg.Package) ([]match.Match, error) { - // find Alpine SecDB matches for the given package name and version +func (m *Matcher) findMatchesForPackage(store vulnerability.Provider, d *distro.Distro, p pkg.Package) ([]match.Match, error) { + // find SecDB matches for the given package name and version secDBMatches, err := search.ByPackageDistro(store, d, p, m.Type()) if err != nil { return nil, err @@ -163,11 +171,11 @@ func (m *Matcher) findApkPackage(store vulnerability.Provider, d *distro.Distro, return matches, nil } -func (m *Matcher) matchBySourceIndirection(store vulnerability.Provider, d *distro.Distro, p pkg.Package) ([]match.Match, error) { +func (m *Matcher) findMatchesForOriginPackage(store vulnerability.Provider, d *distro.Distro, p pkg.Package) ([]match.Match, error) { var matches []match.Match for _, indirectPackage := range pkg.UpstreamPackages(p) { - indirectMatches, err := m.findApkPackage(store, d, indirectPackage) + indirectMatches, err := m.findMatchesForPackage(store, d, indirectPackage) if err != nil { return nil, fmt.Errorf("failed to find vulnerabilities for apk upstream source package: %w", err) } diff --git a/grype/matcher/apk/matcher_test.go b/grype/matcher/apk/matcher_test.go index 6b046024946a..45c4619ffa89 100644 --- a/grype/matcher/apk/matcher_test.go +++ b/grype/matcher/apk/matcher_test.go @@ -25,7 +25,7 @@ type mockStore struct { } func (s *mockStore) GetVulnerability(namespace, id string) ([]grypeDB.Vulnerability, error) { - //TODO implement me + // TODO implement me panic("implement me") } @@ -556,6 +556,62 @@ func TestNvdMatchesNoConstraintWithSecDBFix(t *testing.T) { assertMatches(t, expected, actual) } +func TestNVDMatchCanceledByOriginPackageInSecDB(t *testing.T) { + nvdVuln := grypeDB.Vulnerability{ + ID: "CVE-2015-3211", + VersionFormat: "unknown", + CPEs: []string{"cpe:2.3:a:php-fpm:php-fpm:-:*:*:*:*:*:*:*"}, + Namespace: "nvd:cpe", + } + secDBVuln := grypeDB.Vulnerability{ + ID: "CVE-2015-3211", + VersionConstraint: "< 0", + VersionFormat: "apk", + Namespace: "wolfi:distro:wolfi:rolling", + } + store := mockStore{ + backend: map[string]map[string][]grypeDB.Vulnerability{ + "nvd:cpe": { + "php-fpm": []grypeDB.Vulnerability{nvdVuln}, + }, + "wolfi:distro:wolfi:rolling": { + "php-8.3": []grypeDB.Vulnerability{secDBVuln}, + }, + }, + } + + provider, err := db.NewVulnerabilityProvider(&store) + require.NoError(t, err) + + m := Matcher{} + d, err := distro.New(distro.Wolfi, "") + if err != nil { + t.Fatalf("failed to create a new distro: %+v", err) + } + p := pkg.Package{ + ID: pkg.ID(uuid.NewString()), + Name: "php-8.3-fpm", + Version: "8.3.11-r0", + Type: syftPkg.ApkPkg, + CPEs: []cpe.CPE{ + cpe.Must("cpe:2.3:a:php-fpm:php-fpm:8.3.11-r0:*:*:*:*:*:*:*", ""), + }, + Upstreams: []pkg.UpstreamPackage{ + { + Name: "php-8.3", + Version: "8.3.11-r0", + }, + }, + } + + expected := []match.Match{} + + actual, err := m.Match(provider, d, p) + assert.NoError(t, err) + + assertMatches(t, expected, actual) +} + func TestDistroMatchBySourceIndirection(t *testing.T) { secDbVuln := grypeDB.Vulnerability{