Unify the models of NVD, JVN, OVAL
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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{
|
||||
|
||||
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.
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
102
oval/redhat.go
102
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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
134
report/tui.go
134
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,
|
||||
|
||||
254
report/util.go
254
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{
|
||||
|
||||
29
util/util.go
29
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, ""
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user