From b545b5d0a3d1b44d9209650c608b0672f35185dd Mon Sep 17 00:00:00 2001 From: Kota Kanbe Date: Tue, 2 May 2017 15:39:22 +0900 Subject: [PATCH] Unify the models of NVD, JVN, OVAL --- commands/report.go | 2 + commands/util.go | 34 ++++-- commands/util_test.go | 19 +-- models/models.go | 267 ++++++++++++++++++++++++++++++++++++------ oval/debian.go | 75 ++++++++---- oval/oval.go | 1 + oval/redhat.go | 102 +++++++++++----- report/slack.go | 96 +++++++-------- report/tui.go | 134 ++++++++++++--------- report/util.go | 254 +++++++++++++++++++++------------------- util/util.go | 29 +++++ util/util_test.go | 66 +++++++++++ 12 files changed, 736 insertions(+), 343 deletions(-) diff --git a/commands/report.go b/commands/report.go index 85f2005f..5600e440 100644 --- a/commands/report.go +++ b/commands/report.go @@ -30,6 +30,7 @@ import ( "github.com/future-architect/vuls/report" "github.com/future-architect/vuls/util" "github.com/google/subcommands" + "github.com/k0kubun/pp" ) // ReportCmd is subcommand for reporting @@ -421,6 +422,7 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{} util.Log.Errorf("Failed to fill OVAL information: %s", err) return subcommands.ExitFailure } + pp.Println(filled) filled, err = fillCveInfoFromCveDB(*filled) if err != nil { diff --git a/commands/util.go b/commands/util.go index 1e0bee33..98306c36 100644 --- a/commands/util.go +++ b/commands/util.go @@ -246,15 +246,15 @@ func diff(current, previous models.ScanResults) (diff models.ScanResults, err er return diff, err } -func getNewCves(previousResult, currentResult models.ScanResult) (newVulninfos []models.VulnInfo) { +func getNewCves(previous, current models.ScanResult) (newVulninfos []models.VulnInfo) { previousCveIDsSet := map[string]bool{} - for _, previousVulnInfo := range previousResult.ScannedCves { + for _, previousVulnInfo := range previous.ScannedCves { previousCveIDsSet[previousVulnInfo.CveID] = true } - for _, v := range currentResult.ScannedCves { + for _, v := range current.ScannedCves { if previousCveIDsSet[v.CveID] { - if isCveInfoUpdated(currentResult, previousResult, v.CveID) { + if isCveInfoUpdated(current, previous, v.CveID) { newVulninfos = append(newVulninfos, v) } } else { @@ -264,25 +264,35 @@ func getNewCves(previousResult, currentResult models.ScanResult) (newVulninfos [ return } -func isCveInfoUpdated(currentResult, previousResult models.ScanResult, CveID string) bool { +func isCveInfoUpdated(current, previous models.ScanResult, CveID string) bool { type lastModified struct { Nvd time.Time Jvn time.Time } previousModifies := lastModified{} - for _, c := range previousResult.KnownCves { + for _, c := range previous.KnownCves { if CveID == c.CveID { - previousModifies.Nvd = c.CveDetail.Nvd.LastModifiedDate - previousModifies.Jvn = c.CveDetail.Jvn.LastModifiedDate + //TODO + if nvd, found := c.Get(models.NVD); found { + previousModifies.Nvd = nvd.LastModified + } + if jvn, found := c.Get(models.JVN); found { + previousModifies.Jvn = jvn.LastModified + } } } currentModifies := lastModified{} - for _, c := range currentResult.KnownCves { - if CveID == c.CveDetail.CveID { - currentModifies.Nvd = c.CveDetail.Nvd.LastModifiedDate - currentModifies.Jvn = c.CveDetail.Jvn.LastModifiedDate + for _, c := range current.KnownCves { + if CveID == c.VulnInfo.CveID { + //TODO + if nvd, found := c.Get(models.NVD); found { + previousModifies.Nvd = nvd.LastModified + } + if jvn, found := c.Get(models.JVN); found { + previousModifies.Jvn = jvn.LastModified + } } } return !currentModifies.Nvd.Equal(previousModifies.Nvd) || diff --git a/commands/util_test.go b/commands/util_test.go index 66bfa9f7..dfa5dc71 100644 --- a/commands/util_test.go +++ b/commands/util_test.go @@ -25,7 +25,6 @@ import ( "github.com/future-architect/vuls/models" "github.com/k0kubun/pp" - cve "github.com/kotakanbe/go-cve-dictionary/models" ) func TestDiff(t *testing.T) { @@ -174,10 +173,11 @@ func TestDiff(t *testing.T) { }, KnownCves: []models.CveInfo{ { - CveDetail: cve.CveDetail{ - CveID: "CVE-2016-6662", - Nvd: cve.Nvd{ - LastModifiedDate: time.Date(2016, 1, 1, 0, 0, 0, 0, time.Local), + CveContents: []models.CveContent{ + { + Type: models.NVD, + CveID: "CVE-2016-6662", + LastModified: time.Date(2016, 1, 1, 0, 0, 0, 0, time.Local), }, }, VulnInfo: models.VulnInfo{ @@ -214,10 +214,11 @@ func TestDiff(t *testing.T) { }, KnownCves: []models.CveInfo{ { - CveDetail: cve.CveDetail{ - CveID: "CVE-2016-6662", - Nvd: cve.Nvd{ - LastModifiedDate: time.Date(2017, 3, 15, 13, 40, 57, 0, time.Local), + CveContents: []models.CveContent{ + { + Type: models.NVD, + CveID: "CVE-2016-6662", + LastModified: time.Date(2017, 3, 15, 13, 40, 57, 0, time.Local), }, }, VulnInfo: models.VulnInfo{ diff --git a/models/models.go b/models/models.go index 277cea56..7eee245e 100644 --- a/models/models.go +++ b/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. diff --git a/oval/debian.go b/oval/debian.go index ac892097..b9cf3194 100644 --- a/oval/debian.go +++ b/oval/debian.go @@ -7,7 +7,6 @@ import ( "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" ver "github.com/knqyf263/go-deb-version" - cve "github.com/kotakanbe/go-cve-dictionary/models" ovalconf "github.com/kotakanbe/goval-dictionary/config" db "github.com/kotakanbe/goval-dictionary/db" ovalmodels "github.com/kotakanbe/goval-dictionary/models" @@ -62,54 +61,78 @@ func (o Debian) FillCveInfoFromOvalDB(r *models.ScanResult) (*models.ScanResult, func (o Debian) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Definition) *models.ScanResult { // Update ScannedCves by OVAL info found := false - cves := []models.VulnInfo{} - for _, cve := range r.ScannedCves { - if cve.CveID == definition.Debian.CveID { + updatedCves := []models.VulnInfo{} + + // Update scanned confidence to ovalmatch + for _, scanned := range r.ScannedCves { + if scanned.CveID == definition.Debian.CveID { found = true - if cve.Confidence.Score < models.OvalMatch.Score { - cve.Confidence = models.OvalMatch + if scanned.Confidence.Score < models.OvalMatch.Score { + scanned.Confidence = models.OvalMatch } } - cves = append(cves, cve) + updatedCves = append(updatedCves, scanned) } - packageInfoList := getPackageInfoList(r, definition) vuln := models.VulnInfo{ CveID: definition.Debian.CveID, Confidence: models.OvalMatch, - Packages: packageInfoList, + Packages: getPackageInfoList(r, definition), } if !found { - cves = append(cves, vuln) util.Log.Debugf("%s is newly detected by OVAL", vuln.CveID) + updatedCves = append(updatedCves, vuln) } - r.ScannedCves = cves + r.ScannedCves = updatedCves // Update KnownCves by OVAL info - cveInfo, ok := r.KnownCves.Get(definition.Debian.CveID) + ovalContent := *o.convertToModel(definition) + ovalContent.Type = models.CveContentType(r.Family) + cInfo, ok := r.KnownCves.Get(definition.Debian.CveID) if !ok { - cveInfo.CveDetail = cve.CveDetail{ - CveID: definition.Debian.CveID, - } - cveInfo.VulnInfo = vuln + cInfo.VulnInfo = vuln + cInfo.CveContents = []models.CveContent{ovalContent} } - cveInfo.OvalDetail = *definition - if cveInfo.VulnInfo.Confidence.Score < models.OvalMatch.Score { - cveInfo.Confidence = models.OvalMatch + if !cInfo.Update(ovalContent) { + cInfo.Insert(ovalContent) } - r.KnownCves.Upsert(cveInfo) + if cInfo.VulnInfo.Confidence.Score < models.OvalMatch.Score { + cInfo.Confidence = models.OvalMatch + } + r.KnownCves.Upsert(cInfo) // Update UnknownCves by OVAL info - cveInfo, ok = r.UnknownCves.Get(definition.Debian.CveID) + cInfo, ok = r.UnknownCves.Get(definition.Debian.CveID) if ok { - cveInfo.OvalDetail = *definition - if cveInfo.VulnInfo.Confidence.Score < models.OvalMatch.Score { - cveInfo.Confidence = models.OvalMatch - } r.UnknownCves.Delete(definition.Debian.CveID) - r.KnownCves.Upsert(cveInfo) + + // Insert new CveInfo + if !cInfo.Update(ovalContent) { + cInfo.Insert(ovalContent) + } + if cInfo.VulnInfo.Confidence.Score < models.OvalMatch.Score { + cInfo.Confidence = models.OvalMatch + } + r.KnownCves.Upsert(cInfo) } return r } + +func (o Debian) convertToModel(def *ovalmodels.Definition) *models.CveContent { + var refs []models.Reference + for _, r := range def.References { + refs = append(refs, models.Reference{ + Link: r.RefURL, + Source: r.Source, + RefID: r.RefID, + }) + } + return &models.CveContent{ + CveID: def.Debian.CveID, + Title: def.Title, + Summary: def.Description, + References: refs, + } +} diff --git a/oval/oval.go b/oval/oval.go index a247386e..0b324da0 100644 --- a/oval/oval.go +++ b/oval/oval.go @@ -15,6 +15,7 @@ func getPackageInfoList(r *models.ScanResult, d *ovalmodels.Definition) models.P for _, pack := range d.AffectedPacks { for _, p := range r.Packages { if pack.Name == p.Name { + p.Changelog = models.Changelog{} packageInfoList = append(packageInfoList, p) break } diff --git a/oval/redhat.go b/oval/redhat.go index ae3a127e..9a0e1abd 100644 --- a/oval/redhat.go +++ b/oval/redhat.go @@ -7,7 +7,6 @@ import ( "github.com/future-architect/vuls/models" "github.com/future-architect/vuls/util" ver "github.com/knqyf263/go-deb-version" - cve "github.com/kotakanbe/go-cve-dictionary/models" ovalconf "github.com/kotakanbe/goval-dictionary/config" db "github.com/kotakanbe/goval-dictionary/db" ovalmodels "github.com/kotakanbe/goval-dictionary/models" @@ -56,69 +55,108 @@ func (o Redhat) FillCveInfoFromOvalDB(r *models.ScanResult) (*models.ScanResult, } func (o Redhat) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Definition) *models.ScanResult { - found := make(map[string]bool) - vulnInfos := make(map[string]models.VulnInfo) - packageInfoList := getPackageInfoList(r, definition) + cveIDSet := make(map[string]bool) + cveID2VulnInfo := make(map[string]models.VulnInfo) for _, cve := range definition.Advisory.Cves { - found[cve.CveID] = false - vulnInfos[cve.CveID] = models.VulnInfo{ + cveIDSet[cve.CveID] = false + cveID2VulnInfo[cve.CveID] = models.VulnInfo{ CveID: cve.CveID, Confidence: models.OvalMatch, - Packages: packageInfoList, + Packages: getPackageInfoList(r, definition), } } // Update ScannedCves by OVAL info - cves := []models.VulnInfo{} - for _, scannedCve := range r.ScannedCves { + updatedCves := []models.VulnInfo{} + for _, scanned := range r.ScannedCves { + // Update scanned confidence to ovalmatch for _, c := range definition.Advisory.Cves { - if scannedCve.CveID == c.CveID { - found[c.CveID] = true - if scannedCve.Confidence.Score < models.OvalMatch.Score { - scannedCve.Confidence = models.OvalMatch + if scanned.CveID == c.CveID { + cveIDSet[c.CveID] = true + if scanned.Confidence.Score < models.OvalMatch.Score { + scanned.Confidence = models.OvalMatch } break } } - cves = append(cves, scannedCve) + updatedCves = append(updatedCves, scanned) } - for cveID, found := range found { + for cveID, found := range cveIDSet { if !found { - cves = append(cves, vulnInfos[cveID]) util.Log.Debugf("%s is newly detected by OVAL", cveID) + updatedCves = append(updatedCves, cveID2VulnInfo[cveID]) } } - r.ScannedCves = cves + r.ScannedCves = updatedCves // Update KnownCves by OVAL info for _, c := range definition.Advisory.Cves { - cveInfo, ok := r.KnownCves.Get(c.CveID) + ovalContent := *o.convertToModel(c.CveID, definition) + cInfo, ok := r.KnownCves.Get(c.CveID) if !ok { - cveInfo.CveDetail = cve.CveDetail{ - CveID: c.CveID, - } - cveInfo.VulnInfo = vulnInfos[c.CveID] + cInfo.VulnInfo = cveID2VulnInfo[c.CveID] + cInfo.CveContents = []models.CveContent{ovalContent} } - cveInfo.OvalDetail = *definition - if cveInfo.VulnInfo.Confidence.Score < models.OvalMatch.Score { - cveInfo.Confidence = models.OvalMatch + if !cInfo.Update(ovalContent) { + cInfo.Insert(ovalContent) } - r.KnownCves.Upsert(cveInfo) + if cInfo.VulnInfo.Confidence.Score < models.OvalMatch.Score { + cInfo.Confidence = models.OvalMatch + } + r.KnownCves.Upsert(cInfo) } // Update UnknownCves by OVAL info for _, c := range definition.Advisory.Cves { - cveInfo, ok := r.UnknownCves.Get(c.CveID) + cInfo, ok := r.UnknownCves.Get(c.CveID) if ok { - cveInfo.OvalDetail = *definition - if cveInfo.VulnInfo.Confidence.Score < models.OvalMatch.Score { - cveInfo.Confidence = models.OvalMatch - } r.UnknownCves.Delete(c.CveID) - r.KnownCves.Upsert(cveInfo) + + // Insert new CveInfo + ovalContent := *o.convertToModel(c.CveID, definition) + if !cInfo.Update(ovalContent) { + cInfo.Insert(ovalContent) + } + if cInfo.VulnInfo.Confidence.Score < models.OvalMatch.Score { + cInfo.Confidence = models.OvalMatch + } + r.KnownCves.Upsert(cInfo) } } return r } + +func (o Redhat) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent { + for _, cve := range def.Advisory.Cves { + if cve.CveID != cveID { + continue + } + var refs []models.Reference + //TODO RHSAのリンクを入れる + for _, r := range def.References { + refs = append(refs, models.Reference{ + Link: r.RefURL, + Source: r.Source, + RefID: r.RefID, + }) + } + + // util.ParseCvss2() + + return &models.CveContent{ + Type: models.RedHat, + CveID: cve.CveID, + Title: def.Title, + Summary: def.Description, + Severity: def.Advisory.Severity, + // V2Score: v2Score, // TODO divide into score and vector + Cvss2Vector: cve.Cvss2, // TODO divide into score and vector + Cvss3Vector: cve.Cvss3, // TODO divide into score and vector + References: refs, + CweID: cve.Cwe, + } + } + return nil +} diff --git a/report/slack.go b/report/slack.go index fd9b5bf1..3b9c9b74 100644 --- a/report/slack.go +++ b/report/slack.go @@ -166,7 +166,7 @@ func toSlackAttachments(scanResult models.ScanResult) (attaches []*attachment) { } for _, cveInfo := range cves { - cveID := cveInfo.CveDetail.CveID + cveID := cveInfo.VulnInfo.CveID curentPackages := []string{} for _, p := range cveInfo.Packages { @@ -199,7 +199,7 @@ func toSlackAttachments(scanResult models.ScanResult) (attaches []*attachment) { Short: true, }, }, - Color: color(cveInfo.CveDetail.CvssScore(config.Conf.Lang)), + Color: color(cveInfo.CvssV2Score()), } attaches = append(attaches, &a) } @@ -221,57 +221,61 @@ func color(cvssScore float64) string { } func attachmentText(cveInfo models.CveInfo, osFamily string) string { - linkText := links(cveInfo, osFamily) - switch { - case config.Conf.Lang == "ja" && - 0 < cveInfo.CveDetail.Jvn.CvssScore(): + // linkText := links(cveInfo, osFamily) + //TODO + return "" + // switch { + // case config.Conf.Lang == "ja" && + // 0 < cveInfo.CveDetail.Jvn.CvssScore(): - jvn := cveInfo.CveDetail.Jvn - return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s\n*Confidence:* %v", - cveInfo.CveDetail.CvssScore(config.Conf.Lang), - jvn.CvssSeverity(), - fmt.Sprintf(cvssV2CalcBaseURL, cveInfo.CveDetail.CveID), - jvn.CvssVector(), - jvn.CveTitle(), - linkText, - cveInfo.VulnInfo.Confidence, - ) - case 0 < cveInfo.CveDetail.CvssScore("en"): - nvd := cveInfo.CveDetail.Nvd - return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s\n*Confidence:* %v", - cveInfo.CveDetail.CvssScore(config.Conf.Lang), - nvd.CvssSeverity(), - fmt.Sprintf(cvssV2CalcBaseURL, cveInfo.CveDetail.CveID), - nvd.CvssVector(), - nvd.CveSummary(), - linkText, - cveInfo.VulnInfo.Confidence, - ) - default: - nvd := cveInfo.CveDetail.Nvd - return fmt.Sprintf("?\n%s\n%s\n*Confidence:* %v", - nvd.CveSummary(), linkText, cveInfo.VulnInfo.Confidence) - } + // jvn := cveInfo.CveDetail.Jvn + // return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s\n*Confidence:* %v", + // cveInfo.CveDetail.CvssScore(config.Conf.Lang), + // jvn.CvssSeverity(), + // fmt.Sprintf(cvssV2CalcBaseURL, cveInfo.CveDetail.CveID), + // jvn.CvssVector(), + // jvn.CveTitle(), + // linkText, + // cveInfo.VulnInfo.Confidence, + // ) + // case 0 < cveInfo.CveDetail.CvssScore("en"): + // nvd := cveInfo.CveDetail.Nvd + // return fmt.Sprintf("*%4.1f (%s)* <%s|%s>\n%s\n%s\n*Confidence:* %v", + // cveInfo.CveDetail.CvssScore(config.Conf.Lang), + // nvd.CvssSeverity(), + // fmt.Sprintf(cvssV2CalcBaseURL, cveInfo.CveDetail.CveID), + // nvd.CvssVector(), + // nvd.CveSummary(), + // linkText, + // cveInfo.VulnInfo.Confidence, + // ) + // default: + // nvd := cveInfo.CveDetail.Nvd + // return fmt.Sprintf("?\n%s\n%s\n*Confidence:* %v", + // nvd.CveSummary(), linkText, cveInfo.VulnInfo.Confidence) + // } } func links(cveInfo models.CveInfo, osFamily string) string { links := []string{} - cweID := cveInfo.CveDetail.CweID() - if 0 < len(cweID) { - links = append(links, fmt.Sprintf("<%s|%s>", - cweURL(cweID), cweID)) - if config.Conf.Lang == "ja" { - links = append(links, fmt.Sprintf("<%s|%s(JVN)>", - cweJvnURL(cweID), cweID)) - } - } + //TODO + // cweID := cveInfo.CveDetail.CweID() + // if 0 < len(cweID) { + // links = append(links, fmt.Sprintf("<%s|%s>", + // cweURL(cweID), cweID)) + // if config.Conf.Lang == "ja" { + // links = append(links, fmt.Sprintf("<%s|%s(JVN)>", + // cweJvnURL(cweID), cweID)) + // } + // } - cveID := cveInfo.CveDetail.CveID - if config.Conf.Lang == "ja" && 0 < len(cveInfo.CveDetail.Jvn.Link()) { - jvn := fmt.Sprintf("<%s|JVN>", cveInfo.CveDetail.Jvn.Link()) - links = append(links, jvn) - } + cveID := cveInfo.VulnInfo.CveID + //TODO + // if config.Conf.Lang == "ja" && 0 < len(cveInfo.CveDetail.Jvn.Link()) { + // jvn := fmt.Sprintf("<%s|JVN>", cveInfo.CveDetail.Jvn.Link()) + // links = append(links, jvn) + // } dlinks := distroLinks(cveInfo, osFamily) for _, link := range dlinks { links = append(links, diff --git a/report/tui.go b/report/tui.go index 564c6154..74ab3cf2 100644 --- a/report/tui.go +++ b/report/tui.go @@ -26,7 +26,6 @@ import ( "time" log "github.com/Sirupsen/logrus" - "github.com/future-architect/vuls/config" "github.com/future-architect/vuls/models" "github.com/google/subcommands" "github.com/gosuri/uitable" @@ -613,39 +612,53 @@ func summaryLines() string { for i, d := range currentScanResult.AllCves() { var cols []string - // packs := []string{} - // for _, pack := range d.Packages { - // packs = append(packs, pack.Name) - // } - if config.Conf.Lang == "ja" && 0 < d.CveDetail.Jvn.CvssScore() { - summary := d.CveDetail.Jvn.CveTitle() - cols = []string{ - fmt.Sprintf(indexFormat, i+1), - d.CveDetail.CveID, - fmt.Sprintf("| %4.1f", - d.CveDetail.CvssScore(config.Conf.Lang)), - fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score), - summary, - } - } else { - summary := d.CveDetail.Nvd.CveSummary() - - var cvssScore string - if d.CveDetail.CvssScore("en") <= 0 { - cvssScore = "| ?" - } else { - cvssScore = fmt.Sprintf("| %4.1f", - d.CveDetail.CvssScore(config.Conf.Lang)) - } - - cols = []string{ - fmt.Sprintf(indexFormat, i+1), - d.CveDetail.CveID, - cvssScore, - fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score), - summary, - } + //TODO + var summary string + if cont, found := d.Get(models.NVD); found { + summary = cont.Summary } + var cvssScore string + if d.CvssV2Score() <= 0 { + cvssScore = "| ?" + } else { + cvssScore = fmt.Sprintf("| %4.1f", d.CvssV2Score()) + } + cols = []string{ + fmt.Sprintf(indexFormat, i+1), + d.VulnInfo.CveID, + cvssScore, + fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score), + summary, + } + // if config.Conf.Lang == "ja" && 0 < d.CveDetail.Jvn.CvssScore() { + // summary := d.CveDetail.Jvn.CveTitle() + // cols = []string{ + // fmt.Sprintf(indexFormat, i+1), + // d.CveDetail.CveID, + // fmt.Sprintf("| %4.1f", + // d.CveDetail.CvssScore(config.Conf.Lang)), + // fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score), + // summary, + // } + // } else { + // summary := d.CveDetail.Nvd.CveSummary() + + // var cvssScore string + // if d.CveDetail.CvssScore("en") <= 0 { + // cvssScore = "| ?" + // } else { + // cvssScore = fmt.Sprintf("| %4.1f", + // d.CveDetail.CvssScore(config.Conf.Lang)) + // } + + // cols = []string{ + // fmt.Sprintf(indexFormat, i+1), + // d.CveDetail.CveID, + // cvssScore, + // fmt.Sprintf("| %3d |", d.VulnInfo.Confidence.Score), + // summary, + // } + // } icols := make([]interface{}, len(cols)) for j := range cols { @@ -748,7 +761,7 @@ func detailLines() (string, error) { } cveInfo := currentScanResult.AllCves()[currentCveInfo] - cveID := cveInfo.CveDetail.CveID + cveID := cveInfo.VulnInfo.CveID tmpl, err := template.New("detail").Parse(detailTemplate()) if err != nil { @@ -758,22 +771,27 @@ func detailLines() (string, error) { var cvssSeverity, cvssVector, summary string var refs []cve.Reference switch { - case config.Conf.Lang == "ja" && - 0 < cveInfo.CveDetail.Jvn.CvssScore(): - jvn := cveInfo.CveDetail.Jvn - cvssSeverity = jvn.CvssSeverity() - cvssVector = jvn.CvssVector() - summary = fmt.Sprintf("%s\n%s", jvn.CveTitle(), jvn.CveSummary()) - refs = jvn.VulnSiteReferences() + //TODO + // case config.Conf.Lang == "ja" && + // 0 < cveInfo.CveDetail.Jvn.CvssScore(): + // jvn := cveInfo.CveDetail.Jvn + // cvssSeverity = jvn.CvssSeverity() + // cvssVector = jvn.CvssVector() + // summary = fmt.Sprintf("%s\n%s", jvn.CveTitle(), jvn.CveSummary()) + // refs = jvn.VulnSiteReferences() default: - nvd := cveInfo.CveDetail.Nvd - cvssSeverity = nvd.CvssSeverity() - cvssVector = nvd.CvssVector() - summary = nvd.CveSummary() - refs = nvd.VulnSiteReferences() + var nvd *models.CveContent + if cont, found := cveInfo.Get(models.NVD); found { + nvd = cont + } + // cvssSeverity = nvd.CvssSeverity() + // cvssVector = nvd.CvssVector() + summary = nvd.Summary + // refs = nvd.VulnSiteReferences() } - cweURL := cweURL(cveInfo.CveDetail.CweID()) + //TODO + // cweURL := cweURL(cveInfo.CveDetail.CweID()) links := []string{ fmt.Sprintf("[NVD]( %s )", fmt.Sprintf("%s/%s", nvdBaseURL, cveID)), @@ -787,11 +805,12 @@ func detailLines() (string, error) { links = append(links, fmt.Sprintf("[%s]( %s )", link.title, link.url)) } + //TODO var cvssScore string - if cveInfo.CveDetail.CvssScore(config.Conf.Lang) == -1 { + if cveInfo.CvssV2Score() == -1 { cvssScore = "?" - } else { - cvssScore = fmt.Sprintf("%4.1f", cveInfo.CveDetail.CvssScore(config.Conf.Lang)) + // } else { + // cvssScore = fmt.Sprintf("%4.1f", cveInfo.CveDetail.CvssScore(config.Conf.Lang)) } packages := []string{} @@ -804,13 +823,14 @@ func detailLines() (string, error) { } data := dataForTmpl{ - CveID: cveID, - CvssScore: cvssScore, - CvssSeverity: cvssSeverity, - CvssVector: cvssVector, - Summary: summary, - Confidence: cveInfo.VulnInfo.Confidence, - CweURL: cweURL, + CveID: cveID, + CvssScore: cvssScore, + CvssSeverity: cvssSeverity, + CvssVector: cvssVector, + Summary: summary, + Confidence: cveInfo.VulnInfo.Confidence, + //TODO + // CweURL: cweURL, VulnSiteLinks: links, References: refs, Packages: packages, diff --git a/report/util.go b/report/util.go index 51ac589a..e7b6420d 100644 --- a/report/util.go +++ b/report/util.go @@ -126,38 +126,43 @@ No CVE-IDs are found in updatable packages. var scols []string switch { - case config.Conf.Lang == "ja" && - 0 < d.CveDetail.Jvn.CvssScore(): - summary := fmt.Sprintf("%s\n%s\n%s\n%sConfidence: %v", - d.CveDetail.Jvn.CveTitle(), - d.CveDetail.Jvn.Link(), - distroLinks(d, r.Family)[0].url, - packsVer, - d.VulnInfo.Confidence, - ) - scols = []string{ - d.CveDetail.CveID, - fmt.Sprintf("%-4.1f (%s)", - d.CveDetail.CvssScore(config.Conf.Lang), - d.CveDetail.Jvn.CvssSeverity(), - ), - summary, - } + // case config.Conf.Lang == "ja" && + //TODO + // 0 < d.CveDetail.Jvn.CvssScore(): + // summary := fmt.Sprintf("%s\n%s\n%s\n%sConfidence: %v", + // d.CveDetail.Jvn.CveTitle(), + // d.CveDetail.Jvn.Link(), + // distroLinks(d, r.Family)[0].url, + // packsVer, + // d.VulnInfo.Confidence, + // ) + // scols = []string{ + // d.CveDetail.CveID, + // fmt.Sprintf("%-4.1f (%s)", + // d.CveDetail.CvssScore(config.Conf.Lang), + // d.CveDetail.Jvn.CvssSeverity(), + // ), + // summary, + // } - case 0 < d.CveDetail.CvssScore("en"): + case 0 < d.CvssV2Score(): + var nvd *models.CveContent + if cont, found := d.Get(models.NVD); found { + nvd = cont + } summary := fmt.Sprintf("%s\n%s/%s\n%s\n%sConfidence: %v", - d.CveDetail.Nvd.CveSummary(), + nvd.Summary, cveDetailsBaseURL, - d.CveDetail.CveID, + d.VulnInfo.CveID, distroLinks(d, r.Family)[0].url, packsVer, d.VulnInfo.Confidence, ) scols = []string{ - d.CveDetail.CveID, + d.VulnInfo.CveID, fmt.Sprintf("%-4.1f (%s)", - d.CveDetail.CvssScore(config.Conf.Lang), - d.CveDetail.Nvd.CvssSeverity(), + d.CvssV2Score(), + "TODO", ), summary, } @@ -165,7 +170,7 @@ No CVE-IDs are found in updatable packages. summary := fmt.Sprintf("%s\n%sConfidence: %v", distroLinks(d, r.Family)[0].url, packsVer, d.VulnInfo.Confidence) scols = []string{ - d.CveDetail.CveID, + d.VulnInfo.CveID, "?", summary, } @@ -229,39 +234,40 @@ No CVE-IDs are found in updatable packages. return fmt.Sprintf("%s\n%s\n%s", header, detail, formatChangelogs(r)) } +//TODO func formatPlainTextDetails(r models.ScanResult, osFamily string) (scoredReport, unscoredReport []string) { - for _, cve := range r.KnownCves { - switch config.Conf.Lang { - case "en": - if 0 < cve.CveDetail.Nvd.CvssScore() { - scoredReport = append( - scoredReport, formatPlainTextDetailsLangEn(cve, osFamily)) - } else { - scoredReport = append( - scoredReport, formatPlainTextUnknownCve(cve, osFamily)) - } - case "ja": - if 0 < cve.CveDetail.Jvn.CvssScore() { - scoredReport = append( - scoredReport, formatPlainTextDetailsLangJa(cve, osFamily)) - } else if 0 < cve.CveDetail.Nvd.CvssScore() { - scoredReport = append( - scoredReport, formatPlainTextDetailsLangEn(cve, osFamily)) - } else { - scoredReport = append( - scoredReport, formatPlainTextUnknownCve(cve, osFamily)) - } - } - } - for _, cve := range r.UnknownCves { - unscoredReport = append( - unscoredReport, formatPlainTextUnknownCve(cve, osFamily)) - } + // for _, cve := range r.KnownCves { + // switch config.Conf.Lang { + // case "en": + // if 0 < cve.CveDetail.Nvd.CvssScore() { + // scoredReport = append( + // scoredReport, formatPlainTextDetailsLangEn(cve, osFamily)) + // } else { + // scoredReport = append( + // scoredReport, formatPlainTextUnknownCve(cve, osFamily)) + // } + // case "ja": + // if 0 < cve.CveDetail.Jvn.CvssScore() { + // scoredReport = append( + // scoredReport, formatPlainTextDetailsLangJa(cve, osFamily)) + // } else if 0 < cve.CveDetail.Nvd.CvssScore() { + // scoredReport = append( + // scoredReport, formatPlainTextDetailsLangEn(cve, osFamily)) + // } else { + // scoredReport = append( + // scoredReport, formatPlainTextUnknownCve(cve, osFamily)) + // } + // } + // } + // for _, cve := range r.UnknownCves { + // unscoredReport = append( + // unscoredReport, formatPlainTextUnknownCve(cve, osFamily)) + // } return } func formatPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string { - cveID := cveInfo.CveDetail.CveID + cveID := cveInfo.VulnInfo.CveID dtable := uitable.New() dtable.MaxColWidth = maxColWidth dtable.Wrap = true @@ -281,90 +287,94 @@ func formatPlainTextUnknownCve(cveInfo models.CveInfo, osFamily string) string { return fmt.Sprintf("%s", dtable) } +//TODO func formatPlainTextDetailsLangJa(cveInfo models.CveInfo, osFamily string) string { - cveDetail := cveInfo.CveDetail - cveID := cveDetail.CveID - jvn := cveDetail.Jvn + return "TODO" + // cveDetail := cveInfo.CveDetail + // cveID := cveDetail.CveID + // jvn := cveDetail.Jvn - dtable := uitable.New() - dtable.MaxColWidth = maxColWidth - dtable.Wrap = true - dtable.AddRow(cveID) - dtable.AddRow("-------------") - if score := cveDetail.Jvn.CvssScore(); 0 < score { - dtable.AddRow("Score", - fmt.Sprintf("%4.1f (%s)", - cveDetail.Jvn.CvssScore(), - jvn.CvssSeverity(), - )) - } else { - dtable.AddRow("Score", "?") - } - dtable.AddRow("Vector", jvn.CvssVector()) - dtable.AddRow("Title", jvn.CveTitle()) - dtable.AddRow("Description", jvn.CveSummary()) - dtable.AddRow(cveDetail.CweID(), cweURL(cveDetail.CweID())) - dtable.AddRow(cveDetail.CweID()+"(JVN)", cweJvnURL(cveDetail.CweID())) + // dtable := uitable.New() + // dtable.MaxColWidth = maxColWidth + // dtable.Wrap = true + // dtable.AddRow(cveID) + // dtable.AddRow("-------------") + // if score := cveDetail.Jvn.CvssScore(); 0 < score { + // dtable.AddRow("Score", + // fmt.Sprintf("%4.1f (%s)", + // cveDetail.Jvn.CvssScore(), + // jvn.CvssSeverity(), + // )) + // } else { + // dtable.AddRow("Score", "?") + // } + // dtable.AddRow("Vector", jvn.CvssVector()) + // dtable.AddRow("Title", jvn.CveTitle()) + // dtable.AddRow("Description", jvn.CveSummary()) + // dtable.AddRow(cveDetail.CweID(), cweURL(cveDetail.CweID())) + // dtable.AddRow(cveDetail.CweID()+"(JVN)", cweJvnURL(cveDetail.CweID())) - dtable.AddRow("JVN", jvn.Link()) - dtable.AddRow("NVD", fmt.Sprintf("%s/%s", nvdBaseURL, cveID)) - dtable.AddRow("MITRE", fmt.Sprintf("%s%s", mitreBaseURL, cveID)) - dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)) - dtable.AddRow("CVSSv2 Clac", fmt.Sprintf(cvssV2CalcBaseURL, cveID)) - dtable.AddRow("CVSSv3 Clac", fmt.Sprintf(cvssV3CalcBaseURL, cveID)) + // dtable.AddRow("JVN", jvn.Link()) + // dtable.AddRow("NVD", fmt.Sprintf("%s/%s", nvdBaseURL, cveID)) + // dtable.AddRow("MITRE", fmt.Sprintf("%s%s", mitreBaseURL, cveID)) + // dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)) + // dtable.AddRow("CVSSv2 Clac", fmt.Sprintf(cvssV2CalcBaseURL, cveID)) + // dtable.AddRow("CVSSv3 Clac", fmt.Sprintf(cvssV3CalcBaseURL, cveID)) - dlinks := distroLinks(cveInfo, osFamily) - for _, link := range dlinks { - dtable.AddRow(link.title, link.url) - } + // dlinks := distroLinks(cveInfo, osFamily) + // for _, link := range dlinks { + // dtable.AddRow(link.title, link.url) + // } - dtable = addPackageInfos(dtable, cveInfo.Packages) - dtable = addCpeNames(dtable, cveInfo.CpeNames) - dtable.AddRow("Confidence", cveInfo.VulnInfo.Confidence) + // dtable = addPackageInfos(dtable, cveInfo.Packages) + // dtable = addCpeNames(dtable, cveInfo.CpeNames) + // dtable.AddRow("Confidence", cveInfo.VulnInfo.Confidence) - return fmt.Sprintf("%s", dtable) + // return fmt.Sprintf("%s", dtable) } +//TODO func formatPlainTextDetailsLangEn(d models.CveInfo, osFamily string) string { - cveDetail := d.CveDetail - cveID := cveDetail.CveID - nvd := cveDetail.Nvd + return "" + // cveDetail := d.CveDetail + // cveID := cveDetail.CveID + // nvd := cveDetail.Nvd - dtable := uitable.New() - dtable.MaxColWidth = maxColWidth - dtable.Wrap = true - dtable.AddRow(cveID) - dtable.AddRow("-------------") + // dtable := uitable.New() + // dtable.MaxColWidth = maxColWidth + // dtable.Wrap = true + // dtable.AddRow(cveID) + // dtable.AddRow("-------------") - if score := cveDetail.Nvd.CvssScore(); 0 < score { - dtable.AddRow("Score", - fmt.Sprintf("%4.1f (%s)", - cveDetail.Nvd.CvssScore(), - nvd.CvssSeverity(), - )) - } else { - dtable.AddRow("Score", "?") - } + // if score := cveDetail.Nvd.CvssScore(); 0 < score { + // dtable.AddRow("Score", + // fmt.Sprintf("%4.1f (%s)", + // cveDetail.Nvd.CvssScore(), + // nvd.CvssSeverity(), + // )) + // } else { + // dtable.AddRow("Score", "?") + // } - dtable.AddRow("Vector", nvd.CvssVector()) - dtable.AddRow("Summary", nvd.CveSummary()) - dtable.AddRow("CWE", cweURL(cveDetail.CweID())) + // dtable.AddRow("Vector", nvd.CvssVector()) + // dtable.AddRow("Summary", nvd.CveSummary()) + // dtable.AddRow("CWE", cweURL(cveDetail.CweID())) - dtable.AddRow("NVD", fmt.Sprintf("%s/%s", nvdBaseURL, cveID)) - dtable.AddRow("MITRE", fmt.Sprintf("%s%s", mitreBaseURL, cveID)) - dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)) - dtable.AddRow("CVSSv2 Clac", fmt.Sprintf(cvssV2CalcBaseURL, cveID)) - dtable.AddRow("CVSSv3 Clac", fmt.Sprintf(cvssV3CalcBaseURL, cveID)) + // dtable.AddRow("NVD", fmt.Sprintf("%s/%s", nvdBaseURL, cveID)) + // dtable.AddRow("MITRE", fmt.Sprintf("%s%s", mitreBaseURL, cveID)) + // dtable.AddRow("CVE Details", fmt.Sprintf("%s/%s", cveDetailsBaseURL, cveID)) + // dtable.AddRow("CVSSv2 Clac", fmt.Sprintf(cvssV2CalcBaseURL, cveID)) + // dtable.AddRow("CVSSv3 Clac", fmt.Sprintf(cvssV3CalcBaseURL, cveID)) - links := distroLinks(d, osFamily) - for _, link := range links { - dtable.AddRow(link.title, link.url) - } - dtable = addPackageInfos(dtable, d.Packages) - dtable = addCpeNames(dtable, d.CpeNames) - dtable.AddRow("Confidence", d.VulnInfo.Confidence) + // links := distroLinks(d, osFamily) + // for _, link := range links { + // dtable.AddRow(link.title, link.url) + // } + // dtable = addPackageInfos(dtable, d.Packages) + // dtable = addCpeNames(dtable, d.CpeNames) + // dtable.AddRow("Confidence", d.VulnInfo.Confidence) - return fmt.Sprintf("%s\n", dtable) + // return fmt.Sprintf("%s\n", dtable) } type distroLink struct { @@ -374,7 +384,7 @@ type distroLink struct { // distroLinks add Vendor URL of the CVE to table func distroLinks(cveInfo models.CveInfo, osFamily string) []distroLink { - cveID := cveInfo.CveDetail.CveID + cveID := cveInfo.VulnInfo.CveID switch osFamily { case "rhel", "centos": links := []distroLink{ diff --git a/util/util.go b/util/util.go index 3b94e8a6..c022c87d 100644 --- a/util/util.go +++ b/util/util.go @@ -20,6 +20,7 @@ package util import ( "fmt" "net/url" + "strconv" "strings" "github.com/future-architect/vuls/config" @@ -135,3 +136,31 @@ func Truncate(str string, length int) string { } return str } + +// ParseCvss2 divide CVSSv2 string into score and vector +// 5/AV:N/AC:L/Au:N/C:N/I:N/A:P +func ParseCvss2(scoreVector string) (score float64, vector string) { + var err error + ss := strings.Split(scoreVector, "/") + if 1 < len(ss) { + if score, err = strconv.ParseFloat(ss[0], 64); err != nil { + return 0, "" + } + return score, strings.Join(ss[1:len(ss)], "/") + } + return 0, "" +} + +// ParseCvss3 divide CVSSv3 string into score and vector +// 5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L +func ParseCvss3(scoreVector string) (score float64, vector string) { + var err error + ss := strings.Split(scoreVector, "/CVSS:3.0/") + if 1 < len(ss) { + if score, err = strconv.ParseFloat(ss[0], 64); err != nil { + return 0, "" + } + return score, strings.Join(ss[1:len(ss)], "/") + } + return 0, "" +} diff --git a/util/util_test.go b/util/util_test.go index 49340a3d..19ce9f92 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -171,3 +171,69 @@ func TestTruncate(t *testing.T) { } } } + +func TestParseCvss2(t *testing.T) { + type out struct { + score float64 + vector string + } + var tests = []struct { + in string + out out + }{ + { + in: "5/AV:N/AC:L/Au:N/C:N/I:N/A:P", + out: out{ + score: 5.0, + vector: "AV:N/AC:L/Au:N/C:N/I:N/A:P", + }, + }, + { + in: "", + out: out{ + score: 0, + vector: "", + }, + }, + } + for _, tt := range tests { + s, v := ParseCvss2(tt.in) + if s != tt.out.score || v != tt.out.vector { + t.Errorf("\nexpected: %f, %s\n actual: %f, %s", + tt.out.score, tt.out.vector, s, v) + } + } +} + +func TestParseCvss3(t *testing.T) { + type out struct { + score float64 + vector string + } + var tests = []struct { + in string + out out + }{ + { + in: "5.6/CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L", + out: out{ + score: 5.6, + vector: "AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L", + }, + }, + { + in: "", + out: out{ + score: 0, + vector: "", + }, + }, + } + for _, tt := range tests { + s, v := ParseCvss3(tt.in) + if s != tt.out.score || v != tt.out.vector { + t.Errorf("\nexpected: %f, %s\n actual: %f, %s", + tt.out.score, tt.out.vector, s, v) + } + } +}