all: use consistent index type across packages

All reports still need to have their published fields set.

Change-Id: I64feda32742bb5f85e310211f8da270e4346ad6b
Reviewed-on: https://team-review.git.corp.google.com/c/golang/vulndb/+/1036000
Reviewed-by: Roland Shoemaker <bracewell@google.com>
diff --git a/client/cache.go b/client/cache.go
index 3ef8d8a..05d031c 100644
--- a/client/cache.go
+++ b/client/cache.go
@@ -25,7 +25,7 @@
 // $GOPATH/pkg/mod/cache/download/vulndb/{db hostname}/indexes/index.json
 //   {
 //       Retrieved time.Time
-//       Index map[string]time.Time
+//       Index osv.DBIndex
 //   }
 //
 // Each package also has a JSON file which contains the array of vulnerability
@@ -35,8 +35,8 @@
 //   []*osv.Entry
 
 type Cache interface {
-	ReadIndex(string) (map[string]time.Time, time.Time, error)
-	WriteIndex(string, map[string]time.Time, time.Time) error
+	ReadIndex(string) (osv.DBIndex, time.Time, error)
+	WriteIndex(string, osv.DBIndex, time.Time) error
 	ReadEntries(string, string) ([]*osv.Entry, error)
 	WriteEntries(string, string, []*osv.Entry) error
 }
@@ -54,10 +54,10 @@
 
 type cachedIndex struct {
 	Retrieved time.Time
-	Index     map[string]time.Time
+	Index     osv.DBIndex
 }
 
-func (c *fsCache) ReadIndex(dbName string) (map[string]time.Time, time.Time, error) {
+func (c *fsCache) ReadIndex(dbName string) (osv.DBIndex, time.Time, error) {
 	b, err := os.ReadFile(filepath.Join(cacheRoot, dbName, "index.json"))
 	if err != nil {
 		if os.IsNotExist(err) {
@@ -72,7 +72,7 @@
 	return index.Index, index.Retrieved, nil
 }
 
-func (c *fsCache) WriteIndex(dbName string, index map[string]time.Time, retrieved time.Time) error {
+func (c *fsCache) WriteIndex(dbName string, index osv.DBIndex, retrieved time.Time) error {
 	path := filepath.Join(cacheRoot, dbName)
 	if err := os.MkdirAll(path, 0777); err != nil {
 		return err
diff --git a/client/cache_test.go b/client/cache_test.go
index bd5e7f2..9c474fe 100644
--- a/client/cache_test.go
+++ b/client/cache_test.go
@@ -38,7 +38,7 @@
 	}
 
 	now := time.Now()
-	expectedIdx := map[string]time.Time{
+	expectedIdx := osv.DBIndex{
 		"a.vuln.example.com": time.Time{}.Add(time.Hour),
 		"b.vuln.example.com": time.Time{}.Add(time.Hour * 2),
 		"c.vuln.example.com": time.Time{}.Add(time.Hour * 3),
diff --git a/client/client.go b/client/client.go
index 63694eb..c42ac2f 100644
--- a/client/client.go
+++ b/client/client.go
@@ -19,7 +19,7 @@
 
 type source interface {
 	Get([]string) ([]*osv.Entry, error)
-	Index() (map[string]time.Time, error)
+	Index() (osv.DBIndex, error)
 }
 
 type localSource struct {
@@ -44,8 +44,8 @@
 	return entries, nil
 }
 
-func (ls *localSource) Index() (map[string]time.Time, error) {
-	var index map[string]time.Time
+func (ls *localSource) Index() (osv.DBIndex, error) {
+	var index osv.DBIndex
 	b, err := os.ReadFile(filepath.Join(ls.dir, "index.json"))
 	if err != nil {
 		return nil, err
@@ -63,8 +63,8 @@
 	dbName string
 }
 
-func (hs *httpSource) Index() (map[string]time.Time, error) {
-	var cachedIndex map[string]time.Time
+func (hs *httpSource) Index() (osv.DBIndex, error) {
+	var cachedIndex osv.DBIndex
 	var cachedIndexRetrieved *time.Time
 
 	if hs.cache != nil {
@@ -104,7 +104,7 @@
 	if err != nil {
 		return nil, err
 	}
-	var index map[string]time.Time
+	var index osv.DBIndex
 	if err = json.Unmarshal(b, &index); err != nil {
 		return nil, err
 	}
diff --git a/cmd/gendb/main.go b/cmd/gendb/main.go
index f2526b0..b024817 100644
--- a/cmd/gendb/main.go
+++ b/cmd/gendb/main.go
@@ -9,18 +9,12 @@
 	"path/filepath"
 	"reflect"
 	"strings"
-	"time"
 
 	"github.com/BurntSushi/toml"
 	"golang.org/x/vulndb/osv"
 	"golang.org/x/vulndb/report"
 )
 
-type IndexEntry struct {
-	LastModified   time.Time
-	LastNewFinding time.Time
-}
-
 func fail(why string) {
 	fmt.Fprintln(os.Stderr, why)
 	os.Exit(1)
@@ -77,46 +71,26 @@
 		}
 	}
 
-	index := map[string]*IndexEntry{}
-	if content, err := ioutil.ReadFile(filepath.Join(*jsonDir, "index.json")); err == nil {
-		err = json.Unmarshal(content, &index)
-		if err != nil {
-			fail(fmt.Sprintf("failed to parse index: %s", err))
-		}
-	} else if err != nil && !os.IsNotExist(err) {
-		fail(fmt.Sprintf("failed to read index %q: %s", filepath.Join(*jsonDir, "index.json"), err))
-	}
-
-	// TODO(bracewell): I'm pretty sure the freshness stuff is basically
-	// completely broken at the moment.
-	now := time.Now()
-	for path, v := range jsonVulns {
+	index := make(osv.DBIndex, len(jsonVulns))
+	for path, vulns := range jsonVulns {
 		outPath := filepath.Join(*jsonDir, path)
-		content, err := json.Marshal(v)
+		content, err := json.Marshal(vulns)
 		if err != nil {
 			fail(fmt.Sprintf("failed to marshal json: %s", err))
 		}
-		// fmt.Println("making", filepath.Dir(outPath))
 		err = os.MkdirAll(filepath.Dir(outPath), 0700)
 		if err != nil {
 			fail(fmt.Sprintf("failed to create directory %q: %s", filepath.Dir(outPath), err))
 		}
-		// if there is already an index entry, only update the file
-		// if the set of vulns differ from what is already on disk
-		if _, ok := index[path]; ok && matchesCurrent(outPath, v) {
-			// fmt.Println("skipping", outPath)
-			continue
-		}
-		// fmt.Println("writing", outPath, string(content))
 		err = ioutil.WriteFile(outPath+".json", content, 0644)
 		if err != nil {
 			fail(fmt.Sprintf("failed to write %q: %s", outPath+".json", err))
 		}
-		if index[path] == nil {
-			index[path] = &IndexEntry{}
+		for _, v := range vulns {
+			if v.LastModified.After(index[path]) {
+				index[path] = v.LastModified
+			}
 		}
-		index[path].LastModified = now
-		// also need to set the LastNewFinding, somewhat more complicated...
 	}
 
 	indexJSON, err := json.Marshal(index)
diff --git a/osv/json.go b/osv/json.go
index acf5eb5..19c249c 100644
--- a/osv/json.go
+++ b/osv/json.go
@@ -7,6 +7,13 @@
 	"golang.org/x/vulndb/report"
 )
 
+// DBIndex contains a mapping of vulnerable packages to the
+// last time a new vulnerability was added to the database.
+// TODO: this is probably not the correct place to put this
+// type, since it's not really an OSV/CVF thing, but rather
+// vulndb implementatiion detail.
+type DBIndex map[string]time.Time
+
 type Severity int
 
 const (
@@ -166,7 +173,11 @@
 	// It would be better if this was just a recursive thing probably
 	for _, additional := range r.AdditionalPackages {
 		entryCopy := entry
-		entryCopy.Package.Name = additional.Package
+		additionalImportPath := additional.Module
+		if additional.Package != "" {
+			additionalImportPath = additional.Package
+		}
+		entryCopy.Package.Name = additionalImportPath
 		entryCopy.EcosystemSpecific.Symbols = additional.Symbols
 		entryCopy.Affects = generateAffects(additional.Versions)
 
diff --git a/report/report.go b/report/report.go
index aa39392..2b94a85 100644
--- a/report/report.go
+++ b/report/report.go
@@ -1,5 +1,7 @@
 package report
 
+import "time"
+
 type VersionRange struct {
 	Introduced string
 	Fixed      string
@@ -26,15 +28,17 @@
 		Symbols  []string
 		Versions []VersionRange
 	} `toml:"additional_packages"`
-	Versions    []VersionRange
-	Description string
-	Severity    string
-	CVE         string
-	Credit      string
-	Symbols     []string
-	OS          []string
-	Arch        []string
-	Links       struct {
+	Versions     []VersionRange
+	Description  string
+	Published    time.Time
+	LastModified time.Time `toml:"last_modified"`
+	Severity     string
+	CVE          string
+	Credit       string
+	Symbols      []string
+	OS           []string
+	Arch         []string
+	Links        struct {
 		PR      string
 		Commit  string
 		Context []string