index.go (2513B)
1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package client 6 7 import ( 8 "encoding/json" 9 "fmt" 10 "io/fs" 11 "os" 12 "path/filepath" 13 14 "golang.org/x/vuln/internal/osv" 15 isem "golang.org/x/vuln/internal/semver" 16 ) 17 18 // indexFromDir returns a raw index created from a directory 19 // containing OSV entries. 20 // It skips any non-JSON files but errors if any of the JSON files 21 // cannot be unmarshaled into OSV, or have a filename other than <ID>.json. 22 func indexFromDir(dir string) (map[string][]byte, error) { 23 idx := newIndex() 24 f := os.DirFS(dir) 25 26 if err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error { 27 fname := d.Name() 28 ext := filepath.Ext(fname) 29 switch { 30 case err != nil: 31 return err 32 case d.IsDir(): 33 return nil 34 case ext != ".json": 35 return nil 36 } 37 38 b, err := fs.ReadFile(f, d.Name()) 39 if err != nil { 40 return err 41 } 42 var entry osv.Entry 43 if err := json.Unmarshal(b, &entry); err != nil { 44 return err 45 } 46 if fname != entry.ID+".json" { 47 return fmt.Errorf("OSV entries must have filename of the form <ID>.json, got %s", fname) 48 } 49 50 idx.add(&entry) 51 return nil 52 }); err != nil { 53 return nil, err 54 } 55 56 return idx.raw() 57 } 58 59 func indexFromEntries(entries []*osv.Entry) (map[string][]byte, error) { 60 idx := newIndex() 61 62 for _, entry := range entries { 63 idx.add(entry) 64 } 65 66 return idx.raw() 67 } 68 69 type index struct { 70 db *dbMeta 71 modules modulesIndex 72 } 73 74 func newIndex() *index { 75 return &index{ 76 db: &dbMeta{}, 77 modules: make(map[string]*moduleMeta), 78 } 79 } 80 81 func (i *index) add(entry *osv.Entry) { 82 // Add to db index. 83 if entry.Modified.After(i.db.Modified) { 84 i.db.Modified = entry.Modified 85 } 86 // Add to modules index. 87 for _, affected := range entry.Affected { 88 modulePath := affected.Module.Path 89 if _, ok := i.modules[modulePath]; !ok { 90 i.modules[modulePath] = &moduleMeta{ 91 Path: modulePath, 92 Vulns: []moduleVuln{}, 93 } 94 } 95 module := i.modules[modulePath] 96 module.Vulns = append(module.Vulns, moduleVuln{ 97 ID: entry.ID, 98 Modified: entry.Modified, 99 Fixed: isem.NonSupersededFix(affected.Ranges), 100 }) 101 } 102 } 103 104 func (i *index) raw() (map[string][]byte, error) { 105 data := make(map[string][]byte) 106 107 b, err := json.Marshal(i.db) 108 if err != nil { 109 return nil, err 110 } 111 data[dbEndpoint] = b 112 113 b, err = json.Marshal(i.modules) 114 if err != nil { 115 return nil, err 116 } 117 data[modulesEndpoint] = b 118 119 return data, nil 120 }