osv: move methods to internal

Move methods to internal/ so that the osv package reflects only the JSON
structs with no dependencies.

Change-Id: I68d53f5ac599a45fc2901eab0e4a3df140822e01
Reviewed-on: https://go-review.googlesource.com/c/vuln/+/475095
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
Run-TryBot: Julie Qiu <julieqiu@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/internal/vulncheck/json_test.go b/internal/vulncheck/json_test.go
new file mode 100644
index 0000000..f9ea20e
--- /dev/null
+++ b/internal/vulncheck/json_test.go
@@ -0,0 +1,137 @@
+// Copyright 2021 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 vulncheck
+
+import (
+	"testing"
+
+	"golang.org/x/vuln/osv"
+)
+
+func TestAffectsSemver(t *testing.T) {
+	cases := []struct {
+		affects osv.Affects
+		version string
+		want    bool
+	}{
+		{
+			// empty Affects indicates everything is affected
+			affects: osv.Affects{},
+			version: "v0.0.0",
+			want:    true,
+		},
+		{
+			// Affects containing an empty SEMVER range also indicates
+			// everything is affected
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver}},
+			version: "v0.0.0",
+			want:    true,
+		},
+		{
+			// Affects containing a SEMVER range with only an "introduced":"0"
+			// also indicates everything is affected
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "0"}}}},
+			version: "v0.0.0",
+			want:    true,
+		},
+		{
+			// v1.0.0 < v2.0.0
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "0"}, {Fixed: "2.0.0"}}}},
+			version: "v1.0.0",
+			want:    true,
+		},
+		{
+			// v0.0.1 <= v1.0.0
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "0.0.1"}}}},
+			version: "v1.0.0",
+			want:    true,
+		},
+		{
+			// v1.0.0 <= v1.0.0
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.0"}}}},
+			version: "v1.0.0",
+			want:    true,
+		},
+		{
+			// v1.0.0 <= v1.0.0 < v2.0.0
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.0"}, {Fixed: "2.0.0"}}}},
+			version: "v1.0.0",
+			want:    true,
+		},
+		{
+			// v0.0.1 <= v1.0.0 < v2.0.0
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "0.0.1"}, {Fixed: "2.0.0"}}}},
+			version: "v1.0.0",
+			want:    true,
+		},
+		{
+			// v2.0.0 < v3.0.0
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.0"}, {Fixed: "2.0.0"}}}},
+			version: "v3.0.0",
+			want:    false,
+		},
+		{
+			// Multiple ranges
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.0.0"}, {Fixed: "2.0.0"}, {Introduced: "3.0.0"}}}},
+			version: "v3.0.0",
+			want:    true,
+		},
+		{
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "0"}, {Fixed: "1.18.6"}, {Introduced: "1.19.0"}, {Fixed: "1.19.1"}}}},
+			version: "v1.18.6",
+			want:    false,
+		},
+		{
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "0"}, {Introduced: "1.19.0"}, {Fixed: "1.19.1"}}}},
+			version: "v1.18.6",
+			want:    true,
+		},
+		{
+			// Multiple non-sorted ranges.
+			affects: []osv.AffectsRange{{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "1.19.0"}, {Fixed: "1.19.1"}, {Introduced: "0"}, {Fixed: "1.18.6"}}}},
+			version: "v1.18.1",
+			want:    true,
+		},
+		{
+			// Wrong type range
+			affects: []osv.AffectsRange{{Type: osv.TypeUnspecified, Events: []osv.RangeEvent{{Introduced: "3.0.0"}}}},
+			version: "v3.0.0",
+			want:    true,
+		},
+		{
+			// Semver ranges don't match
+			affects: []osv.AffectsRange{
+				{Type: osv.TypeUnspecified, Events: []osv.RangeEvent{{Introduced: "3.0.0"}}},
+				{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "4.0.0"}}},
+			},
+			version: "v3.0.0",
+			want:    false,
+		},
+		{
+			// Semver ranges do match
+			affects: []osv.AffectsRange{
+				{Type: osv.TypeUnspecified, Events: []osv.RangeEvent{{Introduced: "3.0.0"}}},
+				{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "3.0.0"}}},
+			},
+			version: "v3.0.0",
+			want:    true,
+		},
+		{
+			// Semver ranges match (go prefix)
+			affects: []osv.AffectsRange{
+				{Type: osv.TypeSemver, Events: []osv.RangeEvent{{Introduced: "3.0.0"}}},
+			},
+			version: "go3.0.1",
+			want:    true,
+		},
+	}
+
+	for _, c := range cases {
+		got := affectsSemver(c.affects, c.version)
+		if c.want != got {
+			t.Errorf("%#v.AffectsSemver(%s): want %t, got %t", c.affects, c.version, c.want, got)
+		}
+	}
+}
diff --git a/internal/vulncheck/semver.go b/internal/vulncheck/semver.go
new file mode 100644
index 0000000..54e6208
--- /dev/null
+++ b/internal/vulncheck/semver.go
@@ -0,0 +1,95 @@
+// Copyright 2023 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 vulncheck
+
+import (
+	"sort"
+
+	"golang.org/x/mod/semver"
+	isem "golang.org/x/vuln/internal/semver"
+	"golang.org/x/vuln/osv"
+)
+
+func affectsSemver(a osv.Affects, v string) bool {
+	if len(a) == 0 {
+		// No ranges implies all versions are affected
+		return true
+	}
+	var semverRangePresent bool
+	for _, r := range a {
+		if r.Type != osv.TypeSemver {
+			continue
+		}
+		semverRangePresent = true
+		if containsSemver(r, v) {
+			return true
+		}
+	}
+	// If there were no semver ranges present we
+	// assume that all semvers are affected, similarly
+	// to how to we assume all semvers are affected
+	// if there are no ranges at all.
+	return !semverRangePresent
+}
+
+// containsSemver checks if semver version v is in the
+// range encoded by ar. If ar is not a semver range,
+// returns false.
+//
+// Assumes that
+//   - exactly one of Introduced or Fixed fields is set
+//   - ranges in ar are not overlapping
+//   - beginning of time is encoded with .Introduced="0"
+//   - no-fix is not an event, as opposed to being an
+//     event where Introduced="" and Fixed=""
+func containsSemver(ar osv.AffectsRange, v string) bool {
+	if ar.Type != osv.TypeSemver {
+		return false
+	}
+	if len(ar.Events) == 0 {
+		return true
+	}
+
+	// Strip and then add the semver prefix so we can support bare versions,
+	// versions prefixed with 'v', and versions prefixed with 'go'.
+	v = isem.CanonicalizeSemverPrefix(v)
+
+	// Sort events by semver versions. Event for beginning
+	// of time, if present, always comes first.
+	sort.SliceStable(ar.Events, func(i, j int) bool {
+		e1 := ar.Events[i]
+		v1 := e1.Introduced
+		if v1 == "0" {
+			// -inf case.
+			return true
+		}
+		if e1.Fixed != "" {
+			v1 = e1.Fixed
+		}
+
+		e2 := ar.Events[j]
+		v2 := e2.Introduced
+		if v2 == "0" {
+			// -inf case.
+			return false
+		}
+		if e2.Fixed != "" {
+			v2 = e2.Fixed
+		}
+
+		return semver.Compare(isem.CanonicalizeSemverPrefix(v1), isem.CanonicalizeSemverPrefix(v2)) < 0
+	})
+
+	var affected bool
+	for _, e := range ar.Events {
+		if !affected && e.Introduced != "" {
+			affected = e.Introduced == "0" || semver.Compare(v, isem.CanonicalizeSemverPrefix(e.Introduced)) >= 0
+		} else if affected && e.Fixed != "" {
+			affected = semver.Compare(v, isem.CanonicalizeSemverPrefix(e.Fixed)) < 0
+		}
+	}
+
+	return affected
+}
diff --git a/internal/vulncheck/vulncheck.go b/internal/vulncheck/vulncheck.go
index b8caa11..8c0b97d 100644
--- a/internal/vulncheck/vulncheck.go
+++ b/internal/vulncheck/vulncheck.go
@@ -347,7 +347,7 @@
 					// TODO: issue warning for "" cases above?
 					continue
 				}
-				if !a.Ranges.AffectsSemver(modVersion) {
+				if !affectsSemver(a.Ranges, modVersion) {
 					continue
 				}
 				var filteredImports []osv.EcosystemSpecificImport
diff --git a/osv/json.go b/osv/json.go
index 1116852..d58639c 100644
--- a/osv/json.go
+++ b/osv/json.go
@@ -11,13 +11,7 @@
 // range type is implemented).
 package osv
 
-import (
-	"sort"
-	"time"
-
-	"golang.org/x/mod/semver"
-	isem "golang.org/x/vuln/internal/semver"
-)
+import "time"
 
 type AffectsRangeType string
 
@@ -46,90 +40,8 @@
 	Events []RangeEvent     `json:"events"`
 }
 
-// containsSemver checks if semver version v is in the
-// range encoded by ar. If ar is not a semver range,
-// returns false.
-//
-// Assumes that
-//   - exactly one of Introduced or Fixed fields is set
-//   - ranges in ar are not overlapping
-//   - beginning of time is encoded with .Introduced="0"
-//   - no-fix is not an event, as opposed to being an
-//     event where Introduced="" and Fixed=""
-func (ar AffectsRange) containsSemver(v string) bool {
-	if ar.Type != TypeSemver {
-		return false
-	}
-	if len(ar.Events) == 0 {
-		return true
-	}
-
-	// Strip and then add the semver prefix so we can support bare versions,
-	// versions prefixed with 'v', and versions prefixed with 'go'.
-	v = isem.CanonicalizeSemverPrefix(v)
-
-	// Sort events by semver versions. Event for beginning
-	// of time, if present, always comes first.
-	sort.SliceStable(ar.Events, func(i, j int) bool {
-		e1 := ar.Events[i]
-		v1 := e1.Introduced
-		if v1 == "0" {
-			// -inf case.
-			return true
-		}
-		if e1.Fixed != "" {
-			v1 = e1.Fixed
-		}
-
-		e2 := ar.Events[j]
-		v2 := e2.Introduced
-		if v2 == "0" {
-			// -inf case.
-			return false
-		}
-		if e2.Fixed != "" {
-			v2 = e2.Fixed
-		}
-
-		return semver.Compare(isem.CanonicalizeSemverPrefix(v1), isem.CanonicalizeSemverPrefix(v2)) < 0
-	})
-
-	var affected bool
-	for _, e := range ar.Events {
-		if !affected && e.Introduced != "" {
-			affected = e.Introduced == "0" || semver.Compare(v, isem.CanonicalizeSemverPrefix(e.Introduced)) >= 0
-		} else if affected && e.Fixed != "" {
-			affected = semver.Compare(v, isem.CanonicalizeSemverPrefix(e.Fixed)) < 0
-		}
-	}
-
-	return affected
-}
-
 type Affects []AffectsRange
 
-func (a Affects) AffectsSemver(v string) bool {
-	if len(a) == 0 {
-		// No ranges implies all versions are affected
-		return true
-	}
-	var semverRangePresent bool
-	for _, r := range a {
-		if r.Type != TypeSemver {
-			continue
-		}
-		semverRangePresent = true
-		if r.containsSemver(v) {
-			return true
-		}
-	}
-	// If there were no semver ranges present we
-	// assume that all semvers are affected, similarly
-	// to how to we assume all semvers are affected
-	// if there are no ranges at all.
-	return !semverRangePresent
-}
-
 type Reference struct {
 	Type string `json:"type"`
 	URL  string `json:"url"`
diff --git a/osv/json_test.go b/osv/json_test.go
deleted file mode 100644
index 5ad252e..0000000
--- a/osv/json_test.go
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 2021 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 osv
-
-import (
-	"testing"
-)
-
-func TestAffectsSemver(t *testing.T) {
-	cases := []struct {
-		affects Affects
-		version string
-		want    bool
-	}{
-		{
-			// empty Affects indicates everything is affected
-			affects: Affects{},
-			version: "v0.0.0",
-			want:    true,
-		},
-		{
-			// Affects containing an empty SEMVER range also indicates
-			// everything is affected
-			affects: []AffectsRange{{Type: TypeSemver}},
-			version: "v0.0.0",
-			want:    true,
-		},
-		{
-			// Affects containing a SEMVER range with only an "introduced":"0"
-			// also indicates everything is affected
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "0"}}}},
-			version: "v0.0.0",
-			want:    true,
-		},
-		{
-			// v1.0.0 < v2.0.0
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "0"}, {Fixed: "2.0.0"}}}},
-			version: "v1.0.0",
-			want:    true,
-		},
-		{
-			// v0.0.1 <= v1.0.0
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "0.0.1"}}}},
-			version: "v1.0.0",
-			want:    true,
-		},
-		{
-			// v1.0.0 <= v1.0.0
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "1.0.0"}}}},
-			version: "v1.0.0",
-			want:    true,
-		},
-		{
-			// v1.0.0 <= v1.0.0 < v2.0.0
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "1.0.0"}, {Fixed: "2.0.0"}}}},
-			version: "v1.0.0",
-			want:    true,
-		},
-		{
-			// v0.0.1 <= v1.0.0 < v2.0.0
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "0.0.1"}, {Fixed: "2.0.0"}}}},
-			version: "v1.0.0",
-			want:    true,
-		},
-		{
-			// v2.0.0 < v3.0.0
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "1.0.0"}, {Fixed: "2.0.0"}}}},
-			version: "v3.0.0",
-			want:    false,
-		},
-		{
-			// Multiple ranges
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "1.0.0"}, {Fixed: "2.0.0"}, {Introduced: "3.0.0"}}}},
-			version: "v3.0.0",
-			want:    true,
-		},
-		{
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "0"}, {Fixed: "1.18.6"}, {Introduced: "1.19.0"}, {Fixed: "1.19.1"}}}},
-			version: "v1.18.6",
-			want:    false,
-		},
-		{
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "0"}, {Introduced: "1.19.0"}, {Fixed: "1.19.1"}}}},
-			version: "v1.18.6",
-			want:    true,
-		},
-		{
-			// Multiple non-sorted ranges.
-			affects: []AffectsRange{{Type: TypeSemver, Events: []RangeEvent{{Introduced: "1.19.0"}, {Fixed: "1.19.1"}, {Introduced: "0"}, {Fixed: "1.18.6"}}}},
-			version: "v1.18.1",
-			want:    true,
-		},
-		{
-			// Wrong type range
-			affects: []AffectsRange{{Type: TypeUnspecified, Events: []RangeEvent{{Introduced: "3.0.0"}}}},
-			version: "v3.0.0",
-			want:    true,
-		},
-		{
-			// Semver ranges don't match
-			affects: []AffectsRange{
-				{Type: TypeUnspecified, Events: []RangeEvent{{Introduced: "3.0.0"}}},
-				{Type: TypeSemver, Events: []RangeEvent{{Introduced: "4.0.0"}}},
-			},
-			version: "v3.0.0",
-			want:    false,
-		},
-		{
-			// Semver ranges do match
-			affects: []AffectsRange{
-				{Type: TypeUnspecified, Events: []RangeEvent{{Introduced: "3.0.0"}}},
-				{Type: TypeSemver, Events: []RangeEvent{{Introduced: "3.0.0"}}},
-			},
-			version: "v3.0.0",
-			want:    true,
-		},
-		{
-			// Semver ranges match (go prefix)
-			affects: []AffectsRange{
-				{Type: TypeSemver, Events: []RangeEvent{{Introduced: "3.0.0"}}},
-			},
-			version: "go3.0.1",
-			want:    true,
-		},
-	}
-
-	for _, c := range cases {
-		got := c.affects.AffectsSemver(c.version)
-		if c.want != got {
-			t.Errorf("%#v.AffectsSemver(%s): want %t, got %t", c.affects, c.version, c.want, got)
-		}
-	}
-}