all: initial commit
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..09b3064
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+This repository contains a handful of prototypes for the Go vulnerability database,
+as well as a initial set of vulnerability reports. Some of these packages can probably
+be coalesced, but for now are easier to work on in a more segmented fashion.
+
+* `reports` contains TOML security reports, the format is described in `format.md`
+* `report` provides a package for parsing and linting TOML reports
+* `osv` provides a package for generating OSV-style JSON vulnerability entries from a `report.Report`
+* `client` contains a client for accesing HTTP/fs based vulnerability databases, as well as a minimal caching implementation
+* `cmd/gendb` provides a tool for converting TOML reports into JSON database
+* `cmd/genhtml` provides a tool for converting TOML reports into a HTML website
+* `cmd/linter` provides a tool for linting individual reports
+* `cmd/report2cve` provides a tool for converting TOML reports into JSON CVEs
\ No newline at end of file
diff --git a/client/cache.go b/client/cache.go
new file mode 100644
index 0000000..c99a91a
--- /dev/null
+++ b/client/cache.go
@@ -0,0 +1,115 @@
+package client
+
+import (
+	"encoding/json"
+	"go/build"
+	"os"
+	"path/filepath"
+	"time"
+
+	"golang.org/x/vulndb/osv"
+)
+
+// NOTE: this cache implementation should be internal to the go tooling
+// (i.e. cmd/go/internal/something) so that the vulndb cache is owned
+// by the go command. Also it is currently NOT CONCURRENCY SAFE since
+// it does not implement file locking. When ported to the stdlib it
+// should use cmd/go/internal/lockedfile.
+
+// The cahce uses a single JSON index file for each vulnerability database
+// which contains the map from packages to the time the last
+// vulnerability for that package was added/modified and the time that
+// the index was retrieved from the vulnerability database. The JSON
+// format is as follows:
+//
+// $GOPATH/pkg/mod/cache/download/vulndb/{db hostname}/indexes/index.json
+//   {
+//       Retrieved time.Time
+//       Index map[string]time.Time
+//   }
+//
+// Each package also has a JSON file which contains the array of vulnerability
+// entries for the package. The JSON format is as follows:
+//
+// $GOPATH/pkg/mod/cache/download/vulndb/{db hostname}/{import path}/vulns.json
+//   []*osv.Entry
+
+type Cache interface {
+	ReadIndex(string) (map[string]time.Time, time.Time, error)
+	WriteIndex(string, map[string]time.Time, time.Time) error
+	ReadEntries(string, string) ([]*osv.Entry, error)
+	WriteEntries(string, string, []*osv.Entry) error
+}
+
+type fsCache struct{}
+
+// should be cfg.GOMODCACHE when doing this inside the cmd/go/internal
+var cacheRoot = filepath.Join(build.Default.GOPATH, "/pkg/mod/cache/downlaod/vulndb")
+
+type cachedIndex struct {
+	Retrieved time.Time
+	Index     map[string]time.Time
+}
+
+func (c *fsCache) ReadIndex(dbName string) (map[string]time.Time, time.Time, error) {
+	b, err := os.ReadFile(filepath.Join(cacheRoot, dbName, "index.json"))
+	if err != nil {
+		if os.IsNotExist(err) {
+			return nil, time.Time{}, nil
+		}
+		return nil, time.Time{}, err
+	}
+	var index cachedIndex
+	if err := json.Unmarshal(b, &index); err != nil {
+		return nil, time.Time{}, err
+	}
+	return index.Index, index.Retrieved, nil
+}
+
+func (c *fsCache) WriteIndex(dbName string, index map[string]time.Time, retrieved time.Time) error {
+	path := filepath.Join(cacheRoot, dbName)
+	if err := os.MkdirAll(path, 0777); err != nil {
+		return err
+	}
+	j, err := json.Marshal(cachedIndex{
+		Index:     index,
+		Retrieved: retrieved,
+	})
+	if err != nil {
+		return err
+	}
+	if err := os.WriteFile(filepath.Join(path, "index.json"), j, 0666); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (c *fsCache) ReadEntries(dbName string, p string) ([]*osv.Entry, error) {
+	b, err := os.ReadFile(filepath.Join(cacheRoot, dbName, p, "vulns.json"))
+	if err != nil {
+		if os.IsNotExist(err) {
+			return nil, nil
+		}
+		return nil, err
+	}
+	var entries []*osv.Entry
+	if err := json.Unmarshal(b, &entries); err != nil {
+		return nil, err
+	}
+	return entries, nil
+}
+
+func (c *fsCache) WriteEntries(dbName string, p string, entries []*osv.Entry) error {
+	path := filepath.Join(cacheRoot, dbName, p)
+	if err := os.MkdirAll(path, 0777); err != nil {
+		return err
+	}
+	j, err := json.Marshal(entries)
+	if err != nil {
+		return err
+	}
+	if err := os.WriteFile(filepath.Join(path, "vulns.json"), j, 0666); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/client/cache_test.go b/client/cache_test.go
new file mode 100644
index 0000000..bd5e7f2
--- /dev/null
+++ b/client/cache_test.go
@@ -0,0 +1,81 @@
+package client
+
+import (
+	"os"
+	"path/filepath"
+	"reflect"
+	"testing"
+	"time"
+
+	"golang.org/x/vulndb/osv"
+)
+
+func TestCache(t *testing.T) {
+	originalRoot := cacheRoot
+	defer func() { cacheRoot = originalRoot }()
+
+	tmp, err := os.MkdirTemp("", "vulndb-cache")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tmp)
+	cacheRoot = tmp
+
+	cache := &fsCache{}
+	dbName := "vulndb.golang.org"
+
+	_, _, err = cache.ReadIndex(dbName)
+	if err != nil {
+		t.Fatalf("ReadIndex failed for non-existent database: %v", err)
+	}
+
+	if err = os.Mkdir(filepath.Join(tmp, dbName), 0777); err != nil {
+		t.Fatalf("os.Mkdir failed: %v", err)
+	}
+	_, _, err = cache.ReadIndex(dbName)
+	if err != nil {
+		t.Fatalf("ReadIndex failed for database without cached index: %v", err)
+	}
+
+	now := time.Now()
+	expectedIdx := map[string]time.Time{
+		"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),
+	}
+	if err = cache.WriteIndex(dbName, expectedIdx, now); err != nil {
+		t.Fatalf("WriteIndex failed to write index: %v", err)
+	}
+
+	idx, retrieved, err := cache.ReadIndex(dbName)
+	if err != nil {
+		t.Fatalf("ReadIndex failed for database with cached index: %v", err)
+	}
+	if !reflect.DeepEqual(idx, expectedIdx) {
+		t.Errorf("ReadIndex returned unexpected index, got:\n%s\nwant:\n%s", idx, expectedIdx)
+	}
+	if !retrieved.Equal(now) {
+		t.Errorf("ReadIndex returned unexpected retrieved: got %s, want %s", retrieved, now)
+	}
+
+	if _, err = cache.ReadEntries(dbName, "vuln.example.com"); err != nil {
+		t.Fatalf("ReadEntires failed for non-existent package: %v", err)
+	}
+
+	expectedEntries := []*osv.Entry{
+		&osv.Entry{ID: "001"},
+		&osv.Entry{ID: "002"},
+		&osv.Entry{ID: "003"},
+	}
+	if err := cache.WriteEntries(dbName, "vuln.example.com", expectedEntries); err != nil {
+		t.Fatalf("WriteEntries failed: %v", err)
+	}
+
+	entries, err := cache.ReadEntries(dbName, "vuln.example.com")
+	if err != nil {
+		t.Fatalf("ReadEntries failed for cached package: %v", err)
+	}
+	if !reflect.DeepEqual(entries, expectedEntries) {
+		t.Errorf("ReadEntries returned unexpected entries, got:\n%v\nwant:\n%v", entries, expectedEntries)
+	}
+}
diff --git a/client/client.go b/client/client.go
new file mode 100644
index 0000000..63694eb
--- /dev/null
+++ b/client/client.go
@@ -0,0 +1,242 @@
+package client
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"net/url"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"golang.org/x/vulndb/osv"
+)
+
+type dbIndex struct{}
+
+type source interface {
+	Get([]string) ([]*osv.Entry, error)
+	Index() (map[string]time.Time, error)
+}
+
+type localSource struct {
+	dir string
+}
+
+func (ls *localSource) Get(packages []string) ([]*osv.Entry, error) {
+	var entries []*osv.Entry
+	for _, p := range packages {
+		content, err := os.ReadFile(filepath.Join(ls.dir, p+".json"))
+		if os.IsNotExist(err) {
+			continue
+		} else if err != nil {
+			return nil, err
+		}
+		var e []*osv.Entry
+		if err = json.Unmarshal(content, &e); err != nil {
+			return nil, err
+		}
+		entries = append(entries, e...)
+	}
+	return entries, nil
+}
+
+func (ls *localSource) Index() (map[string]time.Time, error) {
+	var index map[string]time.Time
+	b, err := os.ReadFile(filepath.Join(ls.dir, "index.json"))
+	if err != nil {
+		return nil, err
+	}
+	if err = json.Unmarshal(b, &index); err != nil {
+		return nil, err
+	}
+	return index, nil
+}
+
+type httpSource struct {
+	url    string
+	c      *http.Client
+	cache  Cache
+	dbName string
+}
+
+func (hs *httpSource) Index() (map[string]time.Time, error) {
+	var cachedIndex map[string]time.Time
+	var cachedIndexRetrieved *time.Time
+
+	if hs.cache != nil {
+		cachedIndex, retrieved, err := hs.cache.ReadIndex(hs.dbName)
+		if err != nil {
+			return nil, err
+		}
+
+		if cachedIndex != nil {
+			if time.Since(retrieved) < time.Hour*2 {
+				return cachedIndex, nil
+			}
+
+			cachedIndexRetrieved = &retrieved
+		}
+	}
+
+	req, err := http.NewRequest("GET", path.Join(hs.url, "index.json"), nil)
+	if err != nil {
+		return nil, err
+	}
+	if cachedIndexRetrieved != nil {
+		req.Header.Add("If-Modified-Since", cachedIndexRetrieved.Format(http.TimeFormat))
+	}
+	resp, err := hs.c.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	if cachedIndexRetrieved != nil && resp.StatusCode == http.StatusNotModified {
+		return cachedIndex, nil
+	}
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
+	}
+	b, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return nil, err
+	}
+	var index map[string]time.Time
+	if err = json.Unmarshal(b, &index); err != nil {
+		return nil, err
+	}
+
+	if hs.cache != nil {
+		if err = hs.cache.WriteIndex(hs.dbName, index, time.Now()); err != nil {
+			return nil, err
+		}
+	}
+
+	return index, nil
+}
+
+func (hs *httpSource) Get(packages []string) ([]*osv.Entry, error) {
+	var entries []*osv.Entry
+
+	index, err := hs.Index()
+	if err != nil {
+		return nil, err
+	}
+
+	var stillNeed []string
+	if hs.cache != nil {
+		for _, p := range packages {
+			lastModified, present := index[p]
+			if !present {
+				continue
+			}
+			if cached, err := hs.cache.ReadEntries(hs.dbName, p); err != nil {
+				return nil, err
+			} else if cached != nil {
+				var stale bool
+				for _, e := range entries {
+					if e.LastModified.Before(lastModified) {
+						stale = true
+						break
+					}
+				}
+				if !stale {
+					entries = append(entries, cached...)
+					continue
+				}
+			}
+			stillNeed = append(stillNeed, p)
+		}
+	} else {
+		stillNeed = packages
+	}
+
+	for _, p := range stillNeed {
+		resp, err := hs.c.Get(path.Join(hs.url, p+".json"))
+		if err != nil {
+			return nil, err
+		}
+		defer resp.Body.Close()
+		if resp.StatusCode == http.StatusNotFound {
+			continue
+		}
+		// might want this to be a LimitedReader
+		content, err := io.ReadAll(resp.Body)
+		if err != nil {
+			return nil, err
+		}
+		var e []*osv.Entry
+		if err = json.Unmarshal(content, &e); err != nil {
+			return nil, err
+		}
+		// TODO: we may want to check that the returned entries actually match
+		// the package we asked about, so that the cache cannot be poisoned
+		entries = append(entries, e...)
+
+		if hs.cache != nil {
+			if err := hs.cache.WriteEntries(hs.dbName, p, e); err != nil {
+				return nil, err
+			}
+		}
+	}
+	return nil, nil
+}
+
+type Client struct {
+	sources []source
+}
+
+type Options struct {
+	HTTPClient *http.Client
+	HTTPCache  Cache
+}
+
+func NewClient(sources []string, opts Options) (*Client, error) {
+	c := &Client{}
+	for _, uri := range sources {
+		// should parse the URI out here instead of in there
+		switch {
+		case strings.HasPrefix("http://", uri) || strings.HasPrefix("https://", uri):
+			hs := &httpSource{url: uri}
+			url, err := url.Parse(uri)
+			if err != nil {
+				return nil, err
+			}
+			hs.dbName = url.Hostname()
+			if opts.HTTPCache != nil {
+				hs.cache = opts.HTTPCache
+			}
+			if opts.HTTPClient != nil {
+				hs.c = opts.HTTPClient
+			} else {
+				hs.c = new(http.Client)
+			}
+			c.sources = append(c.sources, hs)
+		case strings.HasPrefix("file://", uri):
+			url, err := url.Parse(uri)
+			if err != nil {
+				return nil, err
+			}
+			c.sources = append(c.sources, &localSource{dir: url.Path})
+		default:
+			return nil, fmt.Errorf("source %q has unsupported scheme", uri)
+		}
+	}
+	return c, nil
+}
+
+func (c *Client) Get(packages []string) ([]*osv.Entry, error) {
+	var entries []*osv.Entry
+	// probably should be parallelized
+	for _, s := range c.sources {
+		e, err := s.Get(packages)
+		if err != nil {
+			return nil, err // be failure tolerant?
+		}
+		entries = append(entries, e...)
+	}
+	return entries, nil
+}
diff --git a/cmd/gendb/main.go b/cmd/gendb/main.go
new file mode 100644
index 0000000..f2526b0
--- /dev/null
+++ b/cmd/gendb/main.go
@@ -0,0 +1,130 @@
+package main
+
+import (
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"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)
+}
+
+// TODO: obviously not for the real world
+const dbURL = "https://team.git.corp.google.com/golang/vulndb/+/refs/heads/main/reports/"
+
+func matchesCurrent(path string, new []osv.Entry) bool {
+	var current []osv.Entry
+	content, err := ioutil.ReadFile(path + ".json")
+	if err != nil {
+		fmt.Println("bad", err)
+		return false
+	}
+	if err = json.Unmarshal(content, &current); err != nil {
+		return false
+	}
+	return reflect.DeepEqual(current, new)
+}
+
+func main() {
+	tomlDir := flag.String("reports", "Directory containing toml reports", "")
+	jsonDir := flag.String("out", "Directory to write JSON database to", "")
+	flag.Parse()
+
+	tomlFiles, err := ioutil.ReadDir(*tomlDir)
+	if err != nil {
+		fail(fmt.Sprintf("can't read %q: %s", *tomlDir, err))
+	}
+
+	jsonVulns := map[string][]osv.Entry{}
+	for _, f := range tomlFiles {
+		if !strings.HasSuffix(f.Name(), ".toml") {
+			continue
+		}
+		content, err := ioutil.ReadFile(filepath.Join(*tomlDir, f.Name()))
+		if err != nil {
+			fail(fmt.Sprintf("can't read %q: %s", f.Name(), err))
+		}
+		var vuln report.Report
+		err = toml.Unmarshal(content, &vuln)
+		if err != nil {
+			fail(fmt.Sprintf("unable to unmarshal %q: %s", f.Name(), err))
+		}
+		if err = vuln.Lint(); err != nil {
+			fail(fmt.Sprintf("invalid vulnerability %q: %s", f.Name(), err))
+		}
+
+		name := strings.TrimSuffix(filepath.Base(f.Name()), filepath.Ext(f.Name()))
+
+		for _, e := range osv.Generate(name, fmt.Sprintf("%s%s.toml", dbURL, name), vuln) {
+			jsonVulns[e.Package.Name] = append(jsonVulns[e.Package.Name], e)
+		}
+	}
+
+	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 {
+		outPath := filepath.Join(*jsonDir, path)
+		content, err := json.Marshal(v)
+		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{}
+		}
+		index[path].LastModified = now
+		// also need to set the LastNewFinding, somewhat more complicated...
+	}
+
+	indexJSON, err := json.Marshal(index)
+	if err != nil {
+		fail(fmt.Sprintf("failed to marshal index json: %s", err))
+	}
+	err = ioutil.WriteFile(filepath.Join(*jsonDir, "index.json"), indexJSON, 0644)
+	if err != nil {
+		fail(fmt.Sprintf("failed to write index: %s", err))
+	}
+}
diff --git a/cmd/gendb/main_test.go b/cmd/gendb/main_test.go
new file mode 100644
index 0000000..06ab7d0
--- /dev/null
+++ b/cmd/gendb/main_test.go
@@ -0,0 +1 @@
+package main
diff --git a/cmd/genhtml/main.go b/cmd/genhtml/main.go
new file mode 100644
index 0000000..3c8f9aa
--- /dev/null
+++ b/cmd/genhtml/main.go
@@ -0,0 +1,207 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"html/template"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+
+	"github.com/BurntSushi/toml"
+	"golang.org/x/vulndb/report"
+)
+
+var indexTemplate = template.Must(template.New("index").Parse(`<html>
+<body>
+	<h1>Go Vulnerability Database</h1>
+	<ul>
+		{{range .Vulns}}<li><a href="{{.}}.html">{{.}}</a></li>{{end}}
+	</ul>
+	<h2>Packages</h2>
+	<ul>
+		{{range .Packages}}<li><a href="{{.}}.html">{{.}}</a></li>{{end}}
+	</ul>
+</body>
+</html>`))
+var packageIndexTemplate = template.Must(template.New("package-index").Parse(`<html>
+<body>
+	<h1>{{.Name}} Vulnerabilities</h1>
+	<ul>
+		{{range .Vulns}}<li><a href="{{.}}.html">{{.}}</a></li>{{end}}
+	</ul>
+</body>
+</html>`))
+var vulnTemplate = template.Must(template.New("vuln").Parse(`<html>
+<body>
+    <h1>{{.Name}}</h1>
+    {{if .Vuln.Severity}}<p><b>Severity: </b>{{.Vuln.Severity}}</p>{{end}}
+    {{if .Vuln.OS}}<p><b>Affected Operating Systems: </b>{{.Vuln.OS}}</p>{{end}}
+    {{if .Vuln.Arch}}<p><b>Affected Architectures: </b>{{.Vuln.Arch}}</p>{{end}}
+	<p>{{.Vuln.Description}}</p>
+	{{if .Vuln.Credit}}<p><b>Credit: </b>{{.Vuln.Credit}}</p>{{end}}
+	{{if .Vuln.CVE}}<p><b>CVE: </b>{{.Vuln.CVE}}</p>{{end}}
+
+    <h2>Affected Packages</h2>
+    <table>
+        <tr>
+            <th>Package</th>
+            <th>Introduced</th>
+            <th>Fixed</th>
+            <th>Symbols</th>
+        </tr>
+        <tr>
+            <td><code>{{.Vuln.Package}}</code></td>
+            {{if not .Vuln.Versions}}<td colspan="2" style="text-align: center">All available versions are vulnerable</td>{{else}}
+            {{range .Vuln.Versions}}
+            <td style="text-align: center">{{.Introduced}}</td>
+            <td style="text-align: center">{{.Fixed}}</td>
+            {{end}}
+            {{end}}
+            <td>
+                <ul>
+                    {{range .Vuln.Symbols}}<li><code>{{.}}</code></li>{{end}}
+                </ul>
+            </td>
+        </tr>
+        {{range .Vuln.AdditionalPackages}}
+        <tr>
+            <td><code>{{.Package}}</code></td>
+            {{if not .Versions}}<td colspan="2" style="text-align: center">All available versions are vulnerable</td>{{else}}
+            {{range .Versions}}
+            <td style="text-align: center">{{.Introduced}}</td>
+            <td style="text-align: center">{{.Fixed}}</td>
+            {{end}}
+            {{end}}
+            <td>
+                <ul>
+                    {{range .Symbols}}<li><code>{{.}}</code></li>{{end}}
+                </ul>
+            </td>
+        </tr>
+        {{end}}
+    </table>
+
+	<h2>Context</h2>
+	{{if .Vuln.Links.Commit}}<p><b>Commit: </b><a href="{{.Vuln.Links.Commit}}">{{.Vuln.Links.Commit}}</a></p>{{end}}
+	{{if .Vuln.Links.PR}}<p><b>PR: </b><a href="{{.Vuln.Links.PR}}">{{.Vuln.Links.PR}}</a></p>{{end}}
+	{{if .Vuln.Links.Context}}<p><b>Additional links:</b><ul>{{range .Vuln.Links.Context}}<li><a href="{{.}}">{{.}}</a></li>{{end}}</ul></p>{{end}}
+</body>
+</html>`))
+
+func generateWebsite(vulns map[string]report.Report, htmlDir string) error {
+	index := map[string][]string{}
+	var vulnNames []string
+	for name, vuln := range vulns {
+		index[vuln.Package] = append(index[vuln.Package], name)
+		for _, additional := range vuln.AdditionalPackages {
+			index[additional.Package] = append(index[additional.Package], name)
+		}
+		vulnNames = append(vulnNames, name)
+
+		filename := filepath.Join(htmlDir, name+".html")
+		file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
+		if err != nil {
+			return err
+		}
+		defer file.Close()
+		err = vulnTemplate.Execute(file, struct {
+			Name string
+			Vuln report.Report
+		}{
+			Name: name,
+			Vuln: vuln,
+		})
+		if err != nil {
+			return err
+		}
+	}
+
+	for p, vulns := range index {
+		filename := filepath.Join(htmlDir, p+".html")
+		if err := os.MkdirAll(strings.TrimSuffix(filename, filepath.Base(filename)), 0755); err != nil {
+			return err
+		}
+		file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
+		if err != nil {
+			return err
+		}
+		defer file.Close()
+		err = packageIndexTemplate.Execute(file, struct {
+			Name  string
+			Vulns []string
+		}{
+			Name:  p,
+			Vulns: vulns,
+		})
+		if err != nil {
+			return err
+		}
+	}
+
+	var packageNames []string
+	for name := range index {
+		packageNames = append(packageNames, name)
+	}
+
+	sort.Strings(packageNames)
+	sort.Strings(vulnNames)
+	file, err := os.OpenFile(filepath.Join(htmlDir, "index.html"), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+	err = indexTemplate.Execute(file, struct {
+		Vulns    []string
+		Packages []string
+	}{
+		Vulns:    vulnNames,
+		Packages: packageNames,
+	})
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func fail(why string) {
+	fmt.Fprintln(os.Stderr, why)
+	os.Exit(1)
+}
+
+func main() {
+	tomlDir := flag.String("reports", "Directory containing toml reports", "")
+	htmlDir := flag.String("out", "Directory to write website to", "")
+	flag.Parse()
+
+	htmlVulns := map[string]report.Report{}
+	tomlFiles, err := ioutil.ReadDir(*tomlDir)
+	if err != nil {
+		fail(fmt.Sprintf("can't read %q: %s", *tomlDir, err))
+	}
+	for _, f := range tomlFiles {
+		if !strings.HasSuffix(f.Name(), ".toml") {
+			continue
+		}
+		content, err := ioutil.ReadFile(f.Name())
+		if err != nil {
+			fail(fmt.Sprintf("can't read %q: %s", f.Name(), err))
+		}
+		var vuln report.Report
+		err = toml.Unmarshal(content, &vuln)
+		if err != nil {
+			fail(fmt.Sprintf("unable to unmarshal %q: %s", f.Name(), err))
+		}
+		if err = vuln.Lint(); err != nil {
+			fail(fmt.Sprintf("invalid vulnerability %q: %s", f.Name(), err))
+		}
+		name := strings.TrimSuffix(filepath.Base(f.Name()), filepath.Ext(f.Name()))
+		htmlVulns[name] = vuln
+	}
+	err = generateWebsite(htmlVulns, *htmlDir)
+	if err != nil {
+		fail(fmt.Sprintf("failed to generate website: %s", err))
+	}
+}
diff --git a/cmd/linter/main.go b/cmd/linter/main.go
new file mode 100644
index 0000000..f893e70
--- /dev/null
+++ b/cmd/linter/main.go
@@ -0,0 +1,36 @@
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	"golang.org/x/vulndb/report"
+
+	"github.com/BurntSushi/toml"
+)
+
+func main() {
+	if len(os.Args) != 2 {
+		fmt.Fprintln(os.Stderr, "only expect a single argument")
+		os.Exit(1)
+	}
+
+	content, err := ioutil.ReadFile(os.Args[1])
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "unable to read %q: %s\n", os.Args[1], err)
+		os.Exit(1)
+	}
+
+	var vuln report.Report
+	err = toml.Unmarshal(content, &vuln)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "unable to parse %q: %s\n", os.Args[1], err)
+		os.Exit(1)
+	}
+
+	if err = vuln.Lint(); err != nil {
+		fmt.Fprintf(os.Stderr, "invalid vulnerability file %q: %s\n", os.Args[1], err)
+		os.Exit(1)
+	}
+}
diff --git a/cmd/report2cve/main.go b/cmd/report2cve/main.go
new file mode 100644
index 0000000..3eca63c
--- /dev/null
+++ b/cmd/report2cve/main.go
@@ -0,0 +1,243 @@
+package main
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/BurntSushi/toml"
+	"golang.org/x/vulndb/report"
+)
+
+// Affects
+type Affects struct {
+	Vendor Vendor `json:"vendor"`
+}
+
+// CVEDataMeta
+type CVEDataMeta struct {
+	ASSIGNER string `json:"ASSIGNER"`
+	ID       string `json:"ID"`
+	STATE    string `json:"STATE"`
+}
+
+// Description
+type Description struct {
+	DescriptionData []LangString `json:"description_data"`
+}
+
+// LangString
+type LangString struct {
+	Lang  string `json:"lang"`
+	Value string `json:"value"`
+}
+
+// Problemtype
+type Problemtype struct {
+	ProblemtypeData []ProblemtypeDataItems `json:"problemtype_data"`
+}
+
+// ProblemtypeDataItems
+type ProblemtypeDataItems struct {
+	Description []LangString `json:"description"`
+}
+
+type VersionData struct {
+	VersionData []VersionDataItems `json:"version_data"`
+}
+
+type ProductDataItem struct {
+	ProductName string      `json:"product_name"`
+	Version     VersionData `json:"version"`
+}
+
+// Product
+type Product struct {
+	ProductData []ProductDataItem `json:"product_data"`
+}
+
+// Reference
+type Reference struct {
+	URL string `json:"url"`
+}
+
+// References
+type References struct {
+	ReferenceData []Reference `json:"reference_data"`
+}
+
+// Vendor
+type Vendor struct {
+	VendorData []VendorDataItems `json:"vendor_data"`
+}
+
+// VendorDataItems
+type VendorDataItems struct {
+	Product    Product `json:"product"`
+	VendorName string  `json:"vendor_name"`
+}
+
+// VersionDataItems
+type VersionDataItems struct {
+	VersionValue    string `json:"version_value"`
+	VersionAffected string `json:"version_affected"`
+}
+
+// CVE
+type CVE struct {
+	DataType    string      `json:"data_type"`
+	DataFormat  string      `json:"data_format"`
+	DataVersion string      `json:"data_version"`
+	CVEDataMeta CVEDataMeta `json:"CVE_data_meta"`
+
+	Affects     Affects     `json:"affects"`
+	Description Description `json:"description"`
+	Problemtype Problemtype `json:"problemtype"`
+	References  References  `json:"references"`
+}
+
+func FromReport(report *report.Report) (*CVE, error) {
+	if report.CVE != "" {
+		return nil, errors.New("report has CVE ID is wrong section (should be in cve_metadata for self-issued CVEs)")
+	}
+	if report.CVEMetadata == nil {
+		return nil, errors.New("report missing cve_metadata section")
+	}
+	if report.CVEMetadata.ID == "" {
+		return nil, errors.New("report missing CVE ID")
+	}
+
+	cve := &CVE{
+		DataType:    "CVE",
+		DataFormat:  "MITRE",
+		DataVersion: "4.0",
+		CVEDataMeta: CVEDataMeta{
+			ID:       report.CVEMetadata.ID,
+			ASSIGNER: "security@golang.org",
+			STATE:    "PUBLIC",
+		},
+
+		Description: Description{
+			DescriptionData: []LangString{
+				{
+					Lang:  "eng",
+					Value: strings.TrimSuffix(report.CVEMetadata.Description, "\n"),
+				},
+			},
+		},
+
+		Problemtype: Problemtype{
+			ProblemtypeData: []ProblemtypeDataItems{
+				{
+					Description: []LangString{
+						{
+							Lang:  "eng",
+							Value: report.CVEMetadata.CWE,
+						},
+					},
+				},
+			},
+		},
+
+		Affects: Affects{
+			Vendor: Vendor{
+				VendorData: []VendorDataItems{
+					{
+						VendorName: "n/a", // ???
+						Product: Product{
+							ProductData: []ProductDataItem{
+								{
+									ProductName: report.Package,
+									Version:     versionToVersion(report.Versions),
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	for _, additional := range report.AdditionalPackages {
+		cve.Affects.Vendor.VendorData = append(cve.Affects.Vendor.VendorData, VendorDataItems{
+			VendorName: "n/a",
+			Product: Product{
+				ProductData: []ProductDataItem{
+					{
+						ProductName: additional.Package,
+						Version:     versionToVersion(additional.Versions),
+					},
+				},
+			},
+		})
+	}
+
+	if report.Links.PR != "" {
+		cve.References.ReferenceData = append(cve.References.ReferenceData, Reference{URL: report.Links.PR})
+	}
+	if report.Links.Commit != "" {
+		cve.References.ReferenceData = append(cve.References.ReferenceData, Reference{URL: report.Links.Commit})
+	}
+	for _, url := range report.Links.Context {
+		cve.References.ReferenceData = append(cve.References.ReferenceData, Reference{URL: url})
+	}
+
+	return cve, nil
+}
+
+func versionToVersion(versions []report.VersionRange) VersionData {
+	vd := VersionData{}
+	for _, vr := range versions {
+		if vr.Introduced != "" {
+			vd.VersionData = append(vd.VersionData, VersionDataItems{
+				VersionValue:    vr.Introduced,
+				VersionAffected: ">=",
+			})
+		}
+		if vr.Fixed != "" {
+			vd.VersionData = append(vd.VersionData, VersionDataItems{
+				VersionValue:    vr.Fixed,
+				VersionAffected: "<",
+			})
+		}
+	}
+	return vd
+}
+
+func main() {
+	if len(os.Args) != 2 {
+		fmt.Fprint(os.Stderr, "usage: report2cve report.toml")
+		os.Exit(1)
+	}
+
+	reportPath := os.Args[1]
+	b, err := os.ReadFile(reportPath)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "failed to read %q: %s\n", reportPath, err)
+		os.Exit(1)
+	}
+
+	var r report.Report
+	if err = toml.Unmarshal(b, &r); err != nil {
+		fmt.Fprintf(os.Stderr, "failed to parse %q: %s\n", reportPath, err)
+		os.Exit(1)
+	}
+
+	cve, err := FromReport(&r)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "failed to generate CVE: %s\n", err)
+		os.Exit(1)
+	}
+
+	// We need to use an encoder so that it doesn't escape angle
+	// brackets.
+	e := json.NewEncoder(os.Stdout)
+	e.SetEscapeHTML(false)
+	e.SetIndent("", "\t")
+	if err = e.Encode(cve); err != nil {
+		fmt.Fprintf(os.Stderr, "failed to marshal CVE: %s\n", err)
+		os.Exit(1)
+	}
+}
diff --git a/format.md b/format.md
new file mode 100644
index 0000000..70eb6d3
--- /dev/null
+++ b/format.md
@@ -0,0 +1,128 @@
+The main section of the TOML contains high level information about the vulnerability
+
+```
+package = "github.com/example/module"
+```
+**required** `package` contains the import path of the vulnerable module.
+
+```
+description = """
+A remote attacker can craft a message which causes a panic via nil pointer dereference if the field
+[`Config.Parser`] is not initialized.
+"""
+```
+**required** `description` contains a textual description of the vulnerability and its impact. This field can contain a subset of markdown markup, used to link to godoc documentation for methods/types in the vulnerable module (and/or other modules).
+
+```
+cve = "CVE-000-000"
+```
+**optional** `cve` contains a CVE number for the vulnerability, if one has been assigned.
+
+```
+credit = "A. Reporter"
+```
+**optional** `credit` contains credit for the person/organization that discovered/reported the vulnerability.
+
+```
+symbols = ["Parser.Parse"]
+```
+**optional** `symbols` contains an array of vulnerable symbols. If included only programs which use these symbols will be marked as vulnerable. If omitted any program which imports this module will be marked vulnerable.
+
+```
+[[versions]]
+```
+The `versions` sections of the TOML contain information about when the vulnerability was introduced, and when it was fixed. If the vulnerability is fixed in multiple major versions, then the TOML should contain multiple `versions` sections. If omitted it is assumed that _every_ version of the module is vulnerable.
+
+```
+introduced = "v0.0.1"
+```
+**optional** `introduced` contains the pseudo-version at which the vulnerability was introduced. If this field is omitted it is assumed that every version, from the initial commit, up to the `fixed` version is vulnerable.
+
+```
+fixed = "v4.0.0-20190408214815-ec0a89a131e3"
+```
+**optional** `fixed` contains the pseudo-version at which the vulnerability was fixed. If this field is omitted it is assumed that every version since the `introduced` version is vulnerable.
+
+```
+[[additional_packages]]
+```
+The `additional_packages` sections of the TOML contain information about additional packages impacted by the vulnerability. These may be other submodules which independently implement the same vulnerability, or alternate module names for the same module.
+
+```
+package = "gopkg.in/vuln-mod"
+```
+**optional** `package` contains the import path of the additionally vulnerable module.
+
+```
+symbols = ["Parser.Parse"]
+```
+**optional** `symbols` contains an array of vulnerable symbols. If included only programs which use these symbols will be marked as vulnerable. If omitted any program which imports this module will be marked vulnerable.
+
+```
+[[additional_packages.versions]]
+```
+The `additional_packages.versions` sections contain version ranges for each additional package, following the same semantics as the `versions` section.
+
+```
+[links]
+```
+The `links` section of the TOML contains further information about the vulnerability.
+
+```
+commit = "https://github.com/example/module/commit/abcd"
+```
+**optional*** `commit` contains a link to the commit which fixes the vulnerability.
+
+```
+pr = "https://github.com/example/module/pulls/123"
+```
+**optional** `pr` contains a link to the PR/CL which fixes the vulnerability.
+
+```
+context = ["https://github.com/example/module/issues/50"]
+```
+**optional** `context` contains an array of additional links which provide additional context about the vulnerability, i.e. GitHub issues, vulnerability reports, etc.
+
+# Example
+
+```
+package = "github.com/example/module"
+description = """A description of the vulnerability present in this module.
+
+The description can contain newlines, and a limited set of markup.
+"""
+cve = "CVE-2021-3185"
+credit = "John Smith"
+symbols = ["Type.MethodA", "MethodB"]
+
+[[versions]]
+# The vulnerability is present in all versions up to
+# version v0.2.0
+fixed = "v0.2.0"
+
+[[versions]]
+# The vulnerability is present in all versions since
+# version v0.2.5
+introduced = "v0.2.5
+
+[[additional_packages]]
+# Major versions must be explicitly specified
+package = "github.com/example/module/v2"
+symbols = ["MethodB"]
+[[additional_packages.versions]]
+fixed = "v2.5.0"
+
+[[additional_packages]]
+package = "github.com/example/module/v3"
+symbols = ["MethodB"]
+[[additional_packages.versions]]
+introduced = "v3.0.1"
+
+[links]
+commit = "https://github.com/example/module/commit/aabbccdd"
+pr = "https://github.com/example/module/pull/10"
+context = [
+    "https://www.openwall.com/lists/oss-security/2016/11/03/1",
+    "https://github.com/example/module/advisories/1"
+]
+```
\ No newline at end of file
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..f068a01
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,9 @@
+module golang.org/x/vulndb
+
+go 1.16
+
+require (
+	github.com/BurntSushi/toml v0.3.1
+	github.com/google/go-cmp v0.5.4
+	golang.org/x/mod v0.4.1
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..5269ef6
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,19 @@
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
+github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=
+golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/new-vuln.sh b/new-vuln.sh
new file mode 100644
index 0000000..4cd8c7d
--- /dev/null
+++ b/new-vuln.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+prev=$(find reports/GO-* | tail -n 1 | sed -n 's/reports\/GO-[0-9]*-\([0-9]*\).toml/\1/p')
+new=$(printf "%04d" $(expr $prev + 1))
+year=$(date +"%Y")
+cp template reports/GO-$year-$new.toml
diff --git a/osv/json.go b/osv/json.go
new file mode 100644
index 0000000..d9fa66e
--- /dev/null
+++ b/osv/json.go
@@ -0,0 +1,173 @@
+package osv
+
+import (
+	"time"
+
+	"golang.org/x/mod/semver"
+	"golang.org/x/vulndb/report"
+)
+
+type Severity int
+
+const (
+	SevNone Severity = iota
+	SevLow
+	SevMedium
+	SevHigh
+	SevCritical
+)
+
+var strToSev = map[string]Severity{
+	// "": SevNone,
+	"low":      SevLow,
+	"medium":   SevMedium,
+	"high":     SevHigh,
+	"critical": SevCritical,
+}
+
+type Type int
+
+const (
+	TypeUnspecified Type = iota
+	TypeGit
+	TypeSemver
+)
+
+type Ecosystem string
+
+const GoEcosystem Ecosystem = "go"
+
+type Package struct {
+	Name      string
+	Ecosystem Ecosystem
+}
+
+type AffectsRange struct {
+	Type       Type
+	Introduced string
+	Fixed      string
+}
+
+func (ar AffectsRange) containsSemver(v string) bool {
+	if ar.Type != TypeSemver {
+		return false
+	}
+
+	return (ar.Introduced == "" || semver.Compare(v, ar.Introduced) >= 0) &&
+		(ar.Fixed == "" || semver.Compare(v, ar.Fixed) < 0)
+}
+
+type Affects struct {
+	Ranges []AffectsRange
+}
+
+func generateAffects(versions []report.VersionRange) Affects {
+	a := Affects{}
+	for _, v := range versions {
+		a.Ranges = append(a.Ranges, AffectsRange{
+			Type:       TypeSemver,
+			Introduced: v.Introduced,
+			Fixed:      v.Fixed,
+		})
+	}
+	return a
+}
+
+func (a Affects) AffectsSemver(v string) bool {
+	if len(a.Ranges) == 0 {
+		// No ranges implies all versions are affected
+		return true
+	}
+	var semverRangePresent bool
+	for _, r := range a.Ranges {
+		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 GoSpecific struct {
+	Symbols []string `json:",omitempty"`
+	GOOS    []string `json:",omitempty"`
+	GOARCH  []string `json:",omitempty"`
+	URL     string
+}
+
+// Entry represents a OSV style JSON vulnerability database
+// entry
+type Entry struct {
+	ID                string
+	Package           Package
+	Summary           string
+	Details           string
+	Severity          Severity
+	Affects           Affects
+	ReferenceURLs     []string   `json:"reference_urls,omitempty"`
+	Aliases           []string   `json:",omitempty"`
+	EcosystemSpecific GoSpecific `json:"ecosystem_specific,omitempty"`
+	LastModified      time.Time  `json:"last_modified"`
+}
+
+func Generate(id string, url string, r report.Report) []Entry {
+	entry := Entry{
+		ID: id,
+		Package: Package{
+			Name:      r.Package,
+			Ecosystem: GoEcosystem,
+		},
+		Summary:      "", // TODO: think if we want to populate this in reports
+		Details:      r.Description,
+		Affects:      generateAffects(r.Versions),
+		LastModified: time.Now(),
+		EcosystemSpecific: GoSpecific{
+			Symbols: r.Symbols,
+			GOOS:    r.OS,
+			GOARCH:  r.Arch,
+			URL:     url,
+		},
+	}
+
+	if r.Severity != "" {
+		entry.Severity = strToSev[r.Severity]
+	} else {
+		// Default to medium or none?
+		entry.Severity = SevMedium
+	}
+
+	if r.Links.PR != "" {
+		entry.ReferenceURLs = append(entry.ReferenceURLs, r.Links.PR)
+	}
+	if r.Links.Commit != "" {
+		entry.ReferenceURLs = append(entry.ReferenceURLs, r.Links.Commit)
+	}
+	if r.Links.Context != nil {
+		entry.ReferenceURLs = append(entry.ReferenceURLs, r.Links.Context...)
+	}
+
+	if r.CVE != "" {
+		entry.Aliases = []string{r.CVE}
+	}
+
+	entries := []Entry{entry}
+
+	// It would be better if this was just a recursive thing probably
+	for _, additional := range r.AdditionalPackages {
+		entryCopy := entry
+		entryCopy.Package.Name = additional.Package
+		entryCopy.EcosystemSpecific.Symbols = additional.Symbols
+		entryCopy.Affects = generateAffects(additional.Versions)
+
+		entries = append(entries, entryCopy)
+	}
+
+	return entries
+}
diff --git a/osv/json_test.go b/osv/json_test.go
new file mode 100644
index 0000000..650964f
--- /dev/null
+++ b/osv/json_test.go
@@ -0,0 +1,262 @@
+package osv
+
+import (
+	"testing"
+	"time"
+
+	"github.com/google/go-cmp/cmp"
+	"golang.org/x/vulndb/report"
+)
+
+func TestGenerate(t *testing.T) {
+	r := report.Report{
+		Package: "example.com/vulnerable/v2",
+		AdditionalPackages: []struct {
+			Package  string
+			Symbols  []string
+			Versions []report.VersionRange
+		}{
+			{
+				Package: "example.com/vulnerable",
+				Symbols: []string{"b", "A.b"},
+				Versions: []report.VersionRange{
+					{Fixed: "v2.1.1"},
+					{Introduced: "v2.3.4", Fixed: "v2.3.5"},
+					{Introduced: "v2.5.0"},
+				},
+			},
+		},
+		Versions: []report.VersionRange{
+			{Fixed: "v2.1.1"},
+			{Introduced: "v2.3.4", Fixed: "v2.3.5"},
+			{Introduced: "v2.5.0"},
+		},
+		Description: "It's a real bad one, I'll tell you that",
+		Severity:    "medium",
+		CVE:         "CVE-0000-0000",
+		Credit:      "ignored",
+		Symbols:     []string{"A", "B.b"},
+		OS:          []string{"windows"},
+		Arch:        []string{"arm64"},
+		Links: struct {
+			PR      string
+			Commit  string
+			Context []string
+		}{
+			PR:      "pr",
+			Commit:  "commit",
+			Context: []string{"issue-a", "issue-b"},
+		},
+	}
+
+	want := []Entry{
+		{
+			ID: "GO-1991-0001",
+			Package: Package{
+				Name:      "example.com/vulnerable/v2",
+				Ecosystem: "go",
+			},
+			Details:  "It's a real bad one, I'll tell you that",
+			Severity: 2,
+			Affects: Affects{
+				Ranges: []AffectsRange{
+					{
+						Type:  TypeSemver,
+						Fixed: "v2.1.1",
+					},
+					{
+						Type:       TypeSemver,
+						Introduced: "v2.3.4",
+						Fixed:      "v2.3.5",
+					},
+					{
+						Type:       TypeSemver,
+						Introduced: "v2.5.0",
+					},
+				},
+			},
+			ReferenceURLs: []string{
+				"pr",
+				"commit",
+				"issue-a",
+				"issue-b",
+			},
+			Aliases: []string{"CVE-0000-0000"},
+			EcosystemSpecific: GoSpecific{
+				Symbols: []string{"A", "B.b"},
+				GOOS:    []string{"windows"},
+				GOARCH:  []string{"arm64"},
+				URL:     "https://vulns.golang.org/GO-1991-0001.html",
+			},
+		},
+		{
+
+			ID: "GO-1991-0001",
+			Package: Package{
+				Name:      "example.com/vulnerable",
+				Ecosystem: "go",
+			},
+			Details:  "It's a real bad one, I'll tell you that",
+			Severity: 2,
+			Affects: Affects{
+				Ranges: []AffectsRange{
+					{
+						Type:  TypeSemver,
+						Fixed: "v2.1.1",
+					},
+					{
+						Type:       TypeSemver,
+						Introduced: "v2.3.4",
+						Fixed:      "v2.3.5",
+					},
+					{
+						Type:       TypeSemver,
+						Introduced: "v2.5.0",
+					},
+				},
+			},
+			ReferenceURLs: []string{
+				"pr",
+				"commit",
+				"issue-a",
+				"issue-b",
+			},
+			Aliases: []string{"CVE-0000-0000"},
+			EcosystemSpecific: GoSpecific{
+				Symbols: []string{"b", "A.b"},
+				GOOS:    []string{"windows"},
+				GOARCH:  []string{"arm64"},
+				URL:     "https://vulns.golang.org/GO-1991-0001.html",
+			},
+		},
+	}
+	got := Generate("GO-1991-0001", "https://vulns.golang.org/GO-1991-0001.html", r)
+	if diff := cmp.Diff(want, got, cmp.Comparer(func(_, _ time.Time) bool { return true })); diff != "" {
+		t.Errorf("Generate returned unexpected result (-want +got):\n%s", diff)
+	}
+}
+
+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,
+		},
+		{
+			// v1.0.0 < v2.0.0
+			affects: Affects{
+				Ranges: []AffectsRange{
+					{Type: TypeSemver, Fixed: "v2.0.0"},
+				},
+			},
+			version: "v1.0.0",
+			want:    true,
+		},
+		{
+			// v0.0.1 <= v1.0.0
+			affects: Affects{
+				Ranges: []AffectsRange{
+					{Type: TypeSemver, Introduced: "v0.0.1"},
+				},
+			},
+			version: "v1.0.0",
+			want:    true,
+		},
+		{
+			// v1.0.0 <= v1.0.0
+			affects: Affects{
+				Ranges: []AffectsRange{
+					{Type: TypeSemver, Introduced: "v1.0.0"},
+				},
+			},
+			version: "v1.0.0",
+			want:    true,
+		},
+		{
+			// v1.0.0 <= v1.0.0 < v2.0.0
+			affects: Affects{
+				Ranges: []AffectsRange{
+					{Type: TypeSemver, Introduced: "v1.0.0", Fixed: "v2.0.0"},
+				},
+			},
+			version: "v1.0.0",
+			want:    true,
+		},
+		{
+			// v0.0.1 <= v1.0.0 < v2.0.0
+			affects: Affects{
+				Ranges: []AffectsRange{
+					{Type: TypeSemver, Introduced: "v0.0.1", Fixed: "v2.0.0"},
+				},
+			},
+			version: "v1.0.0",
+			want:    true,
+		},
+		{
+			// v2.0.0 < v3.0.0
+			affects: Affects{
+				Ranges: []AffectsRange{
+					{Type: TypeSemver, Introduced: "v1.0.0", Fixed: "v2.0.0"},
+				},
+			},
+			version: "v3.0.0",
+			want:    false,
+		},
+		{
+			// Multiple ranges
+			affects: Affects{
+				Ranges: []AffectsRange{
+					{Type: TypeSemver, Introduced: "v1.0.0", Fixed: "v2.0.0"},
+					{Type: TypeSemver, Introduced: "v3.0.0"},
+				},
+			},
+			version: "v3.0.0",
+			want:    true,
+		},
+		{
+			// Wrong type range
+			affects: Affects{
+				Ranges: []AffectsRange{
+					{Type: TypeUnspecified, Introduced: "v3.0.0"},
+				},
+			},
+			version: "v3.0.0",
+			want:    true,
+		},
+		{
+			// Semver ranges don't match
+			affects: Affects{
+				Ranges: []AffectsRange{
+					{Type: TypeUnspecified, Introduced: "v3.0.0"},
+					{Type: TypeSemver, Introduced: "v4.0.0"},
+				},
+			},
+			version: "v3.0.0",
+			want:    false,
+		},
+		{
+			// Semver ranges do match
+			affects: Affects{
+				Ranges: []AffectsRange{
+					{Type: TypeUnspecified, Introduced: "v3.0.0"},
+					{Type: TypeSemver, Introduced: "v3.0.0"},
+				},
+			},
+			version: "v3.0.0",
+			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)
+		}
+	}
+}
diff --git a/report/report.go b/report/report.go
new file mode 100644
index 0000000..6c52a0c
--- /dev/null
+++ b/report/report.go
@@ -0,0 +1,118 @@
+package report
+
+import (
+	"errors"
+	"fmt"
+	"regexp"
+
+	"golang.org/x/mod/module"
+	"golang.org/x/mod/semver"
+)
+
+type VersionRange struct {
+	Introduced string
+	Fixed      string
+}
+
+type Report struct {
+	Package string
+	// TODO: could also be GoToolchain, but we might want
+	// this for other things?
+	//
+	// could we also automate this by just looking for
+	// things prefixed with cmd/go?
+	DoNotExport bool `json:"do_not_export"`
+	// TODO: how does this interact with Versions etc?
+	Stdlib bool `json:"stdlib"`
+	// TODO: the most common usage of additional package should
+	// really be replaced with 'aliases', we'll still need
+	// additional packages for some cases, but it's too heavy
+	// for most
+	AdditionalPackages []struct {
+		Package  string
+		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 {
+		PR      string
+		Commit  string
+		Context []string
+	}
+	CVEMetadata *struct {
+		ID          string
+		CWE         string
+		Description string
+	} `toml:"cve_metadata"`
+}
+
+var cveRegex = regexp.MustCompile(`^CVE-\d{4}-\d{4,}$`)
+
+func (vuln *Report) Lint() error {
+	if vuln.Package == "" {
+		return errors.New("missing package")
+	}
+	if err := module.CheckImportPath(vuln.Package); err != nil {
+		return err
+	}
+
+	for _, additionalPackage := range vuln.AdditionalPackages {
+		if err := module.CheckImportPath(additionalPackage.Package); err != nil {
+			return err
+		}
+	}
+
+	for _, version := range vuln.Versions {
+		if version.Introduced != "" {
+			if !semver.IsValid(version.Introduced) {
+				return fmt.Errorf("bad version.introduced")
+			}
+			if err := module.Check(vuln.Package, version.Introduced); err != nil {
+				return err
+			}
+		}
+		if version.Fixed != "" {
+			if !semver.IsValid(version.Fixed) {
+				return fmt.Errorf("bad version.fixed")
+			}
+			if err := module.Check(vuln.Package, version.Fixed); err != nil {
+				return err
+			}
+		}
+	}
+
+	if vuln.Description == "" {
+		return errors.New("missing description")
+	}
+
+	sevs := map[string]bool{
+		"low":      true,
+		"medium":   true,
+		"high":     true,
+		"critical": true,
+	}
+	// Could also just default to medium if not provided?
+	// Need to document what the default case is and what factors lower
+	// or raise the sev
+	if vuln.Severity != "" && !sevs[vuln.Severity] {
+		return fmt.Errorf("unknown severity %q", vuln.Severity)
+	}
+
+	if vuln.CVE != "" && vuln.CVEMetadata.ID != "" {
+		// TODO: may just want to use one of these? :shrug:
+		return errors.New("only one of cve and cve_metadata.id should be present")
+	}
+
+	if vuln.CVE != "" && !cveRegex.MatchString(vuln.CVE) {
+		return fmt.Errorf("malformed CVE number: %s", vuln.CVE)
+	}
+
+	return nil
+}
diff --git a/reports/GO-2020-0001.toml b/reports/GO-2020-0001.toml
new file mode 100644
index 0000000..1885cb1
--- /dev/null
+++ b/reports/GO-2020-0001.toml
@@ -0,0 +1,29 @@
+package = "github.com/gin-gonic/gin"
+
+description = """
+The default [`Formatter`][LoggerConfig.Formatter] for the [`Logger`][] middleware
+(included in the [`Default`][] engine) allows attackers to inject arbitrary log
+entries by manipulating the request path.
+"""
+
+credit = "@thinkerou <thinkerou@gmail.com>"
+
+# Better static analysis: LoggerWithConfig called with nil conf.Formatter.
+# Test symbol inclusion by making a gin handler without Default or Logger.
+symbols = ["defaultLogFormatter"]
+
+[[versions]]
+# v1.5.1 doesn't exist? not sure how `go mod` is picking this pseudoversion
+fixed = "v1.6.0"
+
+[links]
+pr = "https://github.com/gin-gonic/gin/pull/2237"
+commit = "https://github.com/gin-gonic/gin/commit/a71af9c144f9579f6dbe945341c1df37aaf09c0d"
+
+[cve_metadata]
+id = "CVE-XXX"
+description = """
+Unsanitized input in the default logger in github.com/gin-gonic/gin before v1.6.0
+allows remote attackers to inject arbitary log lines.
+"""
+cwe = "CWE-20: Improper Input Validation"
diff --git a/reports/GO-2020-0002.toml b/reports/GO-2020-0002.toml
new file mode 100644
index 0000000..3623002
--- /dev/null
+++ b/reports/GO-2020-0002.toml
@@ -0,0 +1,18 @@
+package = "github.com/proglottis/gpgme"
+
+cve = "CVE-2020-8945"
+
+description = """
+The [`Data`][], [`Context`][], or [`Key`][] finalizers might run during or
+before GPGME operations, releasing the C structures as they are still in use,
+leading to crashes and potentially code execution through a use-after-free.
+"""
+
+credit = "Ulrich Obergfell <uobergfe@redhat.com>"
+
+[[versions]]
+fixed = "v0.1.1"
+
+[links]
+pr = "https://github.com/proglottis/gpgme/pull/23"
+commit = "https://github.com/proglottis/gpgme/commit/92153bcb59bd2f511e502262c46c7bd660e21733"
diff --git a/reports/GO-2020-0003.toml b/reports/GO-2020-0003.toml
new file mode 100644
index 0000000..81ddcc1
--- /dev/null
+++ b/reports/GO-2020-0003.toml
@@ -0,0 +1,26 @@
+package = "github.com/revel/revel"
+
+description = """
+If the application accepts
+[slice parameters](https://revel.github.io/manual/parameters.html#slices), an
+attacker can cause the application to allocate large amounts of memory and
+crash by manipulating the request query.
+"""
+
+credit = "@SYM01"
+
+[[versions]]
+fixed = "v1.0.0"
+
+[links]
+commit = "https://github.com/revel/revel/commit/d160ecb72207824005b19778594cbdc272e8a605"
+pr = "https://github.com/revel/revel/pull/1427"
+context = ["https://github.com/revel/revel/issues/1424"]
+
+[cve_metadata]
+id = "CVE-XXXX-0002"
+description = """
+Unsanitized input in the query parser in github.com/revel/revel before v1.0.0
+allows remote attackers to cause resource exhaustion via memory allocation.
+"""
+cwe = "CWE-400: Uncontrolled Resource Consumption"
\ No newline at end of file
diff --git a/reports/GO-2020-0004.toml b/reports/GO-2020-0004.toml
new file mode 100644
index 0000000..54a1992
--- /dev/null
+++ b/reports/GO-2020-0004.toml
@@ -0,0 +1,31 @@
+package = "github.com/nanobox-io/golang-nanoauth"
+
+description = """
+If any of the `ListenAndServe` functions are called with an empty token,
+token authentication is disabled globally for all listeners.
+
+Also, a minor timing side channel was present allowing attackers with
+very low latency and able to make a lot of requests to potentially
+recover the token.
+"""
+
+credit = "@bouk"
+
+symbols = ["Auth.ServerHTTP", "Auth.ListenAndServeTLS", "Auth.ListenAndServe"]
+
+[[versions]]
+introduced = "v0.0.0-20160722212129-ac0cc4484ad4"
+fixed = "v0.0.0-20200131131040-063a3fb69896"
+
+[links]
+pr = "https://github.com/nanobox-io/golang-nanoauth/pull/5"
+commit = "https://github.com/nanobox-io/golang-nanoauth/commit/063a3fb69896acf985759f0fe3851f15973993f3"
+
+[cve_metadata]
+id = "CVE-XXXX-0003"
+description = """
+Authentication is globally bypassed in github.com/nanobox-io/golang-nanoauth between
+v0.0.0-20160722212129-ac0cc4484ad4 and v0.0.0-20200131131040-063a3fb69896 if ListenAndServe
+is called with an empty token.
+"""
+cwe = "CWE-305: Authentication Bypass by Primary Weakness"
\ No newline at end of file
diff --git a/reports/GO-2020-0005.toml b/reports/GO-2020-0005.toml
new file mode 100644
index 0000000..bcdf9db
--- /dev/null
+++ b/reports/GO-2020-0005.toml
@@ -0,0 +1,24 @@
+package = "github.com/etcd-io/etcd/wal"
+
+description = """
+Malformed WALs can be constructed such that [`WAL.ReadAll`][] can cause attempted
+out of bounds reads, or creation of arbitarily sized slices, which may be used as
+a DoS vector.
+"""
+
+cve = "CVE-2020-15106"
+
+credit = "Trail of Bits"
+
+symbols = ["WAL.ReadAll"]
+
+[[versions]]
+# Do we also need a way to indicate "fixed after this version, but also these specific
+# earlier point releases are also fixed"? In this case >= 3.4.10 is fixed, but so was
+# 3.3.23
+fixed = "v0.5.0-alpha.5.0.20200423152442-f4b650b51dc4"
+
+[links]
+pr = "https://github.com/etcd-io/etcd/pull/11793"
+commit = "https://github.com/etcd-io/etcd/commit/f4b650b51dc4a53a8700700dc12e1242ac56ba07"
+context = ["https://github.com/etcd-io/etcd/blob/master/security/SECURITY_AUDIT.pdf"]
\ No newline at end of file
diff --git a/reports/GO-2020-0006.toml b/reports/GO-2020-0006.toml
new file mode 100644
index 0000000..cf12ce4
--- /dev/null
+++ b/reports/GO-2020-0006.toml
@@ -0,0 +1,20 @@
+package = "github.com/miekg/dns"
+
+description = """
+An attacker may prevent TCP connections to a [`Server`][] by opening
+a connection and leaving it idle, until the connection is closed by
+the server no other connections will be accepted.
+"""
+
+cve = "CVE-2017-15133"
+
+credit = "Pedro Sampaio"
+
+symbols = ["Server.serveTCP"]
+
+[[versions]]
+fixed = "v1.0.4-0.20180125103619-43913f2f4fbd"
+
+[links]
+pr = "https://github.com/miekg/dns/pull/631"
+commit = "https://github.com/miekg/dns/commit/43913f2f4fbd7dcff930b8a809e709591e4dd79e"
diff --git a/reports/GO-2020-0007.toml b/reports/GO-2020-0007.toml
new file mode 100644
index 0000000..d44c60d
--- /dev/null
+++ b/reports/GO-2020-0007.toml
@@ -0,0 +1,21 @@
+package = "github.com/seccomp/libseccomp-golang"
+
+description = """
+Filters containing rules with multiple syscall arguments are improperly
+constructed, such that all arguments are required to match rather than
+any of the arguments (AND is used rather than OR). These filters can be
+bypassed by only specifying a subset of the arguments due to this
+behavior.
+"""
+
+cve = "CVE-2017-18367"
+
+credit = "@ihac"
+
+symbols = ["ScmpFilter.addRuleGeneric"]
+
+[[versions]]
+fixed = "v0.9.1-0.20170424173420-06e7a29f36a3"
+
+[links]
+commit = "https://github.com/seccomp/libseccomp-golang/commit/06e7a29f36a34b8cf419aeb87b979ee508e58f9e"
\ No newline at end of file
diff --git a/reports/GO-2020-0008.toml b/reports/GO-2020-0008.toml
new file mode 100644
index 0000000..0e23a88
--- /dev/null
+++ b/reports/GO-2020-0008.toml
@@ -0,0 +1,22 @@
+package = "github.com/miekg/dns"
+
+description = """
+DNS message transaction IDs are generated using [`math/rand`] which
+makes them relatively predictable. This reduces the complexity
+of response spoofing attacks against DNS clients.
+"""
+
+cve = "CVE-2019-19794"
+
+symbols = ["id"]
+
+[[versions]]
+fixed = "v1.1.25-0.20191211073109-8ebf2e419df7"
+
+[links]
+pr = "https://github.com/miekg/dns/pull/1044"
+commit = "https://github.com/miekg/dns/commit/8ebf2e419df7857ac8919baa05248789a8ffbf33"
+context = [
+    "https://github.com/miekg/dns/issues/1037",
+    "https://github.com/miekg/dns/issues/1043"
+]
\ No newline at end of file
diff --git a/reports/GO-2020-0009.toml b/reports/GO-2020-0009.toml
new file mode 100644
index 0000000..dd55cb5
--- /dev/null
+++ b/reports/GO-2020-0009.toml
@@ -0,0 +1,40 @@
+package = "github.com/square/go-jose/cipher"
+
+arch = [
+    "386",
+    "arm",
+    "armbe",
+    "amd64p32",
+    "mips",
+    "mipsle",
+    "mips64p32",
+    "mips64p32le",
+    "ppc",
+    "riscv",
+    "s390",
+    "sparc"
+]
+
+description = """
+On 32-bit platforms an attacker can manipulate a ciphertext encrypted with AES-CBC
+with HMAC such that they can control how large the input buffer is when computing
+the HMAC authentication tag. This can can allow a manipulated ciphertext to be
+verified as authentic, opening the door for padding oracle attacks.
+"""
+
+cve = "CVE-2016-9123"
+
+credit = "Quan Nguyen from Google's Information Security Engineering Team"
+
+symbols = ["cbcAEAD.computeAuthTag"]
+
+[[versions]]
+fixed = "v0.0.0-20160903044734-789a4c4bd4c1"
+
+[[additional_packages]]
+package = "github.com/square/go-jose"
+symbols = ["JsonWebEncryption.Decrypt", "JsonWebEncryption.DecryptMulti"]
+
+[links]
+commit = "https://github.com/square/go-jose/commit/789a4c4bd4c118f7564954f441b29c153ccd6a96"
+context = ["https://www.openwall.com/lists/oss-security/2016/11/03/1"]
\ No newline at end of file
diff --git a/reports/GO-2020-0010.toml b/reports/GO-2020-0010.toml
new file mode 100644
index 0000000..2659efa
--- /dev/null
+++ b/reports/GO-2020-0010.toml
@@ -0,0 +1,24 @@
+package = "github.com/square/go-jose/cipher"
+
+description = """
+When using ECDH-ES an attacker can mount an invalid curve attack during
+decryption as the supplied public key is not checked to be on the same
+curve as the recievers private key.
+"""
+
+cve = "CVE-2016-9121"
+
+credit = "Quan Nguyen from Google's Information Security Engineering Team"
+
+symbols = ["DeriveECDHES", "ecDecrypterSigner.decryptKey", "rawJsonWebKey.ecPublicKey"]
+
+[[versions]]
+fixed = "v0.0.0-20160831185616-c7581939a365"
+
+[[additional_packages]]
+package = "github.com/square/go-jose"
+symbols = ["JsonWebEncryption.Decrypt"]
+
+[links]
+commit = "https://github.com/square/go-jose/commit/c7581939a3656bb65e89d64da0a52364a33d2507"
+context = ["https://www.openwall.com/lists/oss-security/2016/11/03/1"]
\ No newline at end of file
diff --git a/reports/GO-2020-0011.toml b/reports/GO-2020-0011.toml
new file mode 100644
index 0000000..81ec824
--- /dev/null
+++ b/reports/GO-2020-0011.toml
@@ -0,0 +1,22 @@
+package = "github.com/square/go-jose"
+
+description = """
+When decrypting JsonWebEncryption objects with multiple recipients
+or JsonWebSignature objects with multiple signatures the Decrypt
+and Verify methods do not indicate which recipient or signature was
+valid. This may lead a caller to rely on protected headers from an
+invalid recipient or signature.
+"""
+
+cve = "CVE-2016-9122"
+
+credit = "Quan Nguyen from Google's Information Security Engineering Team"
+
+symbols = ["JsonWebEncryption.Decrypt", "JsonWebSignature.Verify"]
+
+[[versions]]
+fixed = "v0.0.0-20160922232413-2c5656adca99"
+
+[links]
+commit = "https://github.com/square/go-jose/commit/2c5656adca9909843c4ff50acf1d2cf8f32da7e6"
+context = ["https://www.openwall.com/lists/oss-security/2016/11/03/1"]
\ No newline at end of file
diff --git a/reports/GO-2020-0012.toml b/reports/GO-2020-0012.toml
new file mode 100644
index 0000000..8ba1186
--- /dev/null
+++ b/reports/GO-2020-0012.toml
@@ -0,0 +1,21 @@
+package = "golang.org/x/crypto/ssh"
+
+description = """
+An attacker can craft an ssh-ed25519 or sk-ssh-ed25519@openssh.com public
+key, such that the library will panic when trying to verify a signature
+with it.
+"""
+
+cve = "CVE-2020-9283"
+
+credit = "Alex Gaynor, Fish in a Barrel"
+
+symbols = ["parseED25519", "ed25519PublicKey.Verify", "parseSKEd25519", "skEd25519PublicKey.Verify", "NewPublicKey"]
+
+[[versions]]
+fixed = "v0.0.0-20200220183623-bac4c82f6975"
+
+[links]
+pr = "https://go-review.googlesource.com/c/crypto/+/220357"
+commit = "https://github.com/golang/crypto/commit/bac4c82f69751a6dd76e702d54b3ceb88adab236"
+context = ["https://groups.google.com/g/golang-announce/c/3L45YRc91SY"]
\ No newline at end of file
diff --git a/reports/GO-2020-0013.toml b/reports/GO-2020-0013.toml
new file mode 100644
index 0000000..2e85d72
--- /dev/null
+++ b/reports/GO-2020-0013.toml
@@ -0,0 +1,24 @@
+package = "golang.org/x/crypto/ssh"
+
+description = """
+By default host key verification is disabled which allows for
+man-in-the-middle attacks against SSH clients if
+[`ClientConfig.HostKeyCallback`] is not set.
+"""
+
+cve = "CVE-2017-3204"
+
+credit = "Phil Pennock"
+
+symbols = ["NewClientConn"]
+
+[[versions]]
+fixed = "v0.0.0-20170330155735-e4e2799dd7aa"
+
+[links]
+pr = "https://go-review.googlesource.com/38701"
+commit = "https://github.com/golang/crypto/commit/e4e2799dd7aab89f583e1d898300d96367750991"
+context = [
+    "https://github.com/golang/go/issues/19767",
+    "https://bridge.grumpy-troll.org/2017/04/golang-ssh-security/"
+]
\ No newline at end of file
diff --git a/reports/GO-2020-0014.toml b/reports/GO-2020-0014.toml
new file mode 100644
index 0000000..f07863c
--- /dev/null
+++ b/reports/GO-2020-0014.toml
@@ -0,0 +1,19 @@
+package = "golang.org/x/net/html"
+
+description = """
+[`html.Parse`] does not properly handle "select" tags, which can lead
+to an infinite loop.
+"""
+cve = "CVE-2018-17846"
+
+credit = "@tr3ee"
+
+symbols = ["inSelectIM", "inSelectInTableIM"]
+
+[[versions]]
+fixed = "v0.0.0-20190125091013-d26f9f9a57f3"
+
+[links]
+pr = "https://go-review.googlesource.com/c/137275"
+commit = "https://github.com/golang/net/commit/d26f9f9a57f3fab6a695bec0d84433c2c50f8bbf"
+context = ["https://github.com/golang/go/issues/27842"]
\ No newline at end of file
diff --git a/reports/GO-2020-0015.toml b/reports/GO-2020-0015.toml
new file mode 100644
index 0000000..24136bf
--- /dev/null
+++ b/reports/GO-2020-0015.toml
@@ -0,0 +1,31 @@
+package = "golang.org/x/text/encoding/unicode"
+
+description = """
+An attacker could provide a single byte to a [`UTF16`] decoder instantiated with
+[`UseBOM`] or [`ExpectBOM`] to trigger an infinite loop if the [`String`] function on
+the [`Decoder`] is called, or the [`Decoder`] is passed to [`transform.String`].
+"""
+
+cve = "CVE-2020-14040"
+
+# This was reported by two people, once publicly and once
+# to the security team. Perhaps this should be an array
+# to capture multiple reporters?
+credit = "@abacabadabacaba" # also Anton Gyllenberg
+
+symbols = ["utf16Decoder.Transform"]
+
+[[versions]]
+fixed = "v0.3.3"
+
+[[additional_packages]]
+package = "golang.org/x/text/transform"
+symbols = ["Transform"]
+
+[links]
+pr = "https://go-review.googlesource.com/c/text/+/238238"
+commit = "https://github.com/golang/text/commit/23ae387dee1f90d29a23c0e87ee0b46038fbed0e"
+context = [
+    "https://github.com/golang/go/issues/39491",
+    "https://groups.google.com/g/golang-announce/c/bXVeAmGOqz0"
+]
\ No newline at end of file
diff --git a/reports/GO-2020-0016.toml b/reports/GO-2020-0016.toml
new file mode 100644
index 0000000..487e91d
--- /dev/null
+++ b/reports/GO-2020-0016.toml
@@ -0,0 +1,28 @@
+package = "github.com/ulikunitz/xz"
+
+description = """
+An attacker can construct a series of bytes such that calling
+[`Reader.Read`] on the bytes could cause an infinite loop.
+"""
+
+credit = "@0xdecaf"
+
+symbols = ["readUvarint"]
+
+[[versions]]
+fixed = "v0.5.8"
+
+[links]
+commit = "https://github.com/ulikunitz/xz/commit/69c6093c7b2397b923acf82cb378f55ab2652b9b"
+context = [
+    "https://github.com/ulikunitz/xz/issues/35",
+    "https://github.com/ulikunitz/xz/security/advisories/GHSA-25xm-hr59-7c27"
+]
+
+[cve_metadata]
+id = "CVE-XXXX-0004"
+description = """
+Integer overflow in github.com/ulikunitz/xz before v0.5.8 allows attackers
+to cause denial of service via maliciously crafted input.
+"""
+cwe = "CWE-190: Integer Overflow or Wraparound"
diff --git a/reports/GO-2020-0017.toml b/reports/GO-2020-0017.toml
new file mode 100644
index 0000000..d868d32
--- /dev/null
+++ b/reports/GO-2020-0017.toml
@@ -0,0 +1,28 @@
+package = "github.com/dgrijalva/jwt-go"
+
+description = """
+If a JWT contains an audience claim with an array of strings, rather
+than a single string, and `MapClaims.VerifyAudience` is called with
+`req` set to `false` then audience verification will be bypassed,
+allowing an invalid set of audiences to be provided.
+"""
+
+cve = "CVE-2020-26160"
+
+credit = "@christopher-wong"
+
+symbols = ["MapClaims.VerifyAudience"]
+
+[[versions]]
+introduced = "v0.0.0-20150717181359-44718f8a89b0"
+fixed = "v4.0.0-20190408214815-ec0a89a131e"
+
+[[additional_packages]]
+package = "github.com/dgrijalva/jwt-go/v4"
+symbols = ["MapClaims.VerifyAudience"]
+[[additional_packages.versions]]
+fixed = "v4.0.0-20190408214815-ec0a89a131e3"
+
+[links]
+commit = "https://github.com/dgrijalva/jwt-go/commit/ec0a89a131e3e8567adcb21254a5cd20a70ea4ab"
+context = ["https://github.com/dgrijalva/jwt-go/issues/422"]
\ No newline at end of file
diff --git a/reports/GO-2020-0018.toml b/reports/GO-2020-0018.toml
new file mode 100644
index 0000000..871f02f
--- /dev/null
+++ b/reports/GO-2020-0018.toml
@@ -0,0 +1,19 @@
+package = "github.com/satori/go.uuid"
+
+description = """
+UUIDs generated using [`NewV1`] and [`NewV4`] may not read the expected
+number of random bytes. These UUIDs may contain a significantly smaller
+amount of entropy than expected, possibly leading to collisions.
+"""
+
+credit = "@josselin-c"
+
+symbols = ["NewV4", "rfc4122Generator.getClockSequence", "rfc4122Generator.getHardwareAddr"]
+
+[[versions]]
+fixed = "v1.2.1-0.20181016170032-d91630c85102"
+
+[links]
+pr = "https://github.com/satori/go.uuid/pull/75"
+commit = "https://github.com/satori/go.uuid/commit/d91630c8510268e75203009fe7daf2b8e1d60c45"
+context = ["https://github.com/satori/go.uuid/issues/73"]
\ No newline at end of file
diff --git a/reports/GO-2020-0019.toml b/reports/GO-2020-0019.toml
new file mode 100644
index 0000000..7b7dfc1
--- /dev/null
+++ b/reports/GO-2020-0019.toml
@@ -0,0 +1,21 @@
+package = "github.com/gorilla/websocket"
+
+description = """
+An attacker can craft malicious WebSocket frames that cause an integer
+overflow in a variable which tracks the number of bytes remaining. This
+can cause the server or client to get stuck attempting to read frames
+in a loop.
+"""
+
+cve = "CVE-2020-27813"
+
+credit = "Max Justicz"
+
+symbols = ["Conn.advanceFrame", "messageReader.Read"]
+
+[[versions]]
+fixed = "v1.4.1"
+
+[links]
+pr = "https://github.com/gorilla/websocket/pull/537"
+commit = "https://github.com/gorilla/websocket/commit/5b740c29263eb386f33f265561c8262522f19d37"
\ No newline at end of file
diff --git a/reports/GO-2020-0020.toml b/reports/GO-2020-0020.toml
new file mode 100644
index 0000000..dc6c2e1
--- /dev/null
+++ b/reports/GO-2020-0020.toml
@@ -0,0 +1,18 @@
+package = "github.com/gorilla/handlers"
+
+description = """
+Usage of the [`CORS`] handler may apply improper CORS headers, allowing
+the requester to explicitly control the value of the Access-Control-Allow-Origin
+header, which bypasses the expected behavior of the Same Origin Policy.
+"""
+
+credit = "Evan J Johnson"
+
+symbols = ["cors.ServeHTTP"]
+
+[[versions]]
+fixed = "v1.3.0"
+
+[links]
+pr = "https://github.com/gorilla/handlers/pull/116"
+commit = "https://github.com/gorilla/handlers/commit/90663712d74cb411cbef281bc1e08c19d1a76145"
\ No newline at end of file
diff --git a/reports/GO-2020-0021.toml b/reports/GO-2020-0021.toml
new file mode 100644
index 0000000..064a8b0
--- /dev/null
+++ b/reports/GO-2020-0021.toml
@@ -0,0 +1,20 @@
+package = "github.com/gogits/gogs"
+
+description = """
+Multiple methods are vulnerable to SQL injection attacks as unsanitized
+user input is used to construct SQL statements.
+"""
+
+cve = "CVE-2014-8681"
+
+credit = "Pascal Turbing and Jiahua (Joe) Chen"
+
+symbols = ["GetIssues", "SearchRepositoryByName", "SearchUserByName"]
+
+[[versions]]
+introduced = "v0.3.1-9-g49dc57e"
+fixed = "v0.5.8"
+
+[links]
+commit = "https://github.com/gogs/gogs/commit/83283bca4cb4e0f4ec48a28af680f0d88db3d2c8"
+context = ["https://seclists.org/fulldisclosure/2014/Nov/31"]
\ No newline at end of file
diff --git a/reports/GO-2020-0022.toml b/reports/GO-2020-0022.toml
new file mode 100644
index 0000000..3a9b4e8
--- /dev/null
+++ b/reports/GO-2020-0022.toml
@@ -0,0 +1,18 @@
+package = "github.com/cloudflare/golz4"
+
+description = """
+LZ4 bindings used a deprecated C API that is vulnerable to
+memory corruption which could lead to arbitrary code execution
+if successfully exploited.
+"""
+
+credit = "Don A. Bailey"
+
+symbols = ["Uncompress"]
+
+[[versions]]
+fixed = "v0.0.0-20140711154735-199f5f787806"
+
+[links]
+commit = "https://github.com/cloudflare/golz4/commit/199f5f7878062ca17a98e079f2dbe1205e2ed898"
+context = ["https://github.com/cloudflare/golz4/issues/5"]
\ No newline at end of file
diff --git a/reports/GO-2020-0023.toml b/reports/GO-2020-0023.toml
new file mode 100644
index 0000000..feaee24
--- /dev/null
+++ b/reports/GO-2020-0023.toml
@@ -0,0 +1,16 @@
+package = "github.com/robbert229/jwt"
+
+description = """
+[`Validate`] used non-constant time string comparison to
+compare a provided HMAC against expected HMAC, providing
+a timing side-channel.
+"""
+
+symbols = ["Algorithm.validateSignature"]
+
+[[versions]]
+fixed = "v0.0.0-20170426191122-ca1404ee6e83"
+
+[links]
+commit = "https://github.com/robbert229/jwt/commit/ca1404ee6e83fcbafb66b09ed0d543850a15b654"
+context = ["https://github.com/robbert229/jwt/issues/12"]
\ No newline at end of file
diff --git a/reports/GO-2020-0024.toml b/reports/GO-2020-0024.toml
new file mode 100644
index 0000000..91e4649
--- /dev/null
+++ b/reports/GO-2020-0024.toml
@@ -0,0 +1,20 @@
+package = "github.com/btcsuite/go-socks/socks"
+
+description = """
+The RemoteAddr and LocalAddr methods on the returned net.Conn may
+call themselves, leading to an infinite loop.
+"""
+
+symbols = ["proxiedConn.LocalAddr", "proxiedConn.RemoteAddr"]
+
+[[versions]]
+fixed = "v0.0.0-20130808000456-233bccbb1abe"
+
+[[additional_packages]]
+package = "github.com/btcsuitereleases/go-socks/socks"
+symbols = ["proxiedConn.LocalAddr", "proxiedConn.RemoteAddr"]
+[[additional_packages.versions]]
+fixed = "v0.0.0-20130808000456-233bccbb1abe"
+
+[links]
+commit = "https://github.com/btcsuite/go-socks/commit/233bccbb1abe02f05750f7ace66f5bffdb13defc"
\ No newline at end of file
diff --git a/reports/GO-2020-0025.toml b/reports/GO-2020-0025.toml
new file mode 100644
index 0000000..12c1415
--- /dev/null
+++ b/reports/GO-2020-0025.toml
@@ -0,0 +1,22 @@
+package = "github.com/cloudfoundry/archiver"
+
+description = """
+Malicious Zip and Tar archives can be crafted that contain relative
+file paths, such that arbitary files outside of the target directory
+may be overwritten.
+"""
+
+symbols = ["tgzExtractor.Extract", "zipExtractor.Extract"]
+
+[[versions]]
+fixed = "v0.0.0-20180523222229-09b5706aa936"
+
+[[additional_packages]]
+package = "code.cloudfoundry.org/archiver"
+symbols = ["tgzExtractor.Extract", "zipExtractor.Extract"]
+[[versions]]
+fixed = "v0.0.0-20180523222229-09b5706aa936"
+
+[links]
+commit = "https://github.com/cloudfoundry/archiver/commit/09b5706aa9367972c09144a450bb4523049ee840"
+context = ["https://snyk.io/research/zip-slip-vulnerability"]
\ No newline at end of file
diff --git a/reports/GO-2020-0026.toml b/reports/GO-2020-0026.toml
new file mode 100644
index 0000000..3ff9545
--- /dev/null
+++ b/reports/GO-2020-0026.toml
@@ -0,0 +1,18 @@
+package = "github.com/openshift/source-to-image/pkg/tar"
+
+description = """
+Malicious Zip and Tar archives can be crafted that contain relative
+file paths, such that arbitary files outside of the target directory
+may be overwritten.
+"""
+
+cve = "CVE-2018-1103"
+
+symbols = ["stiTar.ExtractTarStreamFromTarReader", "stiTar.extractLink", "New"]
+
+[[versions]]
+fixed = "v1.1.10-0.20180427153919-f5cbcbc5cc6f"
+
+[links]
+commit = "https://github.com/openshift/source-to-image/commit/f5cbcbc5cc6f8cc2f479a7302443bea407a700cb"
+context = ["https://snyk.io/research/zip-slip-vulnerability"]
\ No newline at end of file
diff --git a/reports/GO-2020-0027.toml b/reports/GO-2020-0027.toml
new file mode 100644
index 0000000..1d06e60
--- /dev/null
+++ b/reports/GO-2020-0027.toml
@@ -0,0 +1,22 @@
+package = "github.com/google/fscrypt/pam"
+
+description = """
+After dropping and then elevating process privileges euid, guid, and groups
+were not properly restored to their original values, allowing an unprivileged
+user to gain membership in the root group.
+"""
+
+cve = "CVE-2018-6558"
+
+symbols = ["NewHandle", "SetProcessPrivileges", "Handle.StopAsPamUser"]
+
+[[versions]]
+fixed = "v0.2.4"
+
+[[additional_packages]]
+package = "github.com/google/fscrypt/security"
+symbols = ["UserKeyringID"]
+
+[links]
+commit = "https://github.com/google/fscrypt/commit/3022c1603d968c22f147b4a2c49c4637dd1be91b"
+context = ["https://github.com/google/fscrypt/issues/77"]
\ No newline at end of file
diff --git a/reports/GO-2020-0028.toml b/reports/GO-2020-0028.toml
new file mode 100644
index 0000000..48d80ed
--- /dev/null
+++ b/reports/GO-2020-0028.toml
@@ -0,0 +1,19 @@
+package = "github.com/miekg/dns"
+
+description = """
+An attacker can craft a malicious DNS zone file which will cause
+[`ParseZone`] to panic due to a nil pointer dereference.
+"""
+
+cve = "CVE-2018-17419"
+
+credit = "@tr3ee"
+
+symbols = ["setTA"]
+
+[[versions]]
+fixed = "v1.0.10"
+
+[links]
+commit = "https://github.com/miekg/dns/commit/501e858f679edecd4a38a86317ce50271014a80d"
+context = ["https://github.com/miekg/dns/issues/742"]
\ No newline at end of file
diff --git a/reports/GO-2020-0029.toml b/reports/GO-2020-0029.toml
new file mode 100644
index 0000000..df02fab
--- /dev/null
+++ b/reports/GO-2020-0029.toml
@@ -0,0 +1,16 @@
+package = "github.com/gin-gonic/gin"
+
+description = """
+An attacker can spoof their source IP address by setting the X-Forwarded-For
+HTTP header to an arbitrary value.
+"""
+
+credit = "@nl5887"
+
+symbols = ["Context.ClientIP"]
+
+[[versions]]
+fixed = "v0.5.0"
+
+[links]
+commit = "https://github.com/gin-gonic/gin/commit/0099840c98ae1473c5ff0f18bc93a8e13ceed829"
\ No newline at end of file
diff --git a/reports/GO-2020-0030.toml b/reports/GO-2020-0030.toml
new file mode 100644
index 0000000..ece344f
--- /dev/null
+++ b/reports/GO-2020-0030.toml
@@ -0,0 +1,21 @@
+package = "github.com/go-gorm/gorm"
+
+description = """
+Multiple methods are vulnerable to blind SQL injection attacks
+due to constructing SQL statements using unsantized user input.
+"""
+
+credit = "@wahyuhadi"
+
+symbols = ["Scope.buildCondition"]
+
+[[versions]]
+fixed = "v0.2.0"
+
+[[additional_packages]]
+package = "github.com/jinzhu/gorm"
+symbols = ["Scope.buildCondition"]
+
+[links]
+commit = "https://github.com/go-gorm/gorm/commit/836fb2c19d84dac7b0272958dfb9af7cf0d0ade4"
+context = ["https://github.com/go-gorm/gorm/issues/2517"]
\ No newline at end of file
diff --git a/reports/GO-2020-0031.toml b/reports/GO-2020-0031.toml
new file mode 100644
index 0000000..fede0ad
--- /dev/null
+++ b/reports/GO-2020-0031.toml
@@ -0,0 +1,15 @@
+package = "github.com/proglottis/gpgme"
+
+description = """
+The C bindings for the GPGME contain a number of use-after-free issues
+which may lead to memory corruption and possible code execution.
+"""
+
+cve = "CVE-2020-8945"
+
+[[versions]]
+fixed = "v0.1.1"
+
+[links]
+commit = "https://github.com/proglottis/gpgme/commit/92153bcb59bd2f511e502262c46c7bd660e21733"
+context = ["https://bugzilla.redhat.com/show_bug.cgi?id=1795838"]
\ No newline at end of file
diff --git a/reports/GO-2020-0032.toml b/reports/GO-2020-0032.toml
new file mode 100644
index 0000000..5d141ee
--- /dev/null
+++ b/reports/GO-2020-0032.toml
@@ -0,0 +1,37 @@
+package = "github.com/goadesign/goa"
+
+description = """
+[`Controller.FileHandler`] allows for directory traversal attacks due
+to usage of unsanitized user input.
+"""
+
+credit = "@christi3k"
+
+symbols = ["Controller.FileHandler"]
+
+[[versions]]
+fixed = "v1.4.3"
+
+[[additional_packages]]
+package = "github.com/goadesign/goa/v2"
+symbols = ["Controller.FileHandler"]
+[[additional_packages.versions]]
+fixed = "v2.0.10"
+
+[[additional_packages]]
+package = "github.com/goadesign/goa/v3"
+symbols = ["Controller.FileHandler"]
+[[additional_packages.versions]]
+fixed = "v3.0.9"
+
+[links]
+commit = "https://github.com/goadesign/goa/commit/70b5a199d0f813d74423993832c424e1fc73fb39"
+pr = "https://github.com/goadesign/goa/pull/2388"
+
+[cve_metadata]
+id = "CVE-XXXX-0012"
+description = """
+Improper path santiziation in github.com/goadesign/goa before v3.0.9, v2.0.10, or
+v1.4.3 allow remote attackers to read files outside of the intended directory.
+"""
+cwe = "CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')"
diff --git a/reports/GO-2020-0033.toml b/reports/GO-2020-0033.toml
new file mode 100644
index 0000000..a55e0a0
--- /dev/null
+++ b/reports/GO-2020-0033.toml
@@ -0,0 +1,18 @@
+package = "github.com/go-aah/aah"
+
+description = """
+[`HTTPEngine.Handle`] allows for directory traversal attacks due
+to usage of unsanitized user input.
+"""
+
+credit = "@snyff"
+
+symbols = ["HTTPEngine.Handle"]
+
+[[versions]]
+fixed = "v0.12.4"
+
+[links]
+commit = "https://github.com/go-aah/aah/commit/881dc9f71d1f7a4e8a9a39df9c5c081d3a2da1ec"
+pr = "https://github.com/go-aah/aah/pull/267"
+context = ["https://github.com/go-aah/aah/issues/266"]
\ No newline at end of file
diff --git a/reports/GO-2020-0034.toml b/reports/GO-2020-0034.toml
new file mode 100644
index 0000000..c110c7b
--- /dev/null
+++ b/reports/GO-2020-0034.toml
@@ -0,0 +1,16 @@
+package = "github.com/artdarek/go-unzip"
+
+description = """
+Malicious Zip archives can be crafted that contain relative file paths,
+such that arbitary files outside of the target directory may be overwritten.
+"""
+
+symbols = ["Unzip.Extract"]
+
+[[versions]]
+fixed = "v1.0.0"
+
+[links]
+commit = "https://github.com/artdarek/go-unzip/commit/4975cbe0a719dc50b12da8585f1f207c82f7dfe0"
+pr = "https://github.com/artdarek/go-unzip/pull/2"
+context = ["https://snyk.io/research/zip-slip-vulnerability"]
\ No newline at end of file
diff --git a/reports/GO-2020-0035.toml b/reports/GO-2020-0035.toml
new file mode 100644
index 0000000..34ee60c
--- /dev/null
+++ b/reports/GO-2020-0035.toml
@@ -0,0 +1,16 @@
+package = "github.com/yi-ge/unzip"
+
+description = """
+Malicious Zip archives can be crafted that contain relative file paths,
+such that arbitary files outside of the target directory may be overwritten.
+"""
+
+symbols = ["Unzip.Extract"]
+
+[[versions]]
+fixed = "v1.0.3-0.20200308084313-2adbaa4891b9"
+
+[links]
+commit = "https://github.com/yi-ge/unzip/commit/2adbaa4891b9690853ef10216189189f5ad7dc73"
+pr = "https://github.com/yi-ge/unzip/pull/1"
+context = ["https://snyk.io/research/zip-slip-vulnerability"]
\ No newline at end of file
diff --git a/reports/GO-2020-0036.toml b/reports/GO-2020-0036.toml
new file mode 100644
index 0000000..a13c6c0
--- /dev/null
+++ b/reports/GO-2020-0036.toml
@@ -0,0 +1,24 @@
+package = "gopkg.in/yaml.v2"
+
+description = """
+An attacker can craft malicious YAML which will consume significant
+system resources when Unmarshalled.
+"""
+
+cve = "CVE-2019-11254"
+
+symbols = ["yaml_parser_fetch_more_tokens"]
+
+[[versions]]
+fixed = "v2.2.8"
+
+[[additional_packages]]
+package = "github.com/go-yaml/yaml"
+symbols = ["yaml_parser_fetch_more_tokens"]
+[[additional_packages.versions]]
+fixed = "v2.2.8"
+
+[links]
+commit = "https://github.com/go-yaml/yaml/commit/53403b58ad1b561927d19068c655246f2db79d48"
+pr = "https://github.com/go-yaml/yaml/pull/555"
+context = ["https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=18496"]
\ No newline at end of file
diff --git a/reports/GO-2020-0037.toml b/reports/GO-2020-0037.toml
new file mode 100644
index 0000000..64e1280
--- /dev/null
+++ b/reports/GO-2020-0037.toml
@@ -0,0 +1,17 @@
+package = "github.com/tendermint/tendermint/rpc/client"
+
+description = """
+A malicious server can exploit support for gzip compression to force
+the client to consume significant system resources, leading to crashes. 
+"""
+
+credit = "@guagualvcha"
+
+symbols = ["makeHTTPClient"]
+
+[[versions]]
+fixed = "v0.31.1"
+
+[links]
+commit = "https://github.com/tendermint/tendermint/commit/03085c2da23b179c4a51f59a03cb40aa4e85a613"
+pr = "https://github.com/tendermint/tendermint/pull/3430"
\ No newline at end of file
diff --git a/reports/GO-2020-0038.toml b/reports/GO-2020-0038.toml
new file mode 100644
index 0000000..c1e58ca
--- /dev/null
+++ b/reports/GO-2020-0038.toml
@@ -0,0 +1,19 @@
+package = "github.com/pion/dtls"
+
+description = """
+An attacker can craft records that allow the processing of arbitrary
+unencrypted application data at any point after the initial handshake
+is completed.
+"""
+
+cve = "CVE-2019-20786"
+
+symbols = ["Conn.handleIncomingPacket"]
+
+[[versions]]
+fixed = "v1.5.2"
+
+[links]
+commit = "https://github.com/pion/dtls/commit/fd73a5df2ff0e1fb6ae6a51e2777d7a16cc4f4e0"
+pr = "https://github.com/pion/dtls/pull/128"
+context = ["https://www.usenix.org/system/files/sec20fall_fiterau-brostean_prepub.pdf"]
\ No newline at end of file
diff --git a/reports/GO-2020-0039.toml b/reports/GO-2020-0039.toml
new file mode 100644
index 0000000..9f4fb9a
--- /dev/null
+++ b/reports/GO-2020-0039.toml
@@ -0,0 +1,26 @@
+package = "github.com/go-macaron/macaron"
+
+description = """
+An attacker can craft a malicious URL which will cause the server
+to redirect a user to a secondary URL.
+"""
+
+cve = "CVE-2020-12666"
+
+credit = "@ev0A"
+
+symbols = ["staticHandler"]
+
+[[versions]]
+fixed = "v1.3.7"
+
+[[additional_packages]]
+package = "gopkg.in/macaron.v1"
+symbols = ["staticHandler"]
+[[additional_packages.versions]]
+fixed = "v1.3.7"
+
+[links]
+commit = "https://github.com/go-macaron/macaron/commit/addc7461c3a90a040e79aa75bfd245107a210245"
+pr = "https://github.com/go-macaron/macaron/pull/199"
+context = ["https://github.com/go-macaron/macaron/issues/198"]
\ No newline at end of file
diff --git a/reports/GO-2020-0040.toml b/reports/GO-2020-0040.toml
new file mode 100644
index 0000000..8580311
--- /dev/null
+++ b/reports/GO-2020-0040.toml
@@ -0,0 +1,11 @@
+package = "github.com/shiyanhui/dht"
+
+description = """
+A malicious peer can craft messages which will cause panics due to
+unchecked type assertions.
+"""
+
+credit = "@hMihaiDavid"
+
+[links]
+context = ["https://github.com/shiyanhui/dht/issues/57"]
\ No newline at end of file
diff --git a/reports/GO-2020-0041.toml b/reports/GO-2020-0041.toml
new file mode 100644
index 0000000..2a02d72
--- /dev/null
+++ b/reports/GO-2020-0041.toml
@@ -0,0 +1,24 @@
+package = "github.com/unknwon/cae/tz"
+
+description = """
+Malicious Zip and Tar archives can be crafted that contain relative
+file paths, such that arbitary files outside of the target directory
+may be overwritten.
+"""
+
+cve = "CVE-2020-7668"
+
+symbols = ["TzArchive.syncFiles", "TzArchive.ExtractToFunc"]
+
+[[versions]]
+fixed = "v1.0.1"
+
+[[additional_packages]]
+package = "github.com/unknwon/cae/zip"
+symbols = ["ZipArchive.Open", "ZipArchive.ExtractToFunc"]
+[[additional_packages.versions]]
+fixed = "v1.0.1"
+
+[links]
+commit = "https://github.com/unknwon/cae/commit/07971c00a1bfd9dc171c3ad0bfab5b67c2287e11"
+context = ["https://snyk.io/research/zip-slip-vulnerability"]
\ No newline at end of file
diff --git a/reports/GO-2020-0042.toml b/reports/GO-2020-0042.toml
new file mode 100644
index 0000000..88c9269
--- /dev/null
+++ b/reports/GO-2020-0042.toml
@@ -0,0 +1,18 @@
+package = "github.com/sassoftware/go-rpmutils/cpio"
+
+description = """
+Malicious RPM archives can be crafted that contain relative
+file paths, such that arbitary files outside of the target directory
+may be overwritten.
+"""
+
+cve = "CVE-2020-7667"
+
+symbols = ["Extract"]
+
+[[versions]]
+fixed = "v0.1.0"
+
+[links]
+commit = "https://github.com/sassoftware/go-rpmutils/commit/a64058cf21b8aada501bba923c9aab66fb6febf0"
+context = ["https://snyk.io/research/zip-slip-vulnerability"]
\ No newline at end of file
diff --git a/reports/GO-2020-0043.toml b/reports/GO-2020-0043.toml
new file mode 100644
index 0000000..d3c312c
--- /dev/null
+++ b/reports/GO-2020-0043.toml
@@ -0,0 +1,26 @@
+package = "github.com/mholt/caddy/caddyhttp/httpserver"
+
+description = """
+Where the server is listening for multiple SNI names an attacker can
+complete a TLS handshake for a host name that does not require TLS
+client authentication and then send HTTP requests for a host name that
+does require TLS client authentication, thereby bypassing those checks.
+"""
+
+cve = "CVE-2018-21246"
+
+symbols = ["httpContext.MakeServers", "Server.serveHTTP", "assertConfigsCompatible"]
+
+[[versions]]
+fixed = "v0.10.13"
+
+[[additional_packages]]
+package = "github.com/mholt/caddy/caddyhttp/httpserver"
+symbols = ["httpContext.MakeServers", "Server.serveHTTP", "assertConfigsCompatible"]
+[[additional_packages.versions]]
+fixed = "v0.10.13"
+
+[links]
+commit = "https://github.com/caddyserver/caddy/commit/4d9ee000c8d2cbcdd8284007c1e0f2da7bc3c7c3"
+pr = "https://github.com/caddyserver/caddy/pull/2099"
+context = ["https://bugs.gentoo.org/715214"]
\ No newline at end of file
diff --git a/reports/GO-2020-0044.toml b/reports/GO-2020-0044.toml
new file mode 100644
index 0000000..f4f4747
--- /dev/null
+++ b/reports/GO-2020-0044.toml
@@ -0,0 +1,16 @@
+package = "github.com/beego/beego/session"
+
+description = """
+An attacker can craft a malicious URL which lead to XSS due to
+usage of unsantized user controlled URL parameters.
+"""
+
+credit = "@Kevil-hui"
+
+[[versions]]
+fixed = "v1.12.2"
+
+[links]
+commit = "https://github.com/beego/beego/commit/6eeea141d885c16c2a01349d57da62c6379a45a2"
+pr = "https://github.com/beego/beego/pull/4018"
+context = ["https://github.com/beego/beego/issues/3983"]
\ No newline at end of file
diff --git a/reports/GO-2020-0045.toml b/reports/GO-2020-0045.toml
new file mode 100644
index 0000000..e7a2e6e
--- /dev/null
+++ b/reports/GO-2020-0045.toml
@@ -0,0 +1,18 @@
+package = "github.com/dinever/golf"
+
+description = """
+CSRF tokens are generated using math/rand, making predicting their values
+relatively trivial.
+"""
+
+credit = "@elithrar"
+
+symbols = ["randomBytes"]
+
+[[versions]]
+fixed = "v0.3.0"
+
+[links]
+commit = "https://github.com/dinever/golf/commit/3776f338be48b5bc5e8cf9faff7851fc52a3f1fe"
+pr = "https://github.com/dinever/golf/pull/24"
+context = ["https://github.com/dinever/golf/issues/20"]
\ No newline at end of file
diff --git a/reports/GO-2020-0046.toml b/reports/GO-2020-0046.toml
new file mode 100644
index 0000000..9783732
--- /dev/null
+++ b/reports/GO-2020-0046.toml
@@ -0,0 +1,24 @@
+package = "github.com/russellhaering/goxmldsig"
+
+description = """
+An attacker can craft a malformed XML Digital Signature which when
+validated causes a panic due to nil pointer deference.
+"""
+
+cve = "CVE-2020-7711"
+
+credit = "@stevenjohnstone"
+
+symbols = ["ValidationContext.validateSignature"]
+
+[[versions]]
+fixed = "v1.1.0"
+
+[[additional_packages]]
+package = "github.com/russellhaering/gosaml2"
+symbols = ["SAMLServiceProvider.validateAssertionSignatures"]
+[[additional_packages.versions]]
+fixed = "v0.6.0"
+
+[links]
+context = ["https://github.com/russellhaering/goxmldsig/issues/48", "https://github.com/russellhaering/gosaml2/issues/59"]
\ No newline at end of file
diff --git a/reports/GO-2020-0047.toml b/reports/GO-2020-0047.toml
new file mode 100644
index 0000000..ac6adbe
--- /dev/null
+++ b/reports/GO-2020-0047.toml
@@ -0,0 +1,12 @@
+package = "github.com/RobotsAndPencils/go-saml"
+
+description = """
+XML Digital Signatures generated and validated using this package use
+SHA-1, as such an attacker may be able to craft input which cause
+hash collisions.
+"""
+
+symbols = ["AuthnRequest.Validate", "NewAuthnRequest", "NewSignedResponse"]
+
+[links]
+context = ["https://github.com/RobotsAndPencils/go-saml/pull/38"]
\ No newline at end of file
diff --git a/reports/GO-2020-0048.toml b/reports/GO-2020-0048.toml
new file mode 100644
index 0000000..b6a9834
--- /dev/null
+++ b/reports/GO-2020-0048.toml
@@ -0,0 +1,20 @@
+package = "github.com/antchfx/xmlquery"
+
+description = """
+[`LoadURL`] does not check the Content-Type of loaded resources,
+which can cause a panic due to nil pointer deference if the loaded
+resource is not XML.
+"""
+
+cve = "CVE-2020-25614"
+
+credit = "@dwisiswant0"
+
+symbols = ["LoadURL"]
+
+[[versions]]
+fixed = "v1.3.1"
+
+[links]
+commit = "https://github.com/antchfx/xmlquery/commit/5648b2f39e8d5d3fc903c45a4f1274829df71821"
+context = ["https://github.com/antchfx/xmlquery/issues/39"]
\ No newline at end of file
diff --git a/reports/GO-2020-0049.toml b/reports/GO-2020-0049.toml
new file mode 100644
index 0000000..93d877b
--- /dev/null
+++ b/reports/GO-2020-0049.toml
@@ -0,0 +1,16 @@
+package = "github.com/justinas/nosurf"
+
+description = """
+[`VerifyToken`] can be bypassed if the expected token contains malformed Base64.
+"""
+
+credit = "@aeneasr"
+
+symbols = ["VerifyToken", "verifyToken"]
+
+[[versions]]
+fixed = "v1.1.1"
+
+[links]
+commit = "https://github.com/justinas/nosurf/commit/4d86df7a4affa1fa50ab39fb09aac56c3ce9c314"
+pr = "https://github.com/justinas/nosurf/pull/60"
\ No newline at end of file
diff --git a/reports/GO-2020-0050.toml b/reports/GO-2020-0050.toml
new file mode 100644
index 0000000..ce396a0
--- /dev/null
+++ b/reports/GO-2020-0050.toml
@@ -0,0 +1,19 @@
+package = "github.com/russellhaering/goxmldsig"
+
+description = """
+An attacker can craft an XML file which will cause signature verification
+to be entirely bypassed.
+"""
+
+cve = "CVE-2020-15216"
+
+credit = "@jupenur"
+
+symbols = ["ValidationContext.findSignature"]
+
+[[versions]]
+fixed = "v1.1.0"
+
+[links]
+commit = "https://github.com/russellhaering/goxmldsig/commit/f6188febf0c29d7ffe26a0436212b19cb9615e64"
+context = ["https://github.com/russellhaering/goxmldsig/security/advisories/GHSA-q547-gmf8-8jr7"]
diff --git a/reports/GO-2021-0051.toml b/reports/GO-2021-0051.toml
new file mode 100644
index 0000000..65f36b9
--- /dev/null
+++ b/reports/GO-2021-0051.toml
@@ -0,0 +1,19 @@
+package = "github.com/labstack/echo/v4"
+
+description = """
+On Windows the static route handler does not properly santize the
+request path, allowing for directory traversal.
+"""
+
+credit = "@little-cui (Apache ServiceComb)"
+
+symbols = ["common.static"]
+
+os = ["windows"]
+
+[[versions]]
+fixed = "v4.1.18-0.20201215153152-4422e3b66b9f"
+
+[links]
+commit = "https://github.com/labstack/echo/commit/4422e3b66b9fd498ed1ae1d0242d660d0ed3faaa"
+pr = "https://github.com/labstack/echo/pull/1718"
\ No newline at end of file
diff --git a/reports/GO-2021-0052.toml b/reports/GO-2021-0052.toml
new file mode 100644
index 0000000..a40528c
--- /dev/null
+++ b/reports/GO-2021-0052.toml
@@ -0,0 +1,16 @@
+package = "github.com/gin-gonic/gin"
+
+description = """
+When used without an internet facing proxy, an adversary can spoof
+their IP address by setting the X-Forwarded-For header.
+"""
+
+cve = "CVE-2020-28483"
+
+credit = "@sorenh"
+
+symbols = ["Context.ClientIP"]
+
+[links]
+pr = "https://github.com/gin-gonic/gin/pull/2632"
+context = ["https://github.com/gin-gonic/gin/pull/2474"]
\ No newline at end of file
diff --git a/reports/GO-2021-0053.toml b/reports/GO-2021-0053.toml
new file mode 100644
index 0000000..0e9038f
--- /dev/null
+++ b/reports/GO-2021-0053.toml
@@ -0,0 +1,14 @@
+package = "github.com/gogo/protobuf"
+
+description = """
+Generated Unmarshal methods do not include proper index bounds validation,
+allowing a maliciously crafted message to cause an out-of-bounds panic.
+"""
+
+cve = "CVE-2021-3121"
+
+[[versions]]
+fixed = "v1.3.2"
+
+[links]
+commit = "https://github.com/gogo/protobuf/commit/b03c65ea87cdc3521ede29f62fe3ce239267c1bc"
\ No newline at end of file
diff --git a/reports/GO-2021-0054.toml b/reports/GO-2021-0054.toml
new file mode 100644
index 0000000..666ab5a
--- /dev/null
+++ b/reports/GO-2021-0054.toml
@@ -0,0 +1,18 @@
+package = "github.com/tidwall/gjson"
+
+description = """
+Maliciously crafted JSON messages can cause an out-of-bounds panic.
+"""
+
+cve = "CVE-2020-36067"
+
+credit = "@toptotu"
+
+symbols = ["unwrap"]
+
+[[versions]]
+fixed = "v1.6.6"
+
+[links]
+commit = "https://github.com/tidwall/gjson/commit/bf4efcb3c18d1825b2988603dea5909140a5302b"
+context = ["https://github.com/tidwall/gjson/issues/196"]
\ No newline at end of file
diff --git a/reports/GO-2021-0055.toml b/reports/GO-2021-0055.toml
new file mode 100644
index 0000000..5eecc81
--- /dev/null
+++ b/reports/GO-2021-0055.toml
@@ -0,0 +1,19 @@
+package = "github.com/dexidp/dex/connector/saml/v2"
+
+description = """
+An XML message can be maliciously crafted such that signature
+verification is bypassed.
+"""
+
+cve = "CVE-2020-15216"
+
+credit = "Juho Nurminen (Mattermost)"
+
+symbols = ["provider.HandlePOST"]
+
+[[versions]]
+fixed = "v2.27.0"
+
+[links]
+commit = "https://github.com/dexidp/dex/commit/324b1c886b407594196113a3dbddebe38eecd4e8"
+context = ["https://github.com/dexidp/dex/security/advisories/GHSA-m9hp-7r99-94h5"]
\ No newline at end of file
diff --git a/reports/GO-2021-0056.toml b/reports/GO-2021-0056.toml
new file mode 100644
index 0000000..6eef503
--- /dev/null
+++ b/reports/GO-2021-0056.toml
@@ -0,0 +1,19 @@
+package = "github.com/russellhaering/goxmldsig"
+
+description = """
+An XML message can be maliciously crafted such that signature
+verification is bypassed.
+"""
+
+cve = "CVE-2020-15216"
+
+credit = "Juho Nurminen (Mattermost)"
+
+symbols = ["ValidationContext.findSignature"]
+
+[[versions]]
+fixed = "v1.1.0"
+
+[links]
+commit = "https://github.com/dexidp/dex/commit/324b1c886b407594196113a3dbddebe38eecd4e8"
+context = ["https://github.com/russellhaering/goxmldsig/security/advisories/GHSA-q547-gmf8-8jr7"]
\ No newline at end of file
diff --git a/reports/GO-2021-0057.toml b/reports/GO-2021-0057.toml
new file mode 100644
index 0000000..8621e7d
--- /dev/null
+++ b/reports/GO-2021-0057.toml
@@ -0,0 +1,19 @@
+package = "github.com/buger/jsonparser"
+
+description = """
+Malicious input can cause an out-of-bounds panic.
+"""
+
+cve = "CVE-2020-35381"
+
+credit = "@toptotu"
+
+symbols = ["searchKeys"]
+
+[[versions]]
+fixed = "v1.1.1"
+
+[links]
+commit = "https://github.com/buger/jsonparser/commit/df3ea76ece10095374fd1c9a22a4fb85a44efc42"
+pr = "https://github.com/buger/jsonparser/pull/221"
+context = ["https://github.com/buger/jsonparser/issues/219"]
\ No newline at end of file
diff --git a/reports/GO-2021-0058.toml b/reports/GO-2021-0058.toml
new file mode 100644
index 0000000..cf32623
--- /dev/null
+++ b/reports/GO-2021-0058.toml
@@ -0,0 +1,36 @@
+package = "github.com/crewjam/saml"
+
+description = """
+An XML message can be maliciously crafted such that signature
+verification is bypassed.
+"""
+
+cve = "CVE-2020-27846"
+
+credit = ""
+
+symbols = [
+    "IdpAuthnRequest.Validate",
+    "ServiceProvider.ParseXMLResponse",
+    "ServiceProvider.ValidateLogoutResponseForm",
+    "ServiceProvider.ValidateLogoutResponseRedirect"
+]
+
+[[versions]]
+fixed = "v0.4.3"
+
+[[additional_packages]]
+package = "github.com/crewjam/saml/samlidp"
+smybols = ["getSPMetadata"]
+[[additional_packages.versions]]
+fixed = "v0.4.3"
+
+[[additional_packages]]
+package = "github.com/crewjam/saml/samlsp"
+smybols = ["ParseMetadata"]
+[[additional_packages.versions]]
+fixed = "v0.4.3"
+
+[links]
+commit = "https://github.com/crewjam/saml/commit/da4f1a0612c0a8dd0452cf8b3c7a6518f6b4d053"
+context = ["https://github.com/crewjam/saml/security/advisories/GHSA-4hq8-gmxx-h6w9"]
\ No newline at end of file
diff --git a/reports/GO-2021-0059.toml b/reports/GO-2021-0059.toml
new file mode 100644
index 0000000..cdd9707
--- /dev/null
+++ b/reports/GO-2021-0059.toml
@@ -0,0 +1,18 @@
+package = "github.com/tidwall/gjson"
+
+description = """
+Maliciously crafted JSON messages can cause an out-of-bounds panic.
+"""
+
+cve = "CVE-2020-35380"
+
+credit = "@toptotu"
+
+symbols = ["sqaush"]
+
+[[versions]]
+fixed = "v1.6.4"
+
+[links]
+commit = "https://github.com/tidwall/gjson/commit/f0ee9ebde4b619767ae4ac03e8e42addb530f6bc"
+context = ["https://github.com/tidwall/gjson/issues/192"]
\ No newline at end of file
diff --git a/reports/GO-2021-0060.toml b/reports/GO-2021-0060.toml
new file mode 100644
index 0000000..15bb121
--- /dev/null
+++ b/reports/GO-2021-0060.toml
@@ -0,0 +1,19 @@
+package = "github.com/russellhaering/gosaml2"
+
+description = """
+An XML message can be maliciously crafted such that signature
+verification is bypassed.
+"""
+
+cve = "CVE-2020-29509"
+
+credit = "Juho Nurminen"
+
+symbols = ["parseResponse"]
+
+[[versions]]
+fixed = "v0.6.0"
+
+[links]
+commit = "https://github.com/russellhaering/gosaml2/commit/42606dafba60c58c458f14f75c4c230459672ab9"
+context = ["https://github.com/russellhaering/gosaml2/security/advisories/GHSA-xhqq-x44f-9fgg"]
\ No newline at end of file
diff --git a/reports/GO-2021-0061.toml b/reports/GO-2021-0061.toml
new file mode 100644
index 0000000..9fb3059
--- /dev/null
+++ b/reports/GO-2021-0061.toml
@@ -0,0 +1,23 @@
+package = "gopkg.in/yaml.v2"
+
+description = """
+A maliciously crafted input can cause resource exhaustion due to
+alias chasing.
+"""
+
+credit = "@simonferquel"
+
+symbols = ["decoder.unmarshal"]
+
+[[versions]]
+fixed = "v2.2.3"
+
+[[additional_packages]]
+package = "github.com/go-yaml/yaml"
+symbols = ["decoder.unmarshal"]
+[[additional_packages.versions]]
+fixed = "v2.2.3"
+
+[links]
+commit = "https://github.com/go-yaml/yaml/commit/bb4e33bf68bf89cad44d386192cbed201f35b241"
+pr = "https://github.com/go-yaml/yaml/pull/375"
\ No newline at end of file
diff --git a/reports/GO-2021-0062.toml b/reports/GO-2021-0062.toml
new file mode 100644
index 0000000..1cc20c8
--- /dev/null
+++ b/reports/GO-2021-0062.toml
@@ -0,0 +1,30 @@
+package = "k8s.io/apiextensions-apiserver/pkg/apiserver"
+
+description = """
+A maliciously crafted YAML or JSON message can cause resource
+exhaustion.
+"""
+
+cve = "CVE-2019-11253"
+
+symbols = ["NewCustomResourceDefinitionHandler"]
+
+[[versions]]
+fixed = "v0.17.0"
+
+[[additional_packages]]
+package = "k8s.io/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver"
+symbols = ["NewCustomResourceDefinitionHandler"]
+[[additional_packages.versions]]
+fixed = "v1.17.0-alpha.2"
+
+[links]
+commit = "https://github.com/kubernetes/apiextensions-apiserver/commit/9cfd100448d12f999fbf913ae5d4fef2fcd66871"
+pr = "https://github.com/kubernetes/kubernetes/pull/83261"
+context = [
+    "https://github.com/kubernetes/kubernetes/issues/83253",
+    "https://gist.github.com/bgeesaman/0e0349e94cd22c48bf14d8a9b7d6b8f2"
+]
+
+# This is a really confusing one to classify becuase of how kubernetes
+# does their vendoring stuff.
\ No newline at end of file
diff --git a/reports/GO-2021-0063.toml b/reports/GO-2021-0063.toml
new file mode 100644
index 0000000..b8db91f
--- /dev/null
+++ b/reports/GO-2021-0063.toml
@@ -0,0 +1,19 @@
+package = "github.com/ethereum/go-ethereum/les"
+
+description = """
+A maliciously crafted RPC message can cause a panic due
+to a nil pointer dereference.
+"""
+
+cve = "CVE-2020-26264"
+
+credit = "@zsfelfoldi"
+
+symbols = ["serverHandler.handleMsg"]
+
+[[versions]]
+fixed = "v1.9.25"
+
+[links]
+commit = "https://github.com/ethereum/go-ethereum/commit/bddd103a9f0af27ef533f04e06ea429cf76b6d46"
+pr = "https://github.com/ethereum/go-ethereum/pull/21896"
\ No newline at end of file
diff --git a/reports/GO-2021-0064.toml b/reports/GO-2021-0064.toml
new file mode 100644
index 0000000..14d3a86
--- /dev/null
+++ b/reports/GO-2021-0064.toml
@@ -0,0 +1,29 @@
+package = "k8s.io/client-go/transport"
+
+description = """
+Authorization tokens may be inappropriately logged if the verbosity
+level is set to a debug level.
+"""
+
+cve = "CVE-2020-8565"
+
+credit = "@sfowl"
+
+symbols = ["requestInfo.toCurl"]
+
+[[versions]]
+fixed = "v0.20.0-alpha.2"
+
+[[additional_packages]]
+package = "k8s.io/kubernetes/staging/src/k8s.io/client-go/transport"
+symbols = ["requestInfo.toCurl"]
+[[additional_packages.versions]]
+fixed = "v1.20.0-alpha.2"
+
+[links]
+commit = "https://github.com/kubernetes/kubernetes/commit/e99df0e5a75eb6e86123b56d53e9b7ca0fd00419"
+pr = "https://github.com/kubernetes/kubernetes/pull/95316"
+context = ["https://github.com/kubernetes/kubernetes/issues/95623"]
+
+# This is a really confusing one to classify becuase of how kubernetes
+# does their vendoring stuff.
\ No newline at end of file
diff --git a/reports/GO-2021-0065.toml b/reports/GO-2021-0065.toml
new file mode 100644
index 0000000..1708a5d
--- /dev/null
+++ b/reports/GO-2021-0065.toml
@@ -0,0 +1,27 @@
+package = "k8s.io/client-go/transport"
+
+description = """
+Authorization tokens may be inappropriately logged if the verbosity
+level is set to a debug level.
+"""
+
+cve = "CVE-2019-11250"
+
+symbols = ["debuggingRoundTripper.RoundTrip"]
+
+[[versions]]
+fixed = "v0.17.0"
+
+[[additional_packages]]
+package = "k8s.io/kubernetes/staging/src/k8s.io/client-go/transport"
+symbols = ["debuggingRoundTripper.RoundTrip"]
+[[additional_packages.versions]]
+fixed = "v1.16.0-beta.1"
+
+[links]
+commit = "https://github.com/kubernetes/kubernetes/commit/4441f1d9c3e94d9a3d93b4f184a591cab02a5245"
+pr = "https://github.com/kubernetes/kubernetes/pull/81330"
+context = ["https://github.com/kubernetes/kubernetes/issues/81114"]
+
+# This is a really confusing one to classify becuase of how kubernetes
+# does their vendoring stuff.
\ No newline at end of file
diff --git a/reports/GO-2021-0066.toml b/reports/GO-2021-0066.toml
new file mode 100644
index 0000000..5e4a330
--- /dev/null
+++ b/reports/GO-2021-0066.toml
@@ -0,0 +1,23 @@
+package = "k8s.io/kubernetes/pkg/credentialprovider"
+
+description = """
+Attempting to read a malformed .dockercfg may cause secrets to be
+inappropriately logged.
+"""
+
+cve = "CVE-2020-8564"
+
+credit = "@sfowl"
+
+symbols = ["readDockerConfigFileFromBytes", "readDockerConfigJSONFileFromBytes"]
+
+[[versions]]
+fixed = "v1.20.0-alpha.1"
+
+[links]
+commit = "https://github.com/kubernetes/kubernetes/commit/11793434dac97a49bfed0150b56ac63e5dc34634"
+pr = "https://github.com/kubernetes/kubernetes/pull/94712"
+context = ["https://github.com/kubernetes/kubernetes/issues/95622"]
+
+# This is a really confusing one to classify becuase of how kubernetes
+# does their vendoring stuff.
\ No newline at end of file
diff --git a/reports/GO-2021-0067.toml b/reports/GO-2021-0067.toml
new file mode 100644
index 0000000..d23b609
--- /dev/null
+++ b/reports/GO-2021-0067.toml
@@ -0,0 +1,21 @@
+package = "archive/zip"
+
+stdlib = true
+
+description = """
+Using Reader.Open on an archive containing a file with a path
+prefixed by "../" will cause a panic due to a stack overflow.
+"""
+
+cve = "CVE-2021-27919"
+
+symbols = ["toValidName"]
+
+[[versions]]
+introduced = "go1.16"
+fixed = "go1.16.1"
+
+[links]
+commit = "https://github.com/golang/go/commit/cd3b4ca9f20fd14187ed4cdfdee1a02ea87e5cd8"
+pr = "https://go-review.googlesource.com/c/go/+/300489"
+context = ["https://github.com/golang/go/issues/44916"]
\ No newline at end of file
diff --git a/reports/GO-2021-0068.toml b/reports/GO-2021-0068.toml
new file mode 100644
index 0000000..b58bfb0
--- /dev/null
+++ b/reports/GO-2021-0068.toml
@@ -0,0 +1,31 @@
+package = "cmd/go"
+
+stdlib = true
+do_not_export = true
+
+description = """
+The go command may execute arbitrary code at build time when using cgo on Windows.
+This can be triggered by running go get on a malicious module, or any other time
+the code is built.
+"""
+
+os = ["windows"]
+
+cve = "CVE-2021-3115"
+
+credit = "RyotaK"
+
+[[versions]]
+fixed = "go1.14.14"
+
+[[versions]]
+fixed = "go1.15.7"
+
+[links]
+commit = "https://github.com/golang/go/commit/953d1feca9b21af075ad5fc8a3dad096d3ccc3a0"
+pr = "https://golang.org/cl/284783"
+context = [
+    "https://github.com/golang/go/issues/43783",
+    "https://golang.org/cl/284780",
+    "https://github.com/golang/go/commit/46e2e2e9d99925bbf724b12693c6d3e27a95d6a0"
+]
\ No newline at end of file
diff --git a/reports/GO-2021-0069.toml b/reports/GO-2021-0069.toml
new file mode 100644
index 0000000..44a9efb
--- /dev/null
+++ b/reports/GO-2021-0069.toml
@@ -0,0 +1,25 @@
+package = "math/big"
+
+stdlib = true
+
+description = """
+A number of math/big.Int methods can panic when provided large inputs due
+to a flawed division method.
+"""
+
+cve = "CVE-2020-28362"
+
+symbols = ["nat.divRecursiveStep"]
+
+[[versions]]
+introduced = "go1.14"
+fixed = "go1.14.12"
+
+[[versions]]
+introduced = "go1.15"
+fixed = "go1.15.5"
+
+[links]
+commit = "https://github.com/golang/go/commit/1e1fa5903b760c6714ba17e50bf850b01f49135c"
+pr = "https://go-review.googlesource.com/c/go/+/269657"
+context = ["https://github.com/golang/go/issues/42552"]
\ No newline at end of file
diff --git a/template b/template
new file mode 100644
index 0000000..2de7929
--- /dev/null
+++ b/template
@@ -0,0 +1,20 @@
+package = ""
+
+description = """
+
+"""
+
+cve = ""
+
+credit = ""
+
+symbols = [""]
+
+[[versions]]
+introduced = ""
+fixed = ""
+
+[links]
+commit = ""
+pr = ""
+context = [""]
\ No newline at end of file