Skip to content

Commit

Permalink
Fix for allowing some version that were invalid
Browse files Browse the repository at this point in the history
The NewVersion function, which uses the loose parser, had the regex for
detection updated based on the official one. A change was made to allow
for versions like 1.2 and other "loose" ones (to use the node semver
term).

StrictNewVersion had some internal validation updated to catch issues.

For NewVersion, the benchmarking is now faster than the previous regex.

Ref Masterminds#211

Signed-off-by: Matt Farina <matt.farina@suse.com>
  • Loading branch information
mattfarina committed Nov 19, 2024
1 parent e6e3d4d commit 252dd61
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 9 deletions.
24 changes: 15 additions & 9 deletions version.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ var (
)

// semVerRegex is the regular expression used to parse a semantic version.
const semVerRegex string = `v?([0-9]+)(\.[0-9]+)?(\.[0-9]+)?` +
`(-([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?` +
`(\+([0-9A-Za-z\-]+(\.[0-9A-Za-z\-]+)*))?`
// This is not the official regex from the semver spec. It has been modified to allow for loose handling
// where versions like 2.1 are detected.
const semVerRegex string = `v?(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:\.(0|[1-9]\d*))?` +
`(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?` +
`(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?`

// Version represents a single semantic version.
type Version struct {
Expand Down Expand Up @@ -146,8 +148,8 @@ func NewVersion(v string) (*Version, error) {
}

sv := &Version{
metadata: m[8],
pre: m[5],
metadata: m[5],
pre: m[4],
original: v,
}

Expand All @@ -158,7 +160,7 @@ func NewVersion(v string) (*Version, error) {
}

if m[2] != "" {
sv.minor, err = strconv.ParseUint(strings.TrimPrefix(m[2], "."), 10, 64)
sv.minor, err = strconv.ParseUint(m[2], 10, 64)
if err != nil {
return nil, fmt.Errorf("Error parsing version segment: %s", err)
}
Expand All @@ -167,7 +169,7 @@ func NewVersion(v string) (*Version, error) {
}

if m[3] != "" {
sv.patch, err = strconv.ParseUint(strings.TrimPrefix(m[3], "."), 10, 64)
sv.patch, err = strconv.ParseUint(m[3], 10, 64)
if err != nil {
return nil, fmt.Errorf("Error parsing version segment: %s", err)
}
Expand Down Expand Up @@ -612,7 +614,9 @@ func containsOnly(s string, comp string) bool {
func validatePrerelease(p string) error {
eparts := strings.Split(p, ".")
for _, p := range eparts {
if containsOnly(p, num) {
if p == "" {
return ErrInvalidMetadata
} else if containsOnly(p, num) {
if len(p) > 1 && p[0] == '0' {
return ErrSegmentStartsZero
}
Expand All @@ -631,7 +635,9 @@ func validatePrerelease(p string) error {
func validateMetadata(m string) error {
eparts := strings.Split(m, ".")
for _, p := range eparts {
if !containsOnly(p, allowed) {
if p == "" {
return ErrInvalidMetadata
} else if !containsOnly(p, allowed) {
return ErrInvalidMetadata
}
}
Expand Down
31 changes: 31 additions & 0 deletions version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func TestStrictNewVersion(t *testing.T) {
{"v1.0", true},
{"1", true},
{"v1", true},
{"1.2", true},
{"1.2.beta", true},
{"v1.2.beta", true},
{"foo", true},
Expand All @@ -45,6 +46,25 @@ func TestStrictNewVersion(t *testing.T) {
// The SemVer spec in a pre-release expects to allow [0-9A-Za-z-]. But,
// the lack of all 3 parts in this version should produce an error.
{"20221209-update-renovatejson-v4", true},

// Various cases that are invalid semver
{"1.1.2+.123", true}, // A leading . in build metadata. This would signify that the first segment is empty
{"1.0.0-alpha_beta", true}, // An underscore in the pre-release is an invalid character
{"1.0.0-alpha..", true}, // Multiple empty segments
{"1.0.0-alpha..1", true}, // Multiple empty segments but one with a value
{"01.1.1", true}, // A leading 0 on a number segment
{"1.01.1", true}, // A leading 0 on a number segment
{"1.1.01", true}, // A leading 0 on a number segment
{"9.8.7+meta+meta", true}, // Multiple metadata parts
{"1.2.31----RC-SNAPSHOT.12.09.1--.12+788", true}, // Leading 0 in a number part of a pre-release segment
{"1.2.3-0123", true},
{"1.2.3-0123.0123", true},
{"+invalid", true},
{"-invalid", true},
{"-invalid.01", true},
{"alpha+beta", true},
{"1.2.3-alpha_beta+foo", true},
{"1.0.0-alpha..1", true},
}

for _, tc := range tests {
Expand Down Expand Up @@ -101,6 +121,17 @@ func TestNewVersion(t *testing.T) {

// The SemVer spec in a pre-release expects to allow [0-9A-Za-z-].
{"20221209-update-renovatejson-v4", false},

// Various cases that are invalid semver
{"1.1.2+.123", true}, // A leading . in build metadata. This would signify that the first segment is empty
{"1.0.0-alpha_beta", true}, // An underscore in the pre-release is an invalid character
{"1.0.0-alpha..", true}, // Multiple empty segments
{"1.0.0-alpha..1", true}, // Multiple empty segments but one with a value
{"01.1.1", true}, // A leading 0 on a number segment
{"1.01.1", true}, // A leading 0 on a number segment
{"1.1.01", true}, // A leading 0 on a number segment
{"9.8.7+meta+meta", true}, // Multiple metadata parts
{"1.2.31----RC-SNAPSHOT.12.09.1--.12+788", true}, // Leading 0 in a number part of a pre-release segment
}

for _, tc := range tests {
Expand Down

0 comments on commit 252dd61

Please sign in to comment.