1package nvd 2 3import ( 4 "fmt" 5 "strconv" 6 "strings" 7 8 c "github.com/kotakanbe/go-cve-dictionary/config" 9 "github.com/kotakanbe/go-cve-dictionary/db" 10 "github.com/kotakanbe/go-cve-dictionary/fetcher" 11 log "github.com/kotakanbe/go-cve-dictionary/log" 12 "github.com/kotakanbe/go-cve-dictionary/models" 13) 14 15// ListFetchedFeeds list fetched feeds information 16func ListFetchedFeeds(driver db.DB) (jsonMetas []models.FeedMeta, err error) { 17 lastMetas, err := driver.GetFetchedFeedMetas() 18 if err != nil { 19 return nil, fmt.Errorf("Failed to get Meta: %s", err) 20 } 21 if len(lastMetas) == 0 { 22 log.Infof("No feeds found") 23 return 24 } 25 26 jsonYears := map[int]bool{} 27 for _, meta := range lastMetas { 28 if !checkNvdURL(meta.URL) { 29 continue 30 } 31 y, err := nvdFeedURLToYear(meta.URL) 32 if err != nil { 33 return nil, err 34 } 35 jsonYears[y] = true 36 } 37 38 jsonY := []int{} 39 for y := range jsonYears { 40 jsonY = append(jsonY, y) 41 } 42 43 jsonMetas, err = FetchLatestFeedMeta(driver, jsonY) 44 if err != nil { 45 return nil, err 46 } 47 return 48} 49 50func checkNvdURL(url string) bool { 51 return strings.Contains(url, "nvdcve-") 52} 53 54func nvdFeedURLToYear(url string) (year int, err error) { 55 //TODO use meta.Year() 56 yearstr := "" 57 if strings.Contains(url, "nvdcve-1.1-") { 58 yearstr = strings.TrimSuffix(strings.Split(url, "nvdcve-1.1-")[1], ".json.gz") 59 } else { 60 return year, fmt.Errorf("Failed to parse URL: %s", url) 61 } 62 63 switch yearstr { 64 case "recent", "modified": 65 return c.Latest, nil 66 default: 67 y, err := strconv.Atoi(yearstr) 68 if err != nil { 69 return 0, fmt.Errorf("Unable conver to int: %d, err: %s", 70 year, err) 71 } 72 return y, nil 73 } 74} 75 76// FetchLatestFeedMeta fetches CVE meta information from NVD 77func FetchLatestFeedMeta(driver db.DB, years []int) (metas []models.FeedMeta, err error) { 78 reqs := []fetcher.FetchRequest{} 79 for _, year := range years { 80 urls := MakeNvdMetaURLs(year) 81 for _, url := range urls { 82 reqs = append(reqs, fetcher.FetchRequest{ 83 Year: year, 84 URL: url, 85 }) 86 } 87 } 88 results, err := fetcher.FetchFeedFiles(reqs) 89 if err != nil { 90 return nil, fmt.Errorf("Failed to fetch. err: %s", err) 91 } 92 93 for _, res := range results { 94 str := string(res.Body) 95 ss := strings.Split(str, "\r\n") 96 if len(ss) != 6 { 97 continue 98 } 99 hash := ss[4] 100 101 url := "" 102 url = strings.Replace(res.URL, ".meta", ".json.gz", -1) 103 104 meta, err := driver.GetFetchedFeedMeta(url) 105 if err != nil { 106 return nil, fmt.Errorf("Failed to get meta: %d, err: %s", 107 res.Year, err) 108 } 109 meta.URL = url 110 meta.LatestHash = hash 111 meta.LatestLastModifiedDate = strings.TrimPrefix(ss[0], "lastModifiedDate:") 112 metas = append(metas, *meta) 113 } 114 return 115} 116 117// UpdateMeta updates meta table 118func UpdateMeta(driver db.DB, metas []models.FeedMeta) error { 119 for _, meta := range metas { 120 meta.Hash = meta.LatestHash 121 meta.LastModifiedDate = meta.LatestLastModifiedDate 122 err := driver.UpsertFeedHash(meta) 123 if err != nil { 124 return fmt.Errorf("Failed to updte meta: %s, err: %s", 125 meta.URL, err) 126 } 127 } 128 return nil 129} 130 131// MakeNvdMetaURLs returns a URL of NVD Feed 132func MakeNvdMetaURLs(year int) (url []string) { 133 formatTemplate := "https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-%s.meta" 134 if year == c.Latest { 135 for _, name := range []string{"modified", "recent"} { 136 url = append(url, fmt.Sprintf(formatTemplate, name)) 137 } 138 } else { 139 feed := strconv.Itoa(year) 140 url = append(url, fmt.Sprintf(formatTemplate, feed)) 141 } 142 return 143} 144