121 lines
2.5 KiB
Go
121 lines
2.5 KiB
Go
|
|
// Copyright 2023 The Go Authors. All rights reserved.
|
||
|
|
// Use of this source code is governed by a BSD-style
|
||
|
|
// license that can be found in the LICENSE file.
|
||
|
|
|
||
|
|
package client
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"fmt"
|
||
|
|
"io/fs"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
|
||
|
|
"golang.org/x/vuln/internal/osv"
|
||
|
|
isem "golang.org/x/vuln/internal/semver"
|
||
|
|
)
|
||
|
|
|
||
|
|
// indexFromDir returns a raw index created from a directory
|
||
|
|
// containing OSV entries.
|
||
|
|
// It skips any non-JSON files but errors if any of the JSON files
|
||
|
|
// cannot be unmarshaled into OSV, or have a filename other than <ID>.json.
|
||
|
|
func indexFromDir(dir string) (map[string][]byte, error) {
|
||
|
|
idx := newIndex()
|
||
|
|
f := os.DirFS(dir)
|
||
|
|
|
||
|
|
if err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
|
||
|
|
fname := d.Name()
|
||
|
|
ext := filepath.Ext(fname)
|
||
|
|
switch {
|
||
|
|
case err != nil:
|
||
|
|
return err
|
||
|
|
case d.IsDir():
|
||
|
|
return nil
|
||
|
|
case ext != ".json":
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
|
||
|
|
b, err := fs.ReadFile(f, d.Name())
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
var entry osv.Entry
|
||
|
|
if err := json.Unmarshal(b, &entry); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
if fname != entry.ID+".json" {
|
||
|
|
return fmt.Errorf("OSV entries must have filename of the form <ID>.json, got %s", fname)
|
||
|
|
}
|
||
|
|
|
||
|
|
idx.add(&entry)
|
||
|
|
return nil
|
||
|
|
}); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return idx.raw()
|
||
|
|
}
|
||
|
|
|
||
|
|
func indexFromEntries(entries []*osv.Entry) (map[string][]byte, error) {
|
||
|
|
idx := newIndex()
|
||
|
|
|
||
|
|
for _, entry := range entries {
|
||
|
|
idx.add(entry)
|
||
|
|
}
|
||
|
|
|
||
|
|
return idx.raw()
|
||
|
|
}
|
||
|
|
|
||
|
|
type index struct {
|
||
|
|
db *dbMeta
|
||
|
|
modules modulesIndex
|
||
|
|
}
|
||
|
|
|
||
|
|
func newIndex() *index {
|
||
|
|
return &index{
|
||
|
|
db: &dbMeta{},
|
||
|
|
modules: make(map[string]*moduleMeta),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (i *index) add(entry *osv.Entry) {
|
||
|
|
// Add to db index.
|
||
|
|
if entry.Modified.After(i.db.Modified) {
|
||
|
|
i.db.Modified = entry.Modified
|
||
|
|
}
|
||
|
|
// Add to modules index.
|
||
|
|
for _, affected := range entry.Affected {
|
||
|
|
modulePath := affected.Module.Path
|
||
|
|
if _, ok := i.modules[modulePath]; !ok {
|
||
|
|
i.modules[modulePath] = &moduleMeta{
|
||
|
|
Path: modulePath,
|
||
|
|
Vulns: []moduleVuln{},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
module := i.modules[modulePath]
|
||
|
|
module.Vulns = append(module.Vulns, moduleVuln{
|
||
|
|
ID: entry.ID,
|
||
|
|
Modified: entry.Modified,
|
||
|
|
Fixed: isem.NonSupersededFix(affected.Ranges),
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (i *index) raw() (map[string][]byte, error) {
|
||
|
|
data := make(map[string][]byte)
|
||
|
|
|
||
|
|
b, err := json.Marshal(i.db)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
data[dbEndpoint] = b
|
||
|
|
|
||
|
|
b, err = json.Marshal(i.modules)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
data[modulesEndpoint] = b
|
||
|
|
|
||
|
|
return data, nil
|
||
|
|
}
|