Unify the models of NVD, JVN, OVAL
This commit is contained in:
		
							
								
								
									
										267
									
								
								models/models.go
									
									
									
									
									
								
							
							
						
						
									
										267
									
								
								models/models.go
									
									
									
									
									
								
							@@ -20,12 +20,12 @@ package models
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/future-architect/vuls/config"
 | 
			
		||||
	"github.com/future-architect/vuls/cveapi"
 | 
			
		||||
	cve "github.com/kotakanbe/go-cve-dictionary/models"
 | 
			
		||||
	goval "github.com/kotakanbe/goval-dictionary/models"
 | 
			
		||||
	cvedict "github.com/kotakanbe/go-cve-dictionary/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ScanResults is slice of ScanResult.
 | 
			
		||||
@@ -73,8 +73,7 @@ type ScanResult struct {
 | 
			
		||||
	Optional [][]interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FillCveDetail fetches CVE detailed information from
 | 
			
		||||
// CVE Database, and then set to fields.
 | 
			
		||||
// FillCveDetail fetches NVD, JVN from CVE Database, and then set to fields.
 | 
			
		||||
func (r ScanResult) FillCveDetail() (*ScanResult, error) {
 | 
			
		||||
	set := map[string]VulnInfo{}
 | 
			
		||||
	var cveIDs []string
 | 
			
		||||
@@ -90,35 +89,45 @@ func (r ScanResult) FillCveDetail() (*ScanResult, error) {
 | 
			
		||||
 | 
			
		||||
	r.IgnoredCves = CveInfos{}
 | 
			
		||||
	for _, d := range ds {
 | 
			
		||||
		nvd := *r.convertNvdToModel(d.CveID, d.Nvd)
 | 
			
		||||
		jvn := *r.convertJvnToModel(d.CveID, d.Jvn)
 | 
			
		||||
		cinfo := CveInfo{
 | 
			
		||||
			CveDetail: d,
 | 
			
		||||
			VulnInfo:  set[d.CveID],
 | 
			
		||||
			CveContents: []CveContent{nvd, jvn},
 | 
			
		||||
			VulnInfo:    set[d.CveID],
 | 
			
		||||
		}
 | 
			
		||||
		cinfo.NilSliceToEmpty()
 | 
			
		||||
 | 
			
		||||
		// ignored
 | 
			
		||||
		found := false
 | 
			
		||||
		ignore := false
 | 
			
		||||
		for _, icve := range config.Conf.Servers[r.ServerName].IgnoreCves {
 | 
			
		||||
			if icve == d.CveID {
 | 
			
		||||
				r.IgnoredCves.Insert(cinfo)
 | 
			
		||||
				found = true
 | 
			
		||||
				ignore = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if found {
 | 
			
		||||
		if ignore {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Update known if KnownCves already have cinfo
 | 
			
		||||
		if c, ok := r.KnownCves.Get(cinfo.CveID); ok {
 | 
			
		||||
			c.CveDetail = d
 | 
			
		||||
			for _, con := range []CveContent{nvd, jvn} {
 | 
			
		||||
				if !c.Update(con) {
 | 
			
		||||
					c.Insert(con)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			r.KnownCves.Update(c)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Update unknown if UnknownCves already have cinfo
 | 
			
		||||
		if c, ok := r.UnknownCves.Get(cinfo.CveID); ok {
 | 
			
		||||
			c.CveDetail = d
 | 
			
		||||
			for _, con := range []CveContent{nvd, jvn} {
 | 
			
		||||
				if !c.Update(con) {
 | 
			
		||||
					c.Insert(con)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			r.UnknownCves.Update(c)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
@@ -138,6 +147,93 @@ func (r ScanResult) FillCveDetail() (*ScanResult, error) {
 | 
			
		||||
	return &r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r ScanResult) convertNvdToModel(cveID string, nvd cvedict.Nvd) *CveContent {
 | 
			
		||||
	var cpes []Cpe
 | 
			
		||||
	for _, c := range nvd.Cpes {
 | 
			
		||||
		cpes = append(cpes, Cpe{CpeName: c.CpeName})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var refs []Reference
 | 
			
		||||
	for _, r := range nvd.References {
 | 
			
		||||
		refs = append(refs, Reference{
 | 
			
		||||
			Link:   r.Link,
 | 
			
		||||
			Source: r.Source,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	validVec := true
 | 
			
		||||
	for _, v := range []string{
 | 
			
		||||
		nvd.AccessVector,
 | 
			
		||||
		nvd.AccessComplexity,
 | 
			
		||||
		nvd.Authentication,
 | 
			
		||||
		nvd.ConfidentialityImpact,
 | 
			
		||||
		nvd.IntegrityImpact,
 | 
			
		||||
		nvd.AvailabilityImpact,
 | 
			
		||||
	} {
 | 
			
		||||
		if len(v) == 0 {
 | 
			
		||||
			validVec = false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vector := ""
 | 
			
		||||
	if validVec {
 | 
			
		||||
		vector = fmt.Sprintf("AV:%s/AC:%s/Au:%s/C:%s/I:%s/A:%s",
 | 
			
		||||
			string(nvd.AccessVector[0]),
 | 
			
		||||
			string(nvd.AccessComplexity[0]),
 | 
			
		||||
			string(nvd.Authentication[0]),
 | 
			
		||||
			string(nvd.ConfidentialityImpact[0]),
 | 
			
		||||
			string(nvd.IntegrityImpact[0]),
 | 
			
		||||
			string(nvd.AvailabilityImpact[0]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//TODO CVSSv3
 | 
			
		||||
	return &CveContent{
 | 
			
		||||
		Type:         NVD,
 | 
			
		||||
		CveID:        cveID,
 | 
			
		||||
		Summary:      nvd.Summary,
 | 
			
		||||
		Cvss2Score:   nvd.Score,
 | 
			
		||||
		Cvss2Vector:  vector,
 | 
			
		||||
		Cpes:         cpes,
 | 
			
		||||
		CweID:        nvd.CweID,
 | 
			
		||||
		References:   refs,
 | 
			
		||||
		Published:    nvd.PublishedDate,
 | 
			
		||||
		LastModified: nvd.LastModifiedDate,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r ScanResult) convertJvnToModel(cveID string, jvn cvedict.Jvn) *CveContent {
 | 
			
		||||
	var cpes []Cpe
 | 
			
		||||
	for _, c := range jvn.Cpes {
 | 
			
		||||
		cpes = append(cpes, Cpe{CpeName: c.CpeName})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	refs := []Reference{{
 | 
			
		||||
		Link:   jvn.JvnLink,
 | 
			
		||||
		Source: string(JVN),
 | 
			
		||||
	}}
 | 
			
		||||
	for _, r := range jvn.References {
 | 
			
		||||
		refs = append(refs, Reference{
 | 
			
		||||
			Link:   r.Link,
 | 
			
		||||
			Source: r.Source,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vector := strings.TrimSuffix(strings.TrimPrefix(jvn.Vector, "("), ")")
 | 
			
		||||
	return &CveContent{
 | 
			
		||||
		Type:         JVN,
 | 
			
		||||
		CveID:        cveID,
 | 
			
		||||
		Title:        jvn.Title,
 | 
			
		||||
		Summary:      jvn.Summary,
 | 
			
		||||
		Severity:     jvn.Severity,
 | 
			
		||||
		Cvss2Score:   jvn.Score,
 | 
			
		||||
		Cvss2Vector:  vector,
 | 
			
		||||
		Cpes:         cpes,
 | 
			
		||||
		References:   refs,
 | 
			
		||||
		Published:    jvn.PublishedDate,
 | 
			
		||||
		LastModified: jvn.LastModifiedDate,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FilterByCvssOver is filter function.
 | 
			
		||||
func (r ScanResult) FilterByCvssOver() ScanResult {
 | 
			
		||||
	cveInfos := []CveInfo{}
 | 
			
		||||
@@ -147,7 +243,7 @@ func (r ScanResult) FilterByCvssOver() ScanResult {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, cveInfo := range r.KnownCves {
 | 
			
		||||
		if config.Conf.CvssScoreOver <= cveInfo.CveDetail.CvssScore(config.Conf.Lang) {
 | 
			
		||||
		if config.Conf.CvssScoreOver <= cveInfo.CvssV2Score() {
 | 
			
		||||
			cveInfos = append(cveInfos, cveInfo)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -217,7 +313,7 @@ func (r ScanResult) CveSummary() string {
 | 
			
		||||
	var high, medium, low, unknown int
 | 
			
		||||
	cves := append(r.KnownCves, r.UnknownCves...)
 | 
			
		||||
	for _, cveInfo := range cves {
 | 
			
		||||
		score := cveInfo.CveDetail.CvssScore(config.Conf.Lang)
 | 
			
		||||
		score := cveInfo.CvssV2Score()
 | 
			
		||||
		switch {
 | 
			
		||||
		case 7.0 <= score:
 | 
			
		||||
			high++
 | 
			
		||||
@@ -379,11 +475,10 @@ func (c CveInfos) Swap(i, j int) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c CveInfos) Less(i, j int) bool {
 | 
			
		||||
	lang := config.Conf.Lang
 | 
			
		||||
	if c[i].CveDetail.CvssScore(lang) == c[j].CveDetail.CvssScore(lang) {
 | 
			
		||||
		return c[i].CveDetail.CveID < c[j].CveDetail.CveID
 | 
			
		||||
	if c[i].CvssV2Score() == c[j].CvssV2Score() {
 | 
			
		||||
		return c[i].CveID < c[j].CveID
 | 
			
		||||
	}
 | 
			
		||||
	return c[j].CveDetail.CvssScore(lang) < c[i].CveDetail.CvssScore(lang)
 | 
			
		||||
	return c[j].CvssV2Score() < c[i].CvssV2Score()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get cveInfo by cveID
 | 
			
		||||
@@ -431,27 +526,120 @@ func (c *CveInfos) Upsert(cveInfo CveInfo) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveInfo has Cve Information.
 | 
			
		||||
// CveInfo has CVE detailed Information.
 | 
			
		||||
type CveInfo struct {
 | 
			
		||||
	CveDetail  cve.CveDetail
 | 
			
		||||
	OvalDetail goval.Definition
 | 
			
		||||
	VulnInfo
 | 
			
		||||
	CveContents []CveContent
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get a CveContent specified by arg
 | 
			
		||||
func (c *CveInfo) Get(typestr CveContentType) (*CveContent, bool) {
 | 
			
		||||
	for _, cont := range c.CveContents {
 | 
			
		||||
		if cont.Type == typestr {
 | 
			
		||||
			return &cont, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return &CveContent{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Insert a CveContent to specified by arg
 | 
			
		||||
func (c *CveInfo) Insert(con CveContent) {
 | 
			
		||||
	c.CveContents = append(c.CveContents, con)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update a CveContent to specified by arg
 | 
			
		||||
func (c *CveInfo) Update(to CveContent) bool {
 | 
			
		||||
	for i, cont := range c.CveContents {
 | 
			
		||||
		if cont.Type == to.Type {
 | 
			
		||||
			c.CveContents[i] = to
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CvssV2Score returns CVSS V2 Score
 | 
			
		||||
func (c *CveInfo) CvssV2Score() float64 {
 | 
			
		||||
	//TODO
 | 
			
		||||
	if cont, found := c.Get(NVD); found {
 | 
			
		||||
		return cont.Cvss2Score
 | 
			
		||||
	} else if cont, found := c.Get(JVN); found {
 | 
			
		||||
		return cont.Cvss2Score
 | 
			
		||||
	} else if cont, found := c.Get(RedHat); found {
 | 
			
		||||
		return cont.Cvss2Score
 | 
			
		||||
	}
 | 
			
		||||
	return -1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NilSliceToEmpty set nil slice fields to empty slice to avoid null in JSON
 | 
			
		||||
func (c *CveInfo) NilSliceToEmpty() {
 | 
			
		||||
	if c.CveDetail.Nvd.Cpes == nil {
 | 
			
		||||
		c.CveDetail.Nvd.Cpes = []cve.Cpe{}
 | 
			
		||||
	}
 | 
			
		||||
	if c.CveDetail.Jvn.Cpes == nil {
 | 
			
		||||
		c.CveDetail.Jvn.Cpes = []cve.Cpe{}
 | 
			
		||||
	}
 | 
			
		||||
	if c.CveDetail.Nvd.References == nil {
 | 
			
		||||
		c.CveDetail.Nvd.References = []cve.Reference{}
 | 
			
		||||
	}
 | 
			
		||||
	if c.CveDetail.Jvn.References == nil {
 | 
			
		||||
		c.CveDetail.Jvn.References = []cve.Reference{}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
	// TODO
 | 
			
		||||
	//  if c.CveDetail.Nvd.Cpes == nil {
 | 
			
		||||
	//      c.CveDetail.Nvd.Cpes = []cve.Cpe{}
 | 
			
		||||
	//  }
 | 
			
		||||
	//  if c.CveDetail.Jvn.Cpes == nil {
 | 
			
		||||
	//      c.CveDetail.Jvn.Cpes = []cve.Cpe{}
 | 
			
		||||
	//  }
 | 
			
		||||
	//  if c.CveDetail.Nvd.References == nil {
 | 
			
		||||
	//      c.CveDetail.Nvd.References = []cve.Reference{}
 | 
			
		||||
	//  }
 | 
			
		||||
	//  if c.CveDetail.Jvn.References == nil {
 | 
			
		||||
	//      c.CveDetail.Jvn.References = []cve.Reference{}
 | 
			
		||||
	//  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CveContentType is a source of CVE information
 | 
			
		||||
type CveContentType string
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// NVD is NVD
 | 
			
		||||
	NVD CveContentType = "nvd"
 | 
			
		||||
 | 
			
		||||
	// JVN is JVN
 | 
			
		||||
	JVN CveContentType = "jvn"
 | 
			
		||||
 | 
			
		||||
	// RedHat is RedHat
 | 
			
		||||
	RedHat CveContentType = "redhat"
 | 
			
		||||
 | 
			
		||||
	// CentOS is CentOS
 | 
			
		||||
	CentOS CveContentType = "centos"
 | 
			
		||||
 | 
			
		||||
	// Debian is Debian
 | 
			
		||||
	Debian CveContentType = "debian"
 | 
			
		||||
 | 
			
		||||
	// Ubuntu is Ubuntu
 | 
			
		||||
	Ubuntu CveContentType = "ubuntu"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// CveContent has abstraction of various vulnerability information
 | 
			
		||||
type CveContent struct {
 | 
			
		||||
	Type         CveContentType
 | 
			
		||||
	CveID        string
 | 
			
		||||
	Title        string
 | 
			
		||||
	Summary      string
 | 
			
		||||
	Severity     string
 | 
			
		||||
	Cvss2Score   float64
 | 
			
		||||
	Cvss2Vector  string
 | 
			
		||||
	Cvss3Score   float64
 | 
			
		||||
	Cvss3Vector  string
 | 
			
		||||
	Cpes         []Cpe
 | 
			
		||||
	References   []Reference
 | 
			
		||||
	CweID        string
 | 
			
		||||
	Published    time.Time
 | 
			
		||||
	LastModified time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cpe is Common Platform Enumeration
 | 
			
		||||
type Cpe struct {
 | 
			
		||||
	CpeName string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reference has a related link of the CVE
 | 
			
		||||
type Reference struct {
 | 
			
		||||
	RefID  string
 | 
			
		||||
	Source string
 | 
			
		||||
	Link   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageInfoList is slice of PackageInfo
 | 
			
		||||
@@ -552,13 +740,14 @@ func (a PackageInfosByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
 | 
			
		||||
 | 
			
		||||
// PackageInfo has installed packages.
 | 
			
		||||
type PackageInfo struct {
 | 
			
		||||
	Name       string
 | 
			
		||||
	Version    string
 | 
			
		||||
	Release    string
 | 
			
		||||
	NewVersion string
 | 
			
		||||
	NewRelease string
 | 
			
		||||
	Repository string
 | 
			
		||||
	Changelog  Changelog
 | 
			
		||||
	Name        string
 | 
			
		||||
	Version     string
 | 
			
		||||
	Release     string
 | 
			
		||||
	NewVersion  string
 | 
			
		||||
	NewRelease  string
 | 
			
		||||
	Repository  string
 | 
			
		||||
	Changelog   Changelog
 | 
			
		||||
	NotFixedYet bool // Ubuntu OVAL Only
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Changelog has contents of changelog and how to get it.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user