From b618b84bac2cd7098abf8ee8742bd662ed617804 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Wed, 22 Dec 2021 13:27:41 -0500 Subject: [PATCH] Ignore explicit list of log4j false positive matches (#559) --- grype/match/explicit_ignores.go | 55 ++++++++++++++++ grype/match/explicit_ignores_test.go | 94 ++++++++++++++++++++++++++++ grype/matcher/controller.go | 2 + 3 files changed, 151 insertions(+) create mode 100644 grype/match/explicit_ignores.go create mode 100644 grype/match/explicit_ignores_test.go diff --git a/grype/match/explicit_ignores.go b/grype/match/explicit_ignores.go new file mode 100644 index 00000000..12a4e6de --- /dev/null +++ b/grype/match/explicit_ignores.go @@ -0,0 +1,55 @@ +package match + +import "github.com/anchore/grype/internal/log" + +var explicitIgnoreRules []IgnoreRule + +func init() { + type ignoreValues struct { + typ string + vulnerabilities []string + packages []string + } + + var explicitIgnores = []ignoreValues{ + // Based on https://github.com/anchore/grype/issues/552, which includes a reference to the + // https://github.com/mergebase/log4j-samples collection, we want to filter these explicitly: + {"java-archive", + []string{"CVE-2021-44228", "CVE-2021-45046", "GHSA-jfh8-c2jp-5v3q", "GHSA-7rjr-3q55-vv33"}, + []string{"log4j-api", "log4j-slf4j-impl", "log4j-to-slf4j", "log4j-1.2-api", "log4j-detector", "log4j-over-slf4j", "slf4j-log4j12"}, + }, + } + + for _, ignore := range explicitIgnores { + for _, vulnerability := range ignore.vulnerabilities { + for _, packageName := range ignore.packages { + explicitIgnoreRules = append(explicitIgnoreRules, IgnoreRule{ + Vulnerability: vulnerability, + Package: IgnoreRulePackage{ + Name: packageName, + Type: ignore.typ, + }, + }) + } + } + } +} + +// ApplyExplicitIgnoreRules This will filter out matches that are defined above, in the +// explicitIgnores list +func ApplyExplicitIgnoreRules(matches Matches) Matches { + matches, ignored := ApplyIgnoreRules(matches, explicitIgnoreRules) + + if len(ignored) > 0 { + log.Debugf("Removed %d explicit vulnerability matches:", len(ignored)) + for idx, i := range ignored { + branch := "├──" + if idx == len(ignored)-1 { + branch = "└──" + } + log.Debugf(" %s %s : %s", branch, i.Match.Vulnerability.ID, i.Package.PURL) + } + } + + return matches +} diff --git a/grype/match/explicit_ignores_test.go b/grype/match/explicit_ignores_test.go new file mode 100644 index 00000000..f9d13bb9 --- /dev/null +++ b/grype/match/explicit_ignores_test.go @@ -0,0 +1,94 @@ +package match + +import ( + "testing" + + "github.com/anchore/grype/grype/pkg" + "github.com/anchore/grype/grype/vulnerability" + syftPkg "github.com/anchore/syft/syft/pkg" + "github.com/stretchr/testify/assert" +) + +func Test_ApplyExplicitIgnoreRules(t *testing.T) { + type cvePkg struct { + cve string + pkg string + } + tests := []struct { + name string + typ syftPkg.Type + matches []cvePkg + expected []string + }{ + // some explicit log4j-related data: + // "CVE-2021-44228", "CVE-2021-45046", "GHSA-jfh8-c2jp-5v3q", "GHSA-7rjr-3q55-vv33", + // "log4j-api", "log4j-slf4j-impl", "log4j-to-slf4j", "log4j-1.2-api", + { + name: "keeps non-matching packages", + typ: "java-archive", + matches: []cvePkg{ + {"CVE-2021-44228", "log4j-core"}, + {"CVE-2021-43452", "foo-tool"}, + }, + expected: []string{"log4j-core", "foo-tool"}, + }, + { + name: "keeps non-matching CVEs", + typ: "java-archive", + matches: []cvePkg{ + {"CVE-2021-428", "log4j-api"}, + {"CVE-2021-43452", "foo-tool"}, + }, + expected: []string{"log4j-api", "foo-tool"}, + }, + { + name: "filters only matching CVE and package", + typ: "java-archive", + matches: []cvePkg{ + {"CVE-2021-44228", "log4j-api"}, + {"CVE-2021-44228", "log4j-core"}, + }, + expected: []string{"log4j-core"}, + }, + { + name: "filters all matching CVEs and packages", + typ: "java-archive", + matches: []cvePkg{ + {"GHSA-jfh8-c2jp-5v3q", "log4j-api"}, + {"GHSA-jfh8-c2jp-5v3q", "log4j-slf4j-impl"}, + }, + expected: []string{}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + matches := Matches{ + byPackage: make(map[pkg.ID][]Match), + } + + for _, cp := range test.matches { + matches.byPackage[pkg.ID(cp.pkg)] = []Match{ + { + Package: pkg.Package{ + Name: cp.pkg, + Type: test.typ, + }, + Vulnerability: vulnerability.Vulnerability{ + ID: cp.cve, + }, + }, + } + } + + filtered := ApplyExplicitIgnoreRules(matches) + + var found []string + for match := range filtered.Enumerate() { + found = append(found, match.Package.Name) + + } + assert.ElementsMatch(t, test.expected, found) + }) + } +} diff --git a/grype/matcher/controller.go b/grype/matcher/controller.go index f8f06a16..bae86b6b 100644 --- a/grype/matcher/controller.go +++ b/grype/matcher/controller.go @@ -108,6 +108,8 @@ func (c *controller) findMatches(provider vulnerability.Provider, d *distro.Dist packagesProcessed.SetCompleted() vulnerabilitiesDiscovered.SetCompleted() + res = match.ApplyExplicitIgnoreRules(res) + return res }