From d6c3fe07fb2b113ee146feeace170c2615a561a4 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 17 Apr 2024 11:57:36 -0400 Subject: [PATCH 1/4] feat: narrow golang match comparison where a package with a pseudo version cannot be compared against a semver constraint Signed-off-by: Christopher Phillips --- grype/search/only_qualified_packages.go | 1 - grype/version/golang_constraint.go | 24 +++++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/grype/search/only_qualified_packages.go b/grype/search/only_qualified_packages.go index 9fbd2c3f4f3..875f67acb12 100644 --- a/grype/search/only_qualified_packages.go +++ b/grype/search/only_qualified_packages.go @@ -16,7 +16,6 @@ func onlyQualifiedPackages(d *distro.Distro, p pkg.Package, allVulns []vulnerabi for _, q := range vuln.PackageQualifiers { v, err := q.Satisfied(d, p) - if err != nil { return nil, fmt.Errorf("failed to check package qualifier=%q for distro=%q package=%q: %w", q, d, p, err) } diff --git a/grype/version/golang_constraint.go b/grype/version/golang_constraint.go index b0fdda9d3d8..244536e8522 100644 --- a/grype/version/golang_constraint.go +++ b/grype/version/golang_constraint.go @@ -1,6 +1,10 @@ package version -import "fmt" +import ( + "fmt" + "regexp" + "strings" +) var _ Constraint = (*golangConstraint)(nil) @@ -31,9 +35,27 @@ func (g golangConstraint) Satisfied(version *Version) (bool, error) { if g.raw == "" { return true, nil // the empty constraint is always satisfied } + + // when we get a pseudo version from the package and the constraint is not a pseudo version, we should not consider it as satisfied + // ex: constraint = ">=v1.0.0", version = "v0.0.0-0.20210101000000-abcdef123456" + if isPseudoVersion(version.String()) && !isPseudoVersion(g.raw) { + return false, nil + } + return g.expression.satisfied(version) } +// Define a regular expression pattern to match pseudo versions +const PseudoVersionPattern = `^v0\.0\.0[-+].*$` + +// Check if a version string is a pseudo version +func isPseudoVersion(version string) bool { + // List of prefixes commonly used for pseudo versions + regex := regexp.MustCompile(PseudoVersionPattern) + + return regex.MatchString(strings.TrimSpace(version)) +} + func newGolangComparator(unit constraintUnit) (Comparator, error) { ver, err := newGolangVersion(unit.version) if err != nil { From aa5bdb4f636f3ecd9a9029c1274070ddc9d7a59e Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 17 Apr 2024 12:02:30 -0400 Subject: [PATCH 2/4] test: add positive and negative cases for testing new match Signed-off-by: Christopher Phillips --- grype/version/golang_constraint_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/grype/version/golang_constraint_test.go b/grype/version/golang_constraint_test.go index 4a73767af01..92fae692eb5 100644 --- a/grype/version/golang_constraint_test.go +++ b/grype/version/golang_constraint_test.go @@ -38,6 +38,19 @@ func TestGolangConstraints(t *testing.T) { constraint: "", satisfied: true, }, + { + name: "pseudo version from package should not be considered as satisfied against semver constraint", + version: "v0.0.0-0.20210101000000-abcdef123456", + constraint: " Date: Wed, 17 Apr 2024 12:23:24 -0400 Subject: [PATCH 3/4] unit: update unit tests with new check for pseudo version constraint Signed-off-by: Christopher Phillips --- grype/matcher/golang/matcher_test.go | 2 +- grype/version/golang_constraint.go | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/grype/matcher/golang/matcher_test.go b/grype/matcher/golang/matcher_test.go index c9b3d417e3a..1b4498027d9 100644 --- a/grype/matcher/golang/matcher_test.go +++ b/grype/matcher/golang/matcher_test.go @@ -183,7 +183,7 @@ func (mp *mockProvider) populateData() { // for TestMatcher_DropMainPackageIfNoVersion "istio.io/istio": { { - Constraint: version.MustGetConstraint("< 5.0.7", version.UnknownFormat), + Constraint: version.MustGetConstraint("< v0.0.0-20230606222826-f59ce19ec6b6", version.UnknownFormat), ID: "CVE-2013-fake-BAD", }, }, diff --git a/grype/version/golang_constraint.go b/grype/version/golang_constraint.go index 244536e8522..eff5bea5781 100644 --- a/grype/version/golang_constraint.go +++ b/grype/version/golang_constraint.go @@ -36,16 +36,26 @@ func (g golangConstraint) Satisfied(version *Version) (bool, error) { return true, nil // the empty constraint is always satisfied } - // when we get a pseudo version from the package and the constraint is not a pseudo version, we should not consider it as satisfied - // ex: constraint = ">=v1.0.0", version = "v0.0.0-0.20210101000000-abcdef123456" - if isPseudoVersion(version.String()) && !isPseudoVersion(g.raw) { + var constraintContainsPseudoVersion bool + for _, units := range g.expression.units { + for _, unit := range units { + if isPseudoVersion(unit.version) { + constraintContainsPseudoVersion = true + break + } + } + } + // when we get a pseudo version from a package, and the constraint being compared against is not a pseudo version, + // we should not consider it as satisfied + // ex: constraint of type ">=v1.0.0", should not be compared to version = "v0.0.0-0.20210101000000-abcdef123456" + if isPseudoVersion(version.String()) && !constraintContainsPseudoVersion { return false, nil } return g.expression.satisfied(version) } -// Define a regular expression pattern to match pseudo versions +// PseudoVersionPattern is a regular expression pattern to match pseudo versions const PseudoVersionPattern = `^v0\.0\.0[-+].*$` // Check if a version string is a pseudo version From c1a08699c3678d86e3621b61560d810f35f70f56 Mon Sep 17 00:00:00 2001 From: Christopher Phillips Date: Wed, 17 Apr 2024 12:49:15 -0400 Subject: [PATCH 4/4] chore: compile once Signed-off-by: Christopher Phillips --- grype/version/golang_constraint.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/grype/version/golang_constraint.go b/grype/version/golang_constraint.go index eff5bea5781..0f966ef3a5b 100644 --- a/grype/version/golang_constraint.go +++ b/grype/version/golang_constraint.go @@ -56,14 +56,14 @@ func (g golangConstraint) Satisfied(version *Version) (bool, error) { } // PseudoVersionPattern is a regular expression pattern to match pseudo versions -const PseudoVersionPattern = `^v0\.0\.0[-+].*$` +const pseudoVersionPattern = `^v0\.0\.0[-+].*$` + +var pseudoVersionRegex = regexp.MustCompile(pseudoVersionPattern) // Check if a version string is a pseudo version func isPseudoVersion(version string) bool { // List of prefixes commonly used for pseudo versions - regex := regexp.MustCompile(PseudoVersionPattern) - - return regex.MatchString(strings.TrimSpace(version)) + return pseudoVersionRegex.MatchString(strings.TrimSpace(version)) } func newGolangComparator(unit constraintUnit) (Comparator, error) {