Imlement OVAL scan on Oracle Linux
This commit is contained in:
@@ -199,6 +199,8 @@ func NewCveContentType(name string) CveContentType {
|
||||
return JVN
|
||||
case "redhat", "centos":
|
||||
return RedHat
|
||||
case "oracle":
|
||||
return Oracle
|
||||
case "ubuntu":
|
||||
return Ubuntu
|
||||
case "debian":
|
||||
|
||||
@@ -53,7 +53,7 @@ func (v VulnInfos) FindScoredVulns() VulnInfos {
|
||||
})
|
||||
}
|
||||
|
||||
// ToSortedSlice returns slice of VulnInfos that is sorted by CVE-ID
|
||||
// ToSortedSlice returns slice of VulnInfos that is sorted by Score, CVE-ID
|
||||
func (v VulnInfos) ToSortedSlice() (sorted []VulnInfo) {
|
||||
for k := range v {
|
||||
sorted = append(sorted, v[k])
|
||||
@@ -244,7 +244,7 @@ func (v VulnInfo) Cvss3Scores() (values []CveContentCvss) {
|
||||
Type: CVSS3,
|
||||
Score: cont.Cvss3Score,
|
||||
Vector: cont.Cvss3Vector,
|
||||
Severity: sev,
|
||||
Severity: strings.ToUpper(sev),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ func (o Debian) FillWithOval(r *models.ScanResult) error {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO merge to VulnInfo.VendorLinks
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if cont, ok := vuln.CveContents[models.Debian]; ok {
|
||||
cont.SourceLink = "https://security-tracker.debian.org/tracker/" + cont.CveID
|
||||
@@ -174,6 +175,7 @@ func (o Ubuntu) FillWithOval(r *models.ScanResult) error {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO merge to VulnInfo.VendorLinks
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if cont, ok := vuln.CveContents[models.Ubuntu]; ok {
|
||||
cont.SourceLink = "http://people.ubuntu.com/~ubuntu-security/cve/" + cont.CveID
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff"
|
||||
@@ -115,13 +116,15 @@ func (b Base) CheckIfOvalFresh(osFamily, release string) (ok bool, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
major := strings.Split(release, ".")[0]
|
||||
since := time.Now()
|
||||
since = since.AddDate(0, 0, -3)
|
||||
if lastModified.Before(since) {
|
||||
util.Log.Warnf("%s-%s OVAL is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. To update OVAL database, see https://github.com/kotakanbe/goval-dictionary#usage",
|
||||
osFamily, release, lastModified)
|
||||
util.Log.Warnf("OVAL for %s %s is old, last modified is %s. It's recommended to update OVAL to improve scanning accuracy. How to update OVAL database, see https://github.com/kotakanbe/goval-dictionary#usage",
|
||||
osFamily, major, lastModified)
|
||||
return false, nil
|
||||
}
|
||||
util.Log.Infof("OVAL is fresh: %s %s ", osFamily, major)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,10 @@ import (
|
||||
)
|
||||
|
||||
// RedHatBase is the base struct for RedHat and CentOS
|
||||
type RedHatBase struct{ Base }
|
||||
type RedHatBase struct {
|
||||
Base
|
||||
family string
|
||||
}
|
||||
|
||||
// FillWithOval returns scan result after updating CVE info by OVAL
|
||||
func (o RedHatBase) FillWithOval(r *models.ScanResult) error {
|
||||
@@ -34,9 +37,17 @@ func (o RedHatBase) FillWithOval(r *models.ScanResult) error {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO merge to VulnInfo.VendorLinks
|
||||
for _, vuln := range r.ScannedCves {
|
||||
if cont, ok := vuln.CveContents[models.RedHat]; ok {
|
||||
cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
|
||||
switch models.NewCveContentType(o.family) {
|
||||
case models.RedHat:
|
||||
if cont, ok := vuln.CveContents[models.RedHat]; ok {
|
||||
cont.SourceLink = "https://access.redhat.com/security/cve/" + cont.CveID
|
||||
}
|
||||
case models.Oracle:
|
||||
if cont, ok := vuln.CveContents[models.Oracle]; ok {
|
||||
cont.SourceLink = fmt.Sprintf("https://linux.oracle.com/cve/%s.html", cont.CveID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -71,7 +82,7 @@ func (o RedHatBase) getDefsByPackNameFromOvalDB(osRelease string,
|
||||
|
||||
var ovaldb db.DB
|
||||
if ovaldb, err = db.NewDB(
|
||||
ovalconf.RedHat,
|
||||
o.family,
|
||||
ovalconf.Conf.DBType,
|
||||
ovalconf.Conf.DBPath,
|
||||
ovalconf.Conf.DebugSQL,
|
||||
@@ -82,7 +93,7 @@ func (o RedHatBase) getDefsByPackNameFromOvalDB(osRelease string,
|
||||
for _, pack := range packs {
|
||||
definitions, err := ovaldb.GetByPackName(osRelease, pack.Name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to get RedHat OVAL info by package name: %v", err)
|
||||
return nil, fmt.Errorf("Failed to get %s OVAL info by package name: %v", o.family, err)
|
||||
}
|
||||
for _, def := range definitions {
|
||||
current := ver.NewVersion(fmt.Sprintf("%s-%s", pack.Version, pack.Release))
|
||||
@@ -99,6 +110,7 @@ func (o RedHatBase) getDefsByPackNameFromOvalDB(osRelease string,
|
||||
}
|
||||
|
||||
func (o RedHatBase) update(r *models.ScanResult, definition *ovalmodels.Definition) {
|
||||
ctype := models.NewCveContentType(o.family)
|
||||
for _, cve := range definition.Advisory.Cves {
|
||||
ovalContent := *o.convertToModel(cve.CveID, definition)
|
||||
vinfo, ok := r.ScannedCves[cve.CveID]
|
||||
@@ -112,7 +124,7 @@ func (o RedHatBase) update(r *models.ScanResult, definition *ovalmodels.Definiti
|
||||
}
|
||||
} else {
|
||||
cveContents := vinfo.CveContents
|
||||
if _, ok := vinfo.CveContents[models.RedHat]; ok {
|
||||
if _, ok := vinfo.CveContents[ctype]; ok {
|
||||
util.Log.Debugf("%s will be updated by OVAL", cve.CveID)
|
||||
} else {
|
||||
util.Log.Debugf("%s also detected by OVAL", cve.CveID)
|
||||
@@ -122,7 +134,7 @@ func (o RedHatBase) update(r *models.ScanResult, definition *ovalmodels.Definiti
|
||||
if vinfo.Confidence.Score < models.OvalMatch.Score {
|
||||
vinfo.Confidence = models.OvalMatch
|
||||
}
|
||||
cveContents[models.RedHat] = ovalContent
|
||||
cveContents[ctype] = ovalContent
|
||||
vinfo.CveContents = cveContents
|
||||
}
|
||||
r.ScannedCves[cve.CveID] = vinfo
|
||||
@@ -147,7 +159,7 @@ func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *mo
|
||||
score3, vec3 := o.parseCvss3(cve.Cvss3)
|
||||
|
||||
return &models.CveContent{
|
||||
Type: models.RedHat,
|
||||
Type: models.NewCveContentType(o.family),
|
||||
CveID: cve.CveID,
|
||||
Title: def.Title,
|
||||
Summary: def.Description,
|
||||
@@ -156,7 +168,6 @@ func (o RedHatBase) convertToModel(cveID string, def *ovalmodels.Definition) *mo
|
||||
Cvss2Vector: vec2,
|
||||
Cvss3Score: score3,
|
||||
Cvss3Vector: vec3,
|
||||
SourceLink: "https://access.redhat.com/security/cve/" + cve.CveID,
|
||||
References: refs,
|
||||
CweID: cve.Cwe,
|
||||
Published: def.Advisory.Issued,
|
||||
@@ -201,7 +212,11 @@ type RedHat struct {
|
||||
|
||||
// NewRedhat creates OVAL client for Redhat
|
||||
func NewRedhat() RedHat {
|
||||
return RedHat{}
|
||||
return RedHat{
|
||||
RedHatBase{
|
||||
family: config.RedHat,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CentOS is the interface for CentOS OVAL
|
||||
@@ -211,5 +226,23 @@ type CentOS struct {
|
||||
|
||||
// NewCentOS creates OVAL client for CentOS
|
||||
func NewCentOS() CentOS {
|
||||
return CentOS{}
|
||||
return CentOS{
|
||||
RedHatBase{
|
||||
family: config.CentOS,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Oracle is the interface for CentOS OVAL
|
||||
type Oracle struct {
|
||||
RedHatBase
|
||||
}
|
||||
|
||||
// NewOracle creates OVAL client for Oracle
|
||||
func NewOracle() Oracle {
|
||||
return Oracle{
|
||||
RedHatBase{
|
||||
family: config.Oracle,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package report
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
c "github.com/future-architect/vuls/config"
|
||||
"github.com/future-architect/vuls/models"
|
||||
@@ -158,17 +159,17 @@ func fillWithOval(r *models.ScanResult) (err error) {
|
||||
ovalClient = oval.NewCentOS()
|
||||
//use RedHat's OVAL
|
||||
ovalFamily = c.RedHat
|
||||
case c.Oracle:
|
||||
ovalClient = oval.NewOracle()
|
||||
ovalFamily = c.Oracle
|
||||
//TODO
|
||||
// case c.Oracle:
|
||||
// ovalClient = oval.New()
|
||||
// ovalFamily = c.Oracle
|
||||
// case c.Suse:
|
||||
// ovalClient = oval.New()
|
||||
// ovalFamily = c.Oracle
|
||||
case c.Amazon, c.Oracle, c.Raspbian, c.FreeBSD:
|
||||
case c.Amazon, c.Raspbian, c.FreeBSD:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("Oval %s is not implemented yet", r.Family)
|
||||
return fmt.Errorf("OVAL for %s is not implemented yet", r.Family)
|
||||
}
|
||||
|
||||
ok, err := ovalClient.CheckIfOvalFetched(ovalFamily, r.Release)
|
||||
@@ -176,7 +177,8 @@ func fillWithOval(r *models.ScanResult) (err error) {
|
||||
return err
|
||||
}
|
||||
if !ok {
|
||||
util.Log.Warnf("OVAL entries of %s-%s are not found. It's recommended to use OVAL to improve scanning accuracy. To fetch OVAL, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", r.Family, r.Release)
|
||||
major := strings.Split(r.Release, ".")[0]
|
||||
util.Log.Warnf("OVAL entries of %s %s are not found. It's recommended to use OVAL to improve scanning accuracy. To fetch OVAL, see https://github.com/kotakanbe/goval-dictionary#usage , Then report with --ovaldb-path or --ovaldb-url flag", ovalFamily, major)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -184,7 +186,6 @@ func fillWithOval(r *models.ScanResult) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
util.Log.Infof("OVAL is fresh: %s-%s ", r.Family, r.Release)
|
||||
|
||||
if err := ovalClient.FillWithOval(r); err != nil {
|
||||
return err
|
||||
|
||||
@@ -233,14 +233,14 @@ func movable(v *gocui.View, nextY int) (ok bool, yLimit int) {
|
||||
}
|
||||
return true, yLimit
|
||||
case "detail":
|
||||
if currentDetailLimitY < nextY {
|
||||
return false, currentDetailLimitY
|
||||
}
|
||||
// if currentDetailLimitY < nextY {
|
||||
// return false, currentDetailLimitY
|
||||
// }
|
||||
return true, currentDetailLimitY
|
||||
case "changelog":
|
||||
if currentChangelogLimitY < nextY {
|
||||
return false, currentChangelogLimitY
|
||||
}
|
||||
// if currentChangelogLimitY < nextY {
|
||||
// return false, currentChangelogLimitY
|
||||
// }
|
||||
return true, currentChangelogLimitY
|
||||
default:
|
||||
return true, 0
|
||||
@@ -733,7 +733,7 @@ func setChangelogLayout(g *gocui.Gui) error {
|
||||
|
||||
type dataForTmpl struct {
|
||||
CveID string
|
||||
Cvsses []models.CveContentCvss
|
||||
Cvsses string
|
||||
Summary string
|
||||
Confidence models.Confidence
|
||||
Cwes []models.CveContentStr
|
||||
@@ -792,9 +792,23 @@ func detailLines() (string, error) {
|
||||
|
||||
summary := vinfo.Summaries(r.Lang, r.Family)[0]
|
||||
|
||||
table := uitable.New()
|
||||
table.MaxColWidth = maxColWidth
|
||||
table.Wrap = true
|
||||
scores := append(vinfo.Cvss3Scores(), vinfo.Cvss2Scores()...)
|
||||
var cols []interface{}
|
||||
for _, score := range scores {
|
||||
cols = []interface{}{
|
||||
score.Value.Severity,
|
||||
score.Value.Format(),
|
||||
score.Type,
|
||||
}
|
||||
table.AddRow(cols...)
|
||||
}
|
||||
|
||||
data := dataForTmpl{
|
||||
CveID: vinfo.CveID,
|
||||
Cvsses: append(vinfo.Cvss3Scores(), vinfo.Cvss2Scores()...),
|
||||
Cvsses: fmt.Sprintf("%s\n", table),
|
||||
Summary: fmt.Sprintf("%s (%s)", summary.Value, summary.Type),
|
||||
Confidence: vinfo.Confidence,
|
||||
Cwes: vinfo.CveContents.CweIDs(r.Family),
|
||||
@@ -817,9 +831,7 @@ const mdTemplate = `
|
||||
|
||||
CVSS Scores
|
||||
--------------
|
||||
{{range .Cvsses -}}
|
||||
* {{.Value.Severity}} {{.Value.Format}} ({{.Type}})
|
||||
{{end}}
|
||||
{{.Cvsses }}
|
||||
|
||||
Summary
|
||||
--------------
|
||||
|
||||
@@ -100,9 +100,9 @@ func formatShortPlainText(r models.ScanResult) string {
|
||||
|
||||
if len(vulns) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
No CVE-IDs are found in updatable packages.
|
||||
%s
|
||||
%s
|
||||
No CVE-IDs are found in updatable packages.
|
||||
%s
|
||||
`, header, r.Packages.FormatUpdatablePacksSummary())
|
||||
}
|
||||
|
||||
@@ -174,9 +174,9 @@ func formatFullPlainText(r models.ScanResult) string {
|
||||
|
||||
if len(vulns) == 0 {
|
||||
return fmt.Sprintf(`
|
||||
%s
|
||||
No CVE-IDs are found in updatable packages.
|
||||
%s
|
||||
%s
|
||||
No CVE-IDs are found in updatable packages.
|
||||
%s
|
||||
`, header, r.Packages.FormatUpdatablePacksSummary())
|
||||
}
|
||||
|
||||
|
||||
@@ -265,7 +265,13 @@ func (o *redhat) scanPackages() error {
|
||||
|
||||
func (o *redhat) scanInstalledPackages() (models.Packages, error) {
|
||||
installed := models.Packages{}
|
||||
cmd := "rpm -qa --queryformat '%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n'"
|
||||
var cmd string
|
||||
majorVersion, _ := o.Distro.MajorVersion()
|
||||
if majorVersion < 6 {
|
||||
cmd = "rpm -qa --queryformat '%{NAME} %{EPOCH} %{VERSION} %{RELEASE} %{ARCH}\n'"
|
||||
} else {
|
||||
cmd = "rpm -qa --queryformat '%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH}\n'"
|
||||
}
|
||||
r := o.exec(cmd, noSudo)
|
||||
if r.isSuccess() {
|
||||
// openssl 0 1.0.1e 30.el6.11 x86_64
|
||||
@@ -295,7 +301,7 @@ func (o *redhat) parseInstalledPackagesLine(line string) (models.Package, error)
|
||||
}
|
||||
ver := ""
|
||||
epoch := fields[1]
|
||||
if epoch == "0" {
|
||||
if epoch == "0" || epoch == "(none)" {
|
||||
ver = fields[2]
|
||||
} else {
|
||||
ver = fmt.Sprintf("%s:%s", epoch, fields[2])
|
||||
|
||||
Reference in New Issue
Block a user