From 0a0a7db8143c587ad0b84ca84f4bd9300e1b8825 Mon Sep 17 00:00:00 2001 From: Benji Visser Date: Fri, 18 Aug 2023 21:23:26 -0600 Subject: [PATCH] Adding matching for semver prerelease (#143) Signed-off-by: Benji Visser --- xeol/search/purl.go | 27 +++++++++++++++++++++++---- xeol/search/purl_test.go | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/xeol/search/purl.go b/xeol/search/purl.go index bae88384..9450958e 100644 --- a/xeol/search/purl.go +++ b/xeol/search/purl.go @@ -69,13 +69,27 @@ func ByDistroCpe(store eol.Provider, distro *linux.Release, eolMatchDate time.Ti return match.Match{}, nil } -// normalizeSemver returns the major.minor.patch portion of a semver string. -// it turns versions like 2.7.8p225 into 2.7.8 +// normalizeSemver returns the major.minor.patch portion of a semver string +// that may have other characters appended to it. We should be very careful +// here to create matches for patterns we KNOW, because otherwise we could +// introduce false positives. func normalizeSemver(version string) string { - re := regexp.MustCompile(`^(\d+\.\d+\.\d+).*`) + // For Ruby versions. Example: 2.5.3p105 -> 2.5.3 + re := regexp.MustCompile(`^(\d+\.\d+\.\d+)p\d+`) return re.ReplaceAllString(version, "$1") } +func versionLength(version string) int { + parts := strings.SplitN(version, "-", 2) + regularVersionParts := strings.Split(parts[0], ".") + length := len(regularVersionParts) + // increment if there's a pre-release part + if len(parts) > 1 && parts[1] != "" { + length++ + } + return length +} + // returnMatchingCycle returns the first cycle that matches the version string. // If no cycle matches, an empty cycle is returned. func returnMatchingCycle(version string, cycles []eol.Cycle) (eol.Cycle, error) { @@ -92,11 +106,12 @@ func returnMatchingCycle(version string, cycles []eol.Cycle) (eol.Cycle, error) } // match on major, minor, or patch - versionLength := len(strings.Split(c.ReleaseCycle, ".")) + versionLength := versionLength(c.ReleaseCycle) cv, err := semver.NewVersion(c.ReleaseCycle) if err != nil { return eol.Cycle{}, err } + switch versionLength { case 1: if v.Major() == cv.Major() { @@ -110,6 +125,10 @@ func returnMatchingCycle(version string, cycles []eol.Cycle) (eol.Cycle, error) if v.Major() == cv.Major() && v.Minor() == cv.Minor() && v.Patch() == cv.Patch() { return c, nil } + case 4: + if v.Major() == cv.Major() && v.Minor() == cv.Minor() && v.Patch() == cv.Patch() && v.Prerelease() == cv.Prerelease() { + return c, nil + } } } diff --git a/xeol/search/purl_test.go b/xeol/search/purl_test.go index 08fa243d..75e1284f 100644 --- a/xeol/search/purl_test.go +++ b/xeol/search/purl_test.go @@ -20,19 +20,23 @@ func TestNormalizeSemver(t *testing.T) { }, { version: "1.2.3-rc1", - expected: "1.2.3", + expected: "1.2.3-rc1", }, { version: "1.2.3-rc1+build1", - expected: "1.2.3", + expected: "1.2.3-rc1+build1", }, { version: "1.2.3p288", expected: "1.2.3", }, { - version: "1.2.3p288+1.3", - expected: "1.2.3", + version: "1.1.1-beta", + expected: "1.1.1-beta", + }, + { + version: "1.1.1-preview1", + expected: "1.1.1-preview1", }, } @@ -173,3 +177,27 @@ func TestReturnMatchingCycle(t *testing.T) { }) } } + +func TestVersionLength(t *testing.T) { + testCases := []struct { + version string + wantLength int + }{ + {"1.0.0", 3}, + {"1.0.0-beta", 4}, + {"1.0.0-beta.1", 4}, + {"2.3", 2}, + {"2", 1}, + {"1.1.1-preview1", 4}, + {"0.0.0", 3}, + {"0.0.0-alpha", 4}, + } + + for _, tt := range testCases { + t.Run(tt.version, func(t *testing.T) { + if gotLength := versionLength(tt.version); gotLength != tt.wantLength { + t.Errorf("versionLength() = %v, want %v", gotLength, tt.wantLength) + } + }) + } +}