cmd/govulncheck/internal/govulncheck: canonicalize semvers
semver.Compare works on semvers that are prefixed with "v". We add "v"
if necessary. Otherwise, latest fixed version of a vulnerability can be
miscalculated.
For #54401
Change-Id: Ib653b84080235822c76612bded66eacb1dbc90eb
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/423375
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Zvonimir Pavlinovic <zpavlinovic@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
diff --git a/cmd/govulncheck/internal/govulncheck/util.go b/cmd/govulncheck/internal/govulncheck/util.go
index 6699236..9bd46b4 100644
--- a/cmd/govulncheck/internal/govulncheck/util.go
+++ b/cmd/govulncheck/internal/govulncheck/util.go
@@ -9,6 +9,7 @@
"strings"
"golang.org/x/mod/semver"
+ isem "golang.org/x/vuln/internal/semver"
"golang.org/x/vuln/osv"
"golang.org/x/vuln/vulncheck"
)
@@ -21,7 +22,8 @@
for _, r := range a.Ranges {
if r.Type == osv.TypeSemver {
for _, e := range r.Events {
- if e.Fixed != "" && (v == "" || semver.Compare(e.Fixed, v) > 0) {
+ if e.Fixed != "" && (v == "" ||
+ semver.Compare(isem.CanonicalizeSemverPrefix(e.Fixed), isem.CanonicalizeSemverPrefix(v)) > 0) {
v = e.Fixed
}
}
diff --git a/cmd/govulncheck/main_test.go b/cmd/govulncheck/main_test.go
index fa07746..608fa68 100644
--- a/cmd/govulncheck/main_test.go
+++ b/cmd/govulncheck/main_test.go
@@ -73,6 +73,30 @@
},
"v1.5.6",
},
+ {
+ "no v prefix",
+ []osv.Affected{
+ {
+ Ranges: osv.Affects{
+ {
+ Type: osv.TypeSemver,
+ Events: []osv.RangeEvent{
+ {Fixed: "1.17.2"},
+ },
+ }},
+ },
+ {
+ Ranges: osv.Affects{
+ {
+ Type: osv.TypeSemver,
+ Events: []osv.RangeEvent{
+ {Introduced: "1.18.0", Fixed: "1.18.4"},
+ },
+ }},
+ },
+ },
+ "1.18.4",
+ },
} {
t.Run(test.name, func(t *testing.T) {
got := govulncheck.LatestFixed(test.in)
diff --git a/internal/semver/semver.go b/internal/semver/semver.go
new file mode 100644
index 0000000..f93235d
--- /dev/null
+++ b/internal/semver/semver.go
@@ -0,0 +1,35 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package semver provides shared utilities for manipulating
+// Go semantic versions.
+package semver
+
+import "strings"
+
+// addSemverPrefix adds a 'v' prefix to s if it isn't already prefixed
+// with 'v' or 'go'. This allows us to easily test go-style SEMVER
+// strings against normal SEMVER strings.
+func addSemverPrefix(s string) string {
+ if !strings.HasPrefix(s, "v") && !strings.HasPrefix(s, "go") {
+ return "v" + s
+ }
+ return s
+}
+
+// removeSemverPrefix removes the 'v' or 'go' prefixes from go-style
+// SEMVER strings, for usage in the public vulnerability format.
+func removeSemverPrefix(s string) string {
+ s = strings.TrimPrefix(s, "v")
+ s = strings.TrimPrefix(s, "go")
+ return s
+}
+
+// CanonicalizeSemverPrefix turns a SEMVER string into the canonical
+// representation using the 'v' prefix, as used by the OSV format.
+// Input may be a bare SEMVER ("1.2.3"), Go prefixed SEMVER ("go1.2.3"),
+// or already canonical SEMVER ("v1.2.3").
+func CanonicalizeSemverPrefix(s string) string {
+ return addSemverPrefix(removeSemverPrefix(s))
+}
diff --git a/internal/semver/semver_test.go b/internal/semver/semver_test.go
new file mode 100644
index 0000000..8a46228
--- /dev/null
+++ b/internal/semver/semver_test.go
@@ -0,0 +1,25 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package semver
+
+import (
+ "testing"
+)
+
+func TestCanonicalize(t *testing.T) {
+ for _, test := range []struct {
+ v string
+ want string
+ }{
+ {"v1.2.3", "v1.2.3"},
+ {"1.2.3", "v1.2.3"},
+ {"go1.2.3", "v1.2.3"},
+ } {
+ got := CanonicalizeSemverPrefix(test.v)
+ if got != test.want {
+ t.Errorf("want %s; got %s", test.want, got)
+ }
+ }
+}
diff --git a/osv/json.go b/osv/json.go
index 2289a13..5685198 100644
--- a/osv/json.go
+++ b/osv/json.go
@@ -12,10 +12,10 @@
package osv
import (
- "strings"
"time"
"golang.org/x/mod/semver"
+ isem "golang.org/x/vuln/internal/semver"
)
type AffectsRangeType string
@@ -45,16 +45,6 @@
Events []RangeEvent `json:"events"`
}
-// addSemverPrefix adds a 'v' prefix to s if it isn't already prefixed
-// with 'v' or 'go'. This allows us to easily test go-style SEMVER
-// strings against normal SEMVER strings.
-func addSemverPrefix(s string) string {
- if !strings.HasPrefix(s, "v") && !strings.HasPrefix(s, "go") {
- return "v" + s
- }
- return s
-}
-
func (ar AffectsRange) containsSemver(v string) bool {
if ar.Type != TypeSemver {
return false
@@ -65,14 +55,14 @@
// Strip and then add the semver prefix so we can support bare versions,
// versions prefixed with 'v', and versions prefixed with 'go'.
- v = canonicalizeSemverPrefix(v)
+ v = isem.CanonicalizeSemverPrefix(v)
var affected bool
for _, e := range ar.Events {
if !affected && e.Introduced != "" {
- affected = e.Introduced == "0" || semver.Compare(v, addSemverPrefix(e.Introduced)) >= 0
+ affected = e.Introduced == "0" || semver.Compare(v, isem.CanonicalizeSemverPrefix(e.Introduced)) >= 0
} else if e.Fixed != "" {
- affected = semver.Compare(v, addSemverPrefix(e.Fixed)) < 0
+ affected = semver.Compare(v, isem.CanonicalizeSemverPrefix(e.Fixed)) < 0
}
}
@@ -160,19 +150,3 @@
Affected []Affected `json:"affected"`
References []Reference `json:"references,omitempty"`
}
-
-// removeSemverPrefix removes the 'v' or 'go' prefixes from go-style
-// SEMVER strings, for usage in the public vulnerability format.
-func removeSemverPrefix(s string) string {
- s = strings.TrimPrefix(s, "v")
- s = strings.TrimPrefix(s, "go")
- return s
-}
-
-// canonicalizeSemverPrefix turns a SEMVER string into the canonical
-// representation using the 'v' prefix, as used by the OSV format.
-// Input may be a bare SEMVER ("1.2.3"), Go prefixed SEMVER ("go1.2.3"),
-// or already canonical SEMVER ("v1.2.3").
-func canonicalizeSemverPrefix(s string) string {
- return addSemverPrefix(removeSemverPrefix(s))
-}