Files
vuls/oval/redhat.go
2017-08-09 16:15:24 +09:00

178 lines
4.8 KiB
Go

package oval
import (
"fmt"
"strconv"
"strings"
"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/util"
ver "github.com/knqyf263/go-deb-version"
ovalconf "github.com/kotakanbe/goval-dictionary/config"
db "github.com/kotakanbe/goval-dictionary/db"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
// RedHatBase is the base struct for RedHat and CentOS
type RedHatBase struct{}
// FillCveInfoFromOvalDB returns scan result after updating CVE info by OVAL
func (o RedHatBase) FillCveInfoFromOvalDB(r *models.ScanResult) error {
if err := o.fillCveInfoFromOvalDB(r); err != nil {
return err
}
for _, vuln := range r.ScannedCves {
if cont, ok := vuln.CveContents[models.RedHat]; ok {
cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
}
}
return nil
}
// FillCveInfoFromOvalDB returns scan result after updating CVE info by OVAL
func (o RedHatBase) fillCveInfoFromOvalDB(r *models.ScanResult) error {
ovalconf.Conf.DBType = config.Conf.OvalDBType
ovalconf.Conf.DBPath = config.Conf.OvalDBPath
util.Log.Infof("open oval-dictionary db (%s): %s",
config.Conf.OvalDBType, config.Conf.OvalDBPath)
d := db.NewRedHat()
defer d.Close()
for _, pack := range r.Packages {
definitions, err := d.GetByPackName(r.Release, pack.Name)
if err != nil {
return fmt.Errorf("Failed to get RedHat OVAL info by package name: %v", err)
}
for _, definition := range definitions {
current, _ := ver.NewVersion(fmt.Sprintf("%s-%s", pack.Version, pack.Release))
for _, p := range definition.AffectedPacks {
if pack.Name != p.Name {
continue
}
affected, _ := ver.NewVersion(p.Version)
if current.LessThan(affected) {
o.fillOvalInfo(r, &definition)
}
}
}
}
return nil
}
func (o RedHatBase) fillOvalInfo(r *models.ScanResult, definition *ovalmodels.Definition) {
for _, cve := range definition.Advisory.Cves {
ovalContent := *o.convertToModel(cve.CveID, definition)
vinfo, ok := r.ScannedCves[cve.CveID]
if !ok {
util.Log.Infof("%s is newly detected by OVAL", cve.CveID)
vinfo = models.VulnInfo{
CveID: cve.CveID,
Confidence: models.OvalMatch,
PackageNames: getPackages(r, definition),
CveContents: models.NewCveContents(ovalContent),
}
} else {
cveContents := vinfo.CveContents
if _, ok := vinfo.CveContents[models.RedHat]; ok {
util.Log.Infof("%s will be updated by OVAL", cve.CveID)
} else {
util.Log.Infof("%s is also detected by OVAL", cve.CveID)
cveContents = models.CveContents{}
}
if vinfo.Confidence.Score < models.OvalMatch.Score {
vinfo.Confidence = models.OvalMatch
}
cveContents[models.RedHat] = ovalContent
vinfo.CveContents = cveContents
}
r.ScannedCves[cve.CveID] = vinfo
}
}
func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *models.CveContent {
for _, cve := range def.Advisory.Cves {
if cve.CveID != cveID {
continue
}
var refs []models.Reference
for _, r := range def.References {
refs = append(refs, models.Reference{
Link: r.RefURL,
Source: r.Source,
RefID: r.RefID,
})
}
score2, vec2 := o.parseCvss2(cve.Cvss2)
score3, vec3 := o.parseCvss3(cve.Cvss3)
return &models.CveContent{
Type: models.RedHat,
CveID: cve.CveID,
Title: def.Title,
Summary: def.Description,
Severity: def.Advisory.Severity,
Cvss2Score: score2,
Cvss2Vector: vec2,
Cvss3Score: score3,
Cvss3Vector: vec3,
SourceLink: "https://access.redhat.com/security/cve/" + cve.CveID,
References: refs,
CweID: cve.Cwe,
Published: def.Advisory.Issued,
LastModified: def.Advisory.Updated,
}
}
return nil
}
// ParseCvss2 divide CVSSv2 string into score and vector
// 5/AV:N/AC:L/Au:N/C:N/I:N/A:P
func (o RedHatBase) 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 (o RedHatBase) 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, ""
}
// RedHat is the interface for RedhatBase OVAL
type RedHat struct {
RedHatBase
}
// NewRedhat creates OVAL client for Redhat
func NewRedhat() RedHat {
return RedHat{}
}
// CentOS is the interface for CentOS OVAL
type CentOS struct {
RedHatBase
}
// NewCentOS creates OVAL client for CentOS
func NewCentOS() CentOS {
return CentOS{}
}