diff --git a/syft/pkg/cataloger/binary/classifiers.go b/syft/pkg/cataloger/binary/classifiers.go index 65f24e666..955a7bacd 100644 --- a/syft/pkg/cataloger/binary/classifiers.go +++ b/syft/pkg/cataloger/binary/classifiers.go @@ -106,13 +106,19 @@ func DefaultClassifiers() []binutils.Classifier { { Class: "redis-binary", FileGlob: "**/redis-server", - EvidenceMatcher: binutils.MatchAny( - // matches most recent versions of redis (~v7), e.g. "7.0.14buildkitsandbox-1702957741000000000" - m.FileContentsVersionMatcher(`[^\d](?P\d+.\d+\.\d+)buildkitsandbox-\d+`), - // matches against older versions of redis (~v3 - v6), e.g. "4.0.11841ce7054bd9-1542359302000000000" - m.FileContentsVersionMatcher(`[^\d](?P[0-9]+\.[0-9]+\.[0-9]+)\w{12}-\d+`), - // matches against older versions of redis (~v2), e.g. "Server started, Redis version 2.8.23" - m.FileContentsVersionMatcher(`Redis version (?P[0-9]+\.[0-9]+\.[0-9]+)`), + EvidenceMatcher: binutils.MatchAll( + // Negative Matchers to exclude valkey-server + binutils.MatchNone( + binutils.MatchPath("**/valkey-server"), + ), + binutils.MatchAny( + // matches most recent versions of redis (~v7), e.g. "7.0.14buildkitsandbox-1702957741000000000" + m.FileContentsVersionMatcher(`[^\d](?P\d+.\d+\.\d+)buildkitsandbox-\d+`), + // matches against older versions of redis (~v3 - v6), e.g. "4.0.11841ce7054bd9-1542359302000000000" + m.FileContentsVersionMatcher(`[^\d](?P[0-9]+\.[0-9]+\.[0-9]+)\w{12}-\d+`), + // matches against older versions of redis (~v2), e.g. "Server started, Redis version 2.8.23" + m.FileContentsVersionMatcher(`Redis version (?P[0-9]+\.[0-9]+\.[0-9]+)`), + ), ), Package: "redis", PURL: mustPURL("pkg:generic/redis@version"), diff --git a/syft/pkg/cataloger/binary/testdata/config.yaml b/syft/pkg/cataloger/binary/testdata/config.yaml index 0100b8315..ad63dfea6 100644 --- a/syft/pkg/cataloger/binary/testdata/config.yaml +++ b/syft/pkg/cataloger/binary/testdata/config.yaml @@ -625,12 +625,14 @@ from-images: paths: - /usr/local/bin/redis-server - - version: 9.0.0 + - name: valkey-server + version: 9.0.0 images: - ref: valkey/valkey:9.0.0@sha256:42ea97850708540d4e05f6241cfbd241c1ba502e64d9a42efb2c2e277a8ca9d6 platform: linux/amd64 paths: - /usr/local/bin/valkey-server + - /usr/local/bin/redis-server - version: 2.9.0 images: diff --git a/syft/pkg/cataloger/internal/binutils/classifier.go b/syft/pkg/cataloger/internal/binutils/classifier.go index f523d755f..011feaeeb 100644 --- a/syft/pkg/cataloger/internal/binutils/classifier.go +++ b/syft/pkg/cataloger/internal/binutils/classifier.go @@ -120,6 +120,20 @@ func MatchAll(matchers ...EvidenceMatcher) EvidenceMatcher { } } +// MatchNone succeeds only if the matcher returns no results. +func MatchNone(matcher EvidenceMatcher) EvidenceMatcher { + return func(classifier Classifier, context MatcherContext) ([]pkg.Package, error) { + match, err := matcher(classifier, context) + if err != nil { + return nil, err + } + if match != nil { + return nil, nil + } + return []pkg.Package{}, nil + } +} + type ContextualEvidenceMatchers struct { CatalogerName string } diff --git a/syft/pkg/cataloger/internal/binutils/classifier_test.go b/syft/pkg/cataloger/internal/binutils/classifier_test.go index 8983ce477..c9d1c9e31 100644 --- a/syft/pkg/cataloger/internal/binutils/classifier_test.go +++ b/syft/pkg/cataloger/internal/binutils/classifier_test.go @@ -243,3 +243,40 @@ func Test_SupportingEvidenceMatcher(t *testing.T) { }) } } + +func TestMatchNone(t *testing.T) { + matchingMatcher := MatchPath("**") + notMatchingMatcher := MatchPath("will-not-match") + + tests := []struct { + name string + matcher EvidenceMatcher + expected bool // true if MatchNone should succeed (inner failed) + }{ + { + name: "inner matches, MatchNone fails", + matcher: MatchNone(matchingMatcher), + expected: false, + }, + { + name: "inner fails, MatchNone succeeds", + matcher: MatchNone(notMatchingMatcher), + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pkgs, err := tt.matcher(Classifier{}, MatcherContext{ + Location: file.NewLocation("some/path"), + }) + require.NoError(t, err) + if tt.expected { + assert.NotNil(t, pkgs) + assert.Empty(t, pkgs) + } else { + assert.Nil(t, pkgs) + } + }) + } +}