From 8a0e6d894eee68fa61ef616b7caf1afe09bcfe0e Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 16 Sep 2024 17:34:33 -0400 Subject: [PATCH] add integration tests Signed-off-by: Alex Goodman --- go.mod | 2 +- go.sum | 4 +- grype/cpe/cpe.go | 2 + grype/internal/packagemetadata/generated.go | 2 +- grype/internal/packagemetadata/names_test.go | 5 + grype/matcher/jvm/matcher_test.go | 152 ++++++++++++++++++ grype/pkg/java_metadata.go | 18 ++- grype/pkg/java_metadata_test.go | 99 ++++++++++++ grype/search/cpe.go | 14 +- grype/version/format.go | 2 +- grype/version/generic_constraint.go | 38 +++++ grype/version/golang_constraint.go | 34 +--- grype/version/jvm_constraint.go | 34 +--- test/integration/db_mock_test.go | 9 ++ test/integration/match_by_image_test.go | 115 +++++++++++-- .../image-jvm-match-coverage/Dockerfile | 2 + .../opt/java/openjdk/release | 5 + 17 files changed, 451 insertions(+), 86 deletions(-) create mode 100644 grype/matcher/jvm/matcher_test.go create mode 100644 grype/pkg/java_metadata_test.go create mode 100644 grype/version/generic_constraint.go create mode 100644 test/integration/test-fixtures/image-jvm-match-coverage/Dockerfile create mode 100644 test/integration/test-fixtures/image-jvm-match-coverage/opt/java/openjdk/release diff --git a/go.mod b/go.mod index 50fee004d68..920307e0a48 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f github.com/anchore/stereoscope v0.0.3 - github.com/anchore/syft v1.12.2 + github.com/anchore/syft v1.12.3-0.20240916182519-7c617fd14e11 github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 github.com/bmatcuk/doublestar/v2 v2.0.4 github.com/charmbracelet/bubbletea v1.1.0 diff --git a/go.sum b/go.sum index 0b509373609..76b51d9c5b3 100644 --- a/go.sum +++ b/go.sum @@ -258,8 +258,8 @@ github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f h1:B/E9ixK github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= github.com/anchore/stereoscope v0.0.3 h1:JRPHySy8S6P+Ff3IDiQ29ap1i8/laUQxDk9K1eFh/2U= github.com/anchore/stereoscope v0.0.3/go.mod h1:5DJheGPjVRsSqegTB24Zi6SCHnYQnA519yeIG+RG+I4= -github.com/anchore/syft v1.12.2 h1:K5YXJ2Ox4C3+Q+rA4jDpsLAoYNd27RMfinvY2JmbEiM= -github.com/anchore/syft v1.12.2/go.mod h1:xFMGMFmhWTK0CJvaKwz6OPVgRdcyCkl7QO/3O/JAXI0= +github.com/anchore/syft v1.12.3-0.20240916182519-7c617fd14e11 h1:M7xJv6jPxyS2GaPmbS+l02YGnO77SGxwcprDhEUupVg= +github.com/anchore/syft v1.12.3-0.20240916182519-7c617fd14e11/go.mod h1:xFMGMFmhWTK0CJvaKwz6OPVgRdcyCkl7QO/3O/JAXI0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= diff --git a/grype/cpe/cpe.go b/grype/cpe/cpe.go index 99d288bb4b5..6f660e10337 100644 --- a/grype/cpe/cpe.go +++ b/grype/cpe/cpe.go @@ -24,8 +24,10 @@ func NewSlice(cpeStrs ...string) ([]cpe.CPE, error) { func MatchWithoutVersion(c cpe.CPE, candidates []cpe.CPE) []cpe.CPE { matches := make([]cpe.CPE, 0) a := wfn.Attributes(c.Attributes) + a.Update = wfn.Any for _, candidate := range candidates { canCopy := wfn.Attributes(candidate.Attributes) + canCopy.Update = wfn.Any if a.MatchWithoutVersion(&canCopy) { matches = append(matches, candidate) } diff --git a/grype/internal/packagemetadata/generated.go b/grype/internal/packagemetadata/generated.go index 4248a11c762..c70c2be3ef0 100644 --- a/grype/internal/packagemetadata/generated.go +++ b/grype/internal/packagemetadata/generated.go @@ -6,5 +6,5 @@ import "github.com/anchore/grype/grype/pkg" // AllTypes returns a list of all pkg metadata types that grype supports (that are represented in the pkg.Package.Metadata field). func AllTypes() []any { - return []any{pkg.ApkMetadata{}, pkg.GolangBinMetadata{}, pkg.GolangModMetadata{}, pkg.JavaMetadata{}, pkg.RpmMetadata{}} + return []any{pkg.ApkMetadata{}, pkg.GolangBinMetadata{}, pkg.GolangModMetadata{}, pkg.JavaMetadata{}, pkg.JavaVMInstallationMetadata{}, pkg.RpmMetadata{}} } diff --git a/grype/internal/packagemetadata/names_test.go b/grype/internal/packagemetadata/names_test.go index 8f3d64e45f7..f1d4e9333b7 100644 --- a/grype/internal/packagemetadata/names_test.go +++ b/grype/internal/packagemetadata/names_test.go @@ -58,6 +58,11 @@ func TestReflectTypeFromJSONName(t *testing.T) { lookup: "RpmMetadata", wantRecord: reflect.TypeOf(pkg.RpmMetadata{}), }, + { + name: "JavaVMInstallationMetadata lookup", + lookup: "JavaVMInstallationMetadata", + wantRecord: reflect.TypeOf(pkg.JavaVMInstallationMetadata{}), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/grype/matcher/jvm/matcher_test.go b/grype/matcher/jvm/matcher_test.go new file mode 100644 index 00000000000..d14230f765a --- /dev/null +++ b/grype/matcher/jvm/matcher_test.go @@ -0,0 +1,152 @@ +package jvm + +import ( + "testing" + + "github.com/google/uuid" + "github.com/scylladb/go-set/strset" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/grype/grype/distro" + "github.com/anchore/grype/grype/match" + "github.com/anchore/grype/grype/pkg" + "github.com/anchore/grype/grype/version" + "github.com/anchore/grype/grype/vulnerability" + "github.com/anchore/syft/syft/cpe" + syftPkg "github.com/anchore/syft/syft/pkg" +) + +func TestMatcher(t *testing.T) { + p := pkg.Package{ + ID: pkg.ID(uuid.NewString()), + Name: "java_se", + Version: "1.8.0_400", + Type: syftPkg.BinaryPkg, + CPEs: []cpe.CPE{ + cpe.Must("cpe:2.3:a:oracle:java_se:1.8.0:update400:*:*:*:*:*:*", cpe.DeclaredSource), + }, + } + matcher := Matcher{ + cfg: MatcherConfig{ + UseCPEs: true, + }, + } + store := newMockProvider() + actual, _ := matcher.Match(store, nil, p) + + foundCVEs := strset.New() + for _, v := range actual { + foundCVEs.Add(v.Vulnerability.ID) + + require.NotEmpty(t, v.Details) + for _, d := range v.Details { + assert.Equal(t, match.CPEMatch, d.Type, "indirect match not indicated") + assert.Equal(t, matcher.Type(), d.Matcher, "failed to capture matcher type") + } + assert.Equal(t, p.Name, v.Package.Name, "failed to capture original package name") + } + + expected := strset.New( + "CVE-2024-20919-real", + "CVE-2024-20919-underscore", + "CVE-2024-20919-bonkers-format", + "CVE-2024-20919-post-jep223", + ) + + for _, id := range expected.List() { + if !foundCVEs.Has(id) { + t.Errorf("missing CVE: %s", id) + } + } + + extra := strset.Difference(foundCVEs, expected) + + for _, id := range extra.List() { + t.Errorf("unexpected CVE: %s", id) + } + + if t.Failed() { + t.Logf("discovered CVES: %d", foundCVEs.Size()) + for _, id := range foundCVEs.List() { + t.Logf(" - %s", id) + } + } +} + +func newMockProvider() *mockProvider { + mp := mockProvider{ + data: make(map[syftPkg.Language]map[string][]vulnerability.Vulnerability), + } + + mp.populateData() + + return &mp +} + +type mockProvider struct { + data map[syftPkg.Language]map[string][]vulnerability.Vulnerability +} + +func (mp *mockProvider) Get(_, _ string) ([]vulnerability.Vulnerability, error) { + // TODO implement me + panic("not implemented") +} + +func (mp *mockProvider) populateData() { + + // derived from vuln data found on CVE-2024-20919 + hit := "< 1.8.0_401 || >= 1.9-ea, < 8.0.401 || >= 9-ea, < 11.0.22 || >= 12-ea, < 17.0.10 || >= 18-ea, < 21.0.2" + + mp.data["nvd:cpe"] = map[string][]vulnerability.Vulnerability{ + "java_se": { + { + // positive cases + Constraint: version.MustGetConstraint(hit, version.JVMFormat), + ID: "CVE-2024-20919-real", + }, + { + // positive cases + Constraint: version.MustGetConstraint("< 22.22.22", version.UnknownFormat), + ID: "CVE-2024-20919-bonkers-format", + }, + { + // positive cases + Constraint: version.MustGetConstraint(hit, version.JVMFormat), + ID: "CVE-2024-20919-underscore", + }, + { + // negative case + Constraint: version.MustGetConstraint("< 1.8.0_399 || >= 1.9-ea, < 8.0.399 || >= 9-ea, < 11.0.22 || >= 12-ea, < 17.0.10 || >= 18-ea, < 21.0.2", version.JVMFormat), + ID: "CVE-FAKE-bad-update", + }, + { + // positive case + Constraint: version.MustGetConstraint("< 8.0.401", version.JVMFormat), + ID: "CVE-2024-20919-post-jep223", + }, + { + // negative case + Constraint: version.MustGetConstraint("< 8.0.399", version.JVMFormat), + ID: "CVE-FAKE-bad-range-post-jep223", + }, + { + // negative case + Constraint: version.MustGetConstraint("< 7.0.0", version.JVMFormat), + ID: "CVE-FAKE-bad-range-post-jep223", + }, + }, + } +} + +func (mp *mockProvider) GetByCPE(p cpe.CPE) ([]vulnerability.Vulnerability, error) { + return mp.data["nvd:cpe"][p.Attributes.Product], nil +} + +func (mp *mockProvider) GetByDistro(d *distro.Distro, p pkg.Package) ([]vulnerability.Vulnerability, error) { + return []vulnerability.Vulnerability{}, nil +} + +func (mp *mockProvider) GetByLanguage(l syftPkg.Language, p pkg.Package) ([]vulnerability.Vulnerability, error) { + return mp.data[l][p.Name], nil +} diff --git a/grype/pkg/java_metadata.go b/grype/pkg/java_metadata.go index 8b2b2ac08b8..529f1450017 100644 --- a/grype/pkg/java_metadata.go +++ b/grype/pkg/java_metadata.go @@ -1,7 +1,7 @@ package pkg import ( - "strings" + "github.com/scylladb/go-set/strset" "github.com/anchore/syft/syft/pkg" ) @@ -31,15 +31,21 @@ type JavaVMReleaseMetadata struct { } func IsJvmPackage(p Package) bool { + if _, ok := p.Metadata.(JavaVMInstallationMetadata); ok { + return true + } + if p.Type == pkg.BinaryPkg { - if strings.Contains(p.Name, "jdk") || strings.Contains(p.Name, "jre") || strings.Contains(p.Name, "java") { + if HasJvmPackageName(p.Name) { return true } } - if _, ok := p.Metadata.(JavaVMInstallationMetadata); ok { - return true - } - return false } + +var jvmIndications = strset.New("java_se", "jre", "jdk", "zulu", "openjdk", "java") + +func HasJvmPackageName(name string) bool { + return jvmIndications.Has(name) +} diff --git a/grype/pkg/java_metadata_test.go b/grype/pkg/java_metadata_test.go new file mode 100644 index 00000000000..271ff885de0 --- /dev/null +++ b/grype/pkg/java_metadata_test.go @@ -0,0 +1,99 @@ +package pkg + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + syftPkg "github.com/anchore/syft/syft/pkg" +) + +func TestIsJvmPackage(t *testing.T) { + tests := []struct { + name string + pkg Package + expected bool + }{ + { + name: "binary package with jdk in name set", + pkg: Package{ + Type: syftPkg.BinaryPkg, + Name: "jdk", + }, + expected: true, + }, + { + name: "binary package with jre in name set", + pkg: Package{ + Type: syftPkg.BinaryPkg, + Name: "jre", + }, + expected: true, + }, + { + name: "binary package with java_se in name set", + pkg: Package{ + Type: syftPkg.BinaryPkg, + Name: "java_se", + }, + expected: true, + }, + { + name: "binary package with zulu in name set", + pkg: Package{ + Type: syftPkg.BinaryPkg, + Name: "zulu", + }, + expected: true, + }, + { + name: "binary package with openjdk in name set", + pkg: Package{ + Type: syftPkg.BinaryPkg, + Name: "openjdk", + }, + expected: true, + }, + { + name: "binary package without jvm-related name", + pkg: Package{ + Type: syftPkg.BinaryPkg, + Name: "nodejs", + }, + expected: false, + }, + { + name: "non-binary package with jvm-related name", + pkg: Package{ + Type: syftPkg.NpmPkg, // we know this could not be a JVM package installation + Name: "jdk", + }, + expected: false, + }, + { + name: "package with JavaVMInstallationMetadata", + pkg: Package{ + Type: syftPkg.RpmPkg, + Name: "random-package", + Metadata: JavaVMInstallationMetadata{}, + }, + expected: true, + }, + { + name: "package without JavaVMInstallationMetadata", + pkg: Package{ + Type: syftPkg.RpmPkg, + Name: "non-jvm-package", + Metadata: nil, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := IsJvmPackage(tt.pkg) + assert.Equal(t, tt.expected, result) + }) + } +} diff --git a/grype/search/cpe.go b/grype/search/cpe.go index 83ef8266639..28574696856 100644 --- a/grype/search/cpe.go +++ b/grype/search/cpe.go @@ -238,7 +238,19 @@ func filterCPEsByVersion(pkgVersion version.Version, allCPEs []cpe.CPE) (matched continue } - constraint, err := version.GetConstraint(c.Attributes.Version, version.UnknownFormat) + ver := c.Attributes.Version + + if pkgVersion.Format == version.JVMFormat { + if c.Attributes.Update != wfn.Any && c.Attributes.Update != wfn.NA { + if strings.HasPrefix(ver, "1.") && !strings.Contains(ver, "_") { + ver = fmt.Sprintf("%s_%s", ver, strings.TrimPrefix(strings.TrimPrefix(c.Attributes.Update, "update"), "_")) + } + } + } + + ver = "< " + ver + + constraint, err := version.GetConstraint(ver, pkgVersion.Format) if err != nil { // if we can't get a version constraint, don't filter out the CPE matchedCPEs = append(matchedCPEs, c) diff --git a/grype/version/format.go b/grype/version/format.go index ab7f7de8c93..c4a47c89570 100644 --- a/grype/version/format.go +++ b/grype/version/format.go @@ -25,7 +25,7 @@ const ( type Format int var formatStr = []string{ - "UnknownFormat", + "Unknown", "Semantic", "Apk", "Deb", diff --git a/grype/version/generic_constraint.go b/grype/version/generic_constraint.go new file mode 100644 index 00000000000..ebd039e290d --- /dev/null +++ b/grype/version/generic_constraint.go @@ -0,0 +1,38 @@ +package version + +import "fmt" + +var _ Constraint = (*genericConstraint)(nil) + +type genericConstraint struct { + raw string + expression constraintExpression + name string +} + +func newGenericConstraint(raw string, genFn comparatorGenerator, name string) (genericConstraint, error) { + constraints, err := newConstraintExpression(raw, genFn) + if err != nil { + return genericConstraint{}, err + } + return genericConstraint{ + expression: constraints, + raw: raw, + name: name, + }, nil +} + +func (g genericConstraint) String() string { + value := "none" + if g.raw != "" { + value = g.raw + } + return fmt.Sprintf("%s (%s)", value, g.name) +} + +func (g genericConstraint) Satisfied(version *Version) (bool, error) { + if g.raw == "" { + return true, nil // the empty constraint is always satisfied + } + return g.expression.satisfied(version) +} diff --git a/grype/version/golang_constraint.go b/grype/version/golang_constraint.go index b0fdda9d3d8..f9443311a3d 100644 --- a/grype/version/golang_constraint.go +++ b/grype/version/golang_constraint.go @@ -2,42 +2,14 @@ package version import "fmt" -var _ Constraint = (*golangConstraint)(nil) - -type golangConstraint struct { - raw string - expression constraintExpression -} - -func newGolangConstraint(raw string) (golangConstraint, error) { - constraints, err := newConstraintExpression(raw, newGolangComparator) - if err != nil { - return golangConstraint{}, err - } - return golangConstraint{ - expression: constraints, - raw: raw, - }, nil -} - -func (g golangConstraint) String() string { - if g.raw == "" { - return "none (go)" - } - return fmt.Sprintf("%s (go)", g.raw) -} - -func (g golangConstraint) Satisfied(version *Version) (bool, error) { - if g.raw == "" { - return true, nil // the empty constraint is always satisfied - } - return g.expression.satisfied(version) +func newGolangConstraint(raw string) (Constraint, error) { + return newGenericConstraint(raw, newGolangComparator, "go") } func newGolangComparator(unit constraintUnit) (Comparator, error) { ver, err := newGolangVersion(unit.version) if err != nil { - return nil, fmt.Errorf("unable to parse constraint version (%s): %w", unit.version, err) + return nil, fmt.Errorf("unable to parse Golang constraint version (%s): %w", unit.version, err) } return ver, nil } diff --git a/grype/version/jvm_constraint.go b/grype/version/jvm_constraint.go index bae6e06436e..1f7930f4d5e 100644 --- a/grype/version/jvm_constraint.go +++ b/grype/version/jvm_constraint.go @@ -2,42 +2,14 @@ package version import "fmt" -var _ Constraint = (*jvmConstraint)(nil) - -type jvmConstraint struct { - raw string - expression constraintExpression -} - -func newJvmConstraint(raw string) (jvmConstraint, error) { - constraints, err := newConstraintExpression(raw, newJvmComparator) - if err != nil { - return jvmConstraint{}, err - } - return jvmConstraint{ - expression: constraints, - raw: raw, - }, nil -} - -func (g jvmConstraint) String() string { - if g.raw == "" { - return "none (jvm)" - } - return fmt.Sprintf("%s (jvm)", g.raw) -} - -func (g jvmConstraint) Satisfied(version *Version) (bool, error) { - if g.raw == "" { - return true, nil // the empty constraint is always satisfied - } - return g.expression.satisfied(version) +func newJvmConstraint(raw string) (Constraint, error) { + return newGenericConstraint(raw, newJvmComparator, "jvm") } func newJvmComparator(unit constraintUnit) (Comparator, error) { ver, err := newJvmVersion(unit.version) if err != nil { - return nil, fmt.Errorf("unable to parse constraint version (%s): %w", unit.version, err) + return nil, fmt.Errorf("unable to parse JVM constraint version (%s): %w", unit.version, err) } return ver, nil } diff --git a/test/integration/db_mock_test.go b/test/integration/db_mock_test.go index e71a1ae525b..fe844f72c40 100644 --- a/test/integration/db_mock_test.go +++ b/test/integration/db_mock_test.go @@ -43,6 +43,15 @@ func newMockDbStore() *mockStore { }, backend: map[string]map[string][]grypeDB.Vulnerability{ "nvd:cpe": { + "jdk": []grypeDB.Vulnerability{ + { + ID: "CVE-jdk", + PackageName: "jdk", + VersionConstraint: "< 1.8.0_401", + VersionFormat: "jvm", + CPEs: []string{"cpe:2.3:a:oracle:jdk:1.8.0:update_401:*:*:*:*:*:*"}, // note extra _ + }, + }, "libvncserver": []grypeDB.Vulnerability{ { ID: "CVE-alpine-libvncserver", diff --git a/test/integration/match_by_image_test.go b/test/integration/match_by_image_test.go index 8f1cdf35b62..bea742d01a1 100644 --- a/test/integration/match_by_image_test.go +++ b/test/integration/match_by_image_test.go @@ -14,7 +14,17 @@ import ( "github.com/anchore/grype/grype/db" "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/matcher" + "github.com/anchore/grype/grype/matcher/dotnet" + "github.com/anchore/grype/grype/matcher/golang" + "github.com/anchore/grype/grype/matcher/java" + "github.com/anchore/grype/grype/matcher/javascript" + "github.com/anchore/grype/grype/matcher/jvm" + "github.com/anchore/grype/grype/matcher/python" + "github.com/anchore/grype/grype/matcher/ruby" + "github.com/anchore/grype/grype/matcher/rust" + "github.com/anchore/grype/grype/matcher/stock" "github.com/anchore/grype/grype/pkg" + "github.com/anchore/grype/grype/search" "github.com/anchore/grype/grype/store" "github.com/anchore/grype/grype/vex" "github.com/anchore/grype/grype/vulnerability" @@ -541,6 +551,50 @@ func addHaskellMatches(t *testing.T, theSource source.Source, catalog *syftPkg.C }) } +func addJvmMatches(t *testing.T, theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore, theResult *match.Matches) { + packages := catalog.PackagesByPath("/opt/java/openjdk/release") + if len(packages) < 1 { + t.Logf("JVM Packages: %+v", packages) + t.Fatalf("problem with upstream syft cataloger (java-jvm-cataloger)") + } + + for _, p := range packages { + thePkg := pkg.New(p) + theVuln := theStore.backend["nvd:cpe"][strings.ToLower(thePkg.Name)][0] + vulnObj, err := vulnerability.NewVulnerability(theVuln) + vulnObj.CPEs = []cpe.CPE{ + cpe.Must("cpe:2.3:a:oracle:jdk:1.8.0:update_401:*:*:*:*:*:*", ""), + } + require.NoError(t, err) + + theResult.Add(match.Match{ + Vulnerability: *vulnObj, + Package: thePkg, + Details: []match.Detail{ + { + Type: match.CPEMatch, + Confidence: 0.9, + SearchedBy: search.CPEParameters{ + Namespace: "nvd:cpe", + CPEs: []string{ + "cpe:2.3:a:oracle:jdk:1.8.0:update400:*:*:*:*:*:*", + }, + Package: search.CPEPackageParameter{Name: "jdk", Version: "1.8.0_400-b07"}, + }, + Found: search.CPEResult{ + VulnerabilityID: "CVE-jdk", + VersionConstraint: "< 1.8.0_401 (jvm)", + CPEs: []string{ + "cpe:2.3:a:oracle:jdk:1.8.0:update_401:*:*:*:*:*:*", + }, + }, + Matcher: match.JVMMatcher, + }, + }, + }) + } +} + func addRustMatches(t *testing.T, theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore, theResult *match.Matches) { packages := catalog.PackagesByPath("/hello-auditable") if len(packages) < 1 { @@ -588,11 +642,11 @@ func TestMatchByImage(t *testing.T) { } tests := []struct { - fixtureImage string - expectedFn func(source.Source, *syftPkg.Collection, *mockStore) match.Matches + name string + expectedFn func(source.Source, *syftPkg.Collection, *mockStore) match.Matches }{ { - fixtureImage: "image-debian-match-coverage", + name: "image-debian-match-coverage", expectedFn: func(theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore) match.Matches { expectedMatches := match.NewMatches() addPythonMatches(t, theSource, catalog, theStore, &expectedMatches) @@ -607,7 +661,7 @@ func TestMatchByImage(t *testing.T) { }, }, { - fixtureImage: "image-centos-match-coverage", + name: "image-centos-match-coverage", expectedFn: func(theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore) match.Matches { expectedMatches := match.NewMatches() addRhelMatches(t, theSource, catalog, theStore, &expectedMatches) @@ -615,7 +669,7 @@ func TestMatchByImage(t *testing.T) { }, }, { - fixtureImage: "image-alpine-match-coverage", + name: "image-alpine-match-coverage", expectedFn: func(theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore) match.Matches { expectedMatches := match.NewMatches() addAlpineMatches(t, theSource, catalog, theStore, &expectedMatches) @@ -623,7 +677,7 @@ func TestMatchByImage(t *testing.T) { }, }, { - fixtureImage: "image-sles-match-coverage", + name: "image-sles-match-coverage", expectedFn: func(theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore) match.Matches { expectedMatches := match.NewMatches() addSlesMatches(t, theSource, catalog, theStore, &expectedMatches) @@ -631,7 +685,7 @@ func TestMatchByImage(t *testing.T) { }, }, { - fixtureImage: "image-portage-match-coverage", + name: "image-portage-match-coverage", expectedFn: func(theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore) match.Matches { expectedMatches := match.NewMatches() addPortageMatches(t, theSource, catalog, theStore, &expectedMatches) @@ -639,21 +693,29 @@ func TestMatchByImage(t *testing.T) { }, }, { - fixtureImage: "image-rust-auditable-match-coverage", + name: "image-rust-auditable-match-coverage", expectedFn: func(theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore) match.Matches { expectedMatches := match.NewMatches() addRustMatches(t, theSource, catalog, theStore, &expectedMatches) return expectedMatches }, }, + { + name: "image-jvm-match-coverage", + expectedFn: func(theSource source.Source, catalog *syftPkg.Collection, theStore *mockStore) match.Matches { + expectedMatches := match.NewMatches() + addJvmMatches(t, theSource, catalog, theStore, &expectedMatches) + return expectedMatches + }, + }, } for _, test := range tests { - t.Run(test.fixtureImage, func(t *testing.T) { + t.Run(test.name, func(t *testing.T) { theStore := newMockDbStore() - imagetest.GetFixtureImage(t, "docker-archive", test.fixtureImage) - tarPath := imagetest.GetFixtureImageTarPath(t, test.fixtureImage) + imagetest.GetFixtureImage(t, "docker-archive", test.name) + tarPath := imagetest.GetFixtureImageTarPath(t, test.name) // this is purely done to help setup mocks theSource, err := syft.GetSource(context.Background(), tarPath, syft.DefaultGetSourceConfig().WithSources("docker-archive")) @@ -671,7 +733,36 @@ func TestMatchByImage(t *testing.T) { require.NoError(t, err) require.NotNil(t, s) - matchers := matcher.NewDefaultMatchers(matcher.Config{}) + // TODO: we need to use the API default configuration, not something hard coded here + matchers := matcher.NewDefaultMatchers(matcher.Config{ + Java: java.MatcherConfig{ + UseCPEs: true, + }, + JVM: jvm.MatcherConfig{ + UseCPEs: true, + }, + Ruby: ruby.MatcherConfig{ + UseCPEs: true, + }, + Python: python.MatcherConfig{ + UseCPEs: true, + }, + Dotnet: dotnet.MatcherConfig{ + UseCPEs: true, + }, + Javascript: javascript.MatcherConfig{ + UseCPEs: true, + }, + Golang: golang.MatcherConfig{ + UseCPEs: true, + }, + Rust: rust.MatcherConfig{ + UseCPEs: true, + }, + Stock: stock.MatcherConfig{ + UseCPEs: true, + }, + }) vp, err := db.NewVulnerabilityProvider(theStore) require.NoError(t, err) diff --git a/test/integration/test-fixtures/image-jvm-match-coverage/Dockerfile b/test/integration/test-fixtures/image-jvm-match-coverage/Dockerfile new file mode 100644 index 00000000000..04fdc270146 --- /dev/null +++ b/test/integration/test-fixtures/image-jvm-match-coverage/Dockerfile @@ -0,0 +1,2 @@ +FROM scratch +COPY . . diff --git a/test/integration/test-fixtures/image-jvm-match-coverage/opt/java/openjdk/release b/test/integration/test-fixtures/image-jvm-match-coverage/opt/java/openjdk/release new file mode 100644 index 00000000000..532486dc639 --- /dev/null +++ b/test/integration/test-fixtures/image-jvm-match-coverage/opt/java/openjdk/release @@ -0,0 +1,5 @@ +JAVA_VERSION="1.8.0_400" +FULL_VERSION="1.8.0_400-b07" +NOPE_SEMANTIC_VERSION="8.0.400+7" +IMPLEMENTOR="Oracle" +IMAGE_TYPE="JDK"