internal/vulns: correctly interpret ranges

For golang/go#54480

Change-Id: I81295bfe6f03cf83d38c365195ea783115a0f959
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/427934
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Julie Qiu <julieqiu@google.com>
diff --git a/internal/frontend/vulns.go b/internal/frontend/vulns.go
index 217d511..0aadf39 100644
--- a/internal/frontend/vulns.go
+++ b/internal/frontend/vulns.go
@@ -300,12 +300,16 @@
 		var vs []string
 		for _, p := range pairs {
 			var s string
-			if p.intro == "" {
-				s = p.fixed + " and earlier"
+			if p.intro == "" && p.fixed == "" {
+				// If neither field is set, the vuln applies to all versions.
+				// Leave it blank, the template will render it properly.
+				s = ""
+			} else if p.intro == "" {
+				s = "before " + p.fixed
 			} else if p.fixed == "" {
 				s = p.intro + " and later"
 			} else {
-				s = p.intro + " - " + p.fixed
+				s = "from " + p.intro + " before " + p.fixed
 			}
 			vs = append(vs, s)
 		}
diff --git a/internal/frontend/vulns_test.go b/internal/frontend/vulns_test.go
index a130ddb..2fc2fb2 100644
--- a/internal/frontend/vulns_test.go
+++ b/internal/frontend/vulns_test.go
@@ -220,3 +220,57 @@
 		})
 	}
 }
+
+func TestAffectedPackages(t *testing.T) {
+	for _, test := range []struct {
+		name string
+		in   []osv.RangeEvent
+		want string
+	}{
+		{
+			"no intro or fixed",
+			nil,
+			"",
+		},
+		{
+			"no intro",
+			[]osv.RangeEvent{{Fixed: "1.5"}},
+			"before v1.5",
+		},
+		{
+			"both",
+			[]osv.RangeEvent{{Introduced: "1.5"}, {Fixed: "1.10"}},
+			"from v1.5 before v1.10",
+		},
+		{
+			"multiple",
+			[]osv.RangeEvent{
+				{Introduced: "1.5", Fixed: "1.10"},
+				{Fixed: "2.3"},
+			},
+			"from v1.5 before v1.10, before v2.3",
+		},
+	} {
+		t.Run(test.name, func(t *testing.T) {
+			entry := &osv.Entry{
+				Affected: []osv.Affected{{
+					Package: osv.Package{Name: "example.com/p"},
+					EcosystemSpecific: osv.EcosystemSpecific{
+						Imports: []osv.EcosystemSpecificImport{{
+							Path: "example.com/p",
+						}},
+					},
+					Ranges: osv.Affects{{
+						Type:   osv.TypeSemver,
+						Events: test.in,
+					}},
+				}},
+			}
+			out := affectedPackages(entry)
+			got := out[0].Versions
+			if got != test.want {
+				t.Errorf("got %q, want %q\n", got, test.want)
+			}
+		})
+	}
+}