cvefeed/dictionary.go (79 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cvefeed
import (
"errors"
"fmt"
"os"
"strings"
"sync"
)
// Dictionary is a slice of entries
type Dictionary map[string]Vuln
// Override amends entries in Dictionary with configurations from Dictionary d2;
// CVE will be matched if it matches the original config of d and does not match the config of d2.
func (d *Dictionary) Override(d2 Dictionary) {
if d == nil {
return
}
if *d == nil {
*d = make(Dictionary)
}
for k, cve := range d2 {
if _, ok := (*d)[k]; ok {
(*d)[k] = OverrideVuln((*d)[k], cve)
}
}
}
// LoadJSONDictionary parses dictionary from multiple NVD vulnerability feed JSON files
func LoadJSONDictionary(paths ...string) (Dictionary, error) {
return LoadFeed(loadJSONFile, paths...)
}
// LoadFeed calls loadFunc for each file in paths and returns the combined outputs in a Dictionary.
func LoadFeed(loadFunc func(string) ([]Vuln, error), paths ...string) (Dictionary, error) {
dict := make(Dictionary)
var wg sync.WaitGroup
done := make(chan struct{})
errDone := make(chan struct{})
dictChan := make(chan []Vuln, 1)
errChan := make(chan error, 1)
for _, path := range paths {
wg.Add(1)
go func(path string) {
defer wg.Done()
feed, err := loadFunc(path)
if err != nil {
errChan <- fmt.Errorf("dictionary: failed to load feed %q: %v", path, err)
return
}
dictChan <- feed
}(path)
}
go func() {
for d := range dictChan {
for _, cve := range d {
if cveid := cve.ID(); cveid != "" {
dict[cveid] = cve
}
}
}
close(done)
}()
var errs []string
go func() {
for e := range errChan {
errs = append(errs, e.Error())
}
close(errDone)
}()
wg.Wait()
close(dictChan)
close(errChan)
<-done
<-errDone
if len(errs) > 0 {
return dict, errors.New(strings.Join(errs, "\n"))
}
return dict, nil
}
// loadJSONFile parses dictionary from NVD vulnerability feed JSON file
func loadJSONFile(path string) ([]Vuln, error) {
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("dictionary: failed to load feed %q: %v", path, err)
}
defer f.Close()
return ParseJSON(f)
}