Support Debian

This commit is contained in:
knqyf263
2017-04-22 17:02:35 +09:00
committed by kota kanbe
parent 2cec20c7ee
commit 10a27042b5
8 changed files with 295 additions and 32 deletions

View File

@@ -50,6 +50,9 @@ type ReportCmd struct {
cvedbpath string
cvedbURL string
ovaldbtype string
ovaldbpath string
toSlack bool
toEMail bool
toLocalFile bool
@@ -162,6 +165,19 @@ func (p *ReportCmd) SetFlags(f *flag.FlagSet) {
defaultCveDBPath,
"/path/to/sqlite3 (For get cve detail from cve.sqlite3)")
f.StringVar(
&p.ovaldbtype,
"ovaldb-type",
"sqlite3",
"DB type for fetching OVAL dictionary (sqlite3 or mysql)")
defaultOvalDBPath := filepath.Join(wd, "oval.sqlite3")
f.StringVar(
&p.ovaldbpath,
"ovaldb-path",
defaultOvalDBPath,
"/path/to/sqlite3 (For get oval detail from oval.sqlite3)")
f.StringVar(
&p.cvedbURL,
"cvedb-url",
@@ -276,6 +292,8 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
c.Conf.CveDBType = p.cvedbtype
c.Conf.CveDBPath = p.cvedbpath
c.Conf.CveDBURL = p.cvedbURL
c.Conf.OvalDBType = p.ovaldbtype
c.Conf.OvalDBPath = p.ovaldbpath
c.Conf.CvssScoreOver = p.cvssScoreOver
c.Conf.IgnoreUnscoredCves = p.ignoreUnscoredCves
c.Conf.HTTPProxy = p.httpProxy
@@ -399,11 +417,18 @@ func (p *ReportCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{}
}
}
filled, err := fillCveInfoFromCveDB(r)
filled, err := fillCveInfoFromOvalDB(r)
if err != nil {
util.Log.Errorf("Failed to fill OVAL information: %s", err)
return subcommands.ExitFailure
}
filled, err = fillCveInfoFromCveDB(*filled)
if err != nil {
util.Log.Errorf("Failed to fill CVE information: %s", err)
return subcommands.ExitFailure
}
filled.Lang = c.Conf.Lang
if err := overwriteJSONFile(dir, *filled); err != nil {
util.Log.Errorf("Failed to write JSON: %s", err)

View File

@@ -35,19 +35,20 @@ import (
// ScanCmd is Subcommand of host discovery mode
type ScanCmd struct {
debug bool
configPath string
resultsDir string
logDir string
cacheDBPath string
httpProxy string
askKeyPassword bool
containersOnly bool
skipBroken bool
sshNative bool
pipe bool
timeoutSec int
scanTimeoutSec int
debug bool
configPath string
resultsDir string
logDir string
cacheDBPath string
httpProxy string
askKeyPassword bool
containersOnly bool
packageListOnly bool
skipBroken bool
sshNative bool
pipe bool
timeoutSec int
scanTimeoutSec int
}
// Name return subcommand name
@@ -132,6 +133,12 @@ func (p *ScanCmd) SetFlags(f *flag.FlagSet) {
"Ask ssh privatekey password before scanning",
)
f.BoolVar(
&p.packageListOnly,
"package-list-only",
false,
"List all packages without scan")
f.BoolVar(
&p.pipe,
"pipe",
@@ -223,6 +230,7 @@ func (p *ScanCmd) Execute(_ context.Context, f *flag.FlagSet, _ ...interface{})
c.Conf.SSHNative = p.sshNative
c.Conf.HTTPProxy = p.httpProxy
c.Conf.ContainersOnly = p.containersOnly
c.Conf.PackageListOnly = p.packageListOnly
c.Conf.SkipBroken = p.skipBroken
util.Log.Info("Validating config...")

View File

@@ -31,6 +31,7 @@ import (
c "github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/cveapi"
"github.com/future-architect/vuls/models"
"github.com/future-architect/vuls/oval"
"github.com/future-architect/vuls/report"
"github.com/future-architect/vuls/util"
)
@@ -180,6 +181,23 @@ func fillCveInfoFromCveDB(r models.ScanResult) (*models.ScanResult, error) {
return r.FillCveDetail()
}
func fillCveInfoFromOvalDB(r models.ScanResult) (*models.ScanResult, error) {
var ovalClient oval.OvalClient
switch r.Family {
case "ubuntu", "debian":
ovalClient = oval.NewDebian()
fmt.Println("hello")
case "redhat":
// TODO: RedHat
// ovalClient = oval.NewRedhat()
}
result, err := ovalClient.FillCveInfoFromOvalDB(r)
if err != nil {
return nil, err
}
return result, nil
}
func loadPreviousScanHistory(current models.ScanHistory) (previous models.ScanHistory, err error) {
var dirs jsonDirs
if dirs, err = lsValidJSONDirs(); err != nil {

View File

@@ -44,9 +44,10 @@ type Config struct {
CvssScoreOver float64
IgnoreUnscoredCves bool
SSHNative bool
ContainersOnly bool
SkipBroken bool
SSHNative bool
ContainersOnly bool
PackageListOnly bool
SkipBroken bool
HTTPProxy string `valid:"url"`
LogDir string
@@ -57,6 +58,9 @@ type Config struct {
CveDBURL string
CacheDBPath string
OvalDBType string
OvalDBPath string
FormatXML bool
FormatJSON bool
FormatOneEMail bool

View File

@@ -25,6 +25,7 @@ import (
"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"
)
// ScanHistory is the history of Scanning.
@@ -67,9 +68,9 @@ type ScanResult struct {
// Scanned Vulns via SSH + CPE Vulns
ScannedCves []VulnInfo
KnownCves []CveInfo
UnknownCves []CveInfo
IgnoredCves []CveInfo
KnownCves CveInfos
UnknownCves CveInfos
IgnoredCves CveInfos
Packages PackageInfoList
@@ -92,7 +93,7 @@ func (r ScanResult) FillCveDetail() (*ScanResult, error) {
return nil, err
}
known, unknown, ignored := CveInfos{}, CveInfos{}, CveInfos{}
r.IgnoredCves = CveInfos{}
for _, d := range ds {
cinfo := CveInfo{
CveDetail: d,
@@ -104,7 +105,7 @@ func (r ScanResult) FillCveDetail() (*ScanResult, error) {
found := false
for _, icve := range config.Conf.Servers[r.ServerName].IgnoreCves {
if icve == d.CveID {
ignored = append(ignored, cinfo)
r.IgnoredCves.Insert(cinfo)
found = true
break
}
@@ -113,29 +114,45 @@ func (r ScanResult) FillCveDetail() (*ScanResult, error) {
continue
}
// Update known if KnownCves already have cinfo
if c, ok := r.KnownCves.Get(cinfo.CveID); ok {
c.CveDetail = d
r.KnownCves.Update(c)
continue
}
// Update unknown if UnknownCves already have cinfo
if c, ok := r.UnknownCves.Get(cinfo.CveID); ok {
c.CveDetail = d
r.UnknownCves.Update(c)
continue
}
// unknown
if d.CvssScore(config.Conf.Lang) <= 0 {
unknown = append(unknown, cinfo)
r.UnknownCves.Insert(cinfo)
continue
}
// known
known = append(known, cinfo)
r.KnownCves.Insert(cinfo)
}
sort.Sort(known)
sort.Sort(unknown)
sort.Sort(ignored)
r.KnownCves = known
r.UnknownCves = unknown
r.IgnoredCves = ignored
sort.Sort(r.KnownCves)
sort.Sort(r.UnknownCves)
sort.Sort(r.IgnoredCves)
return &r, nil
}
// FilterByCvssOver is filter function.
func (r ScanResult) FilterByCvssOver() ScanResult {
cveInfos := []CveInfo{}
// TODO: Set correct default value
if config.Conf.CvssScoreOver == 0 {
config.Conf.CvssScoreOver = -1.1
}
for _, cveInfo := range r.KnownCves {
if config.Conf.CvssScoreOver < cveInfo.CveDetail.CvssScore(config.Conf.Lang) {
if config.Conf.CvssScoreOver <= cveInfo.CveDetail.CvssScore(config.Conf.Lang) {
cveInfos = append(cveInfos, cveInfo)
}
}
@@ -260,6 +277,9 @@ const (
// PkgAuditMatchStr is a String representation of PkgAuditMatch
PkgAuditMatchStr = "PkgAuditMatch"
// OvalMatchStr is a String representation of OvalMatch
OvalMatchStr = "OvalMatch"
// ChangelogExactMatchStr is a String representation of ChangelogExactMatch
ChangelogExactMatchStr = "ChangelogExactMatch"
@@ -282,6 +302,9 @@ var YumUpdateSecurityMatch = Confidence{100, YumUpdateSecurityMatchStr}
// PkgAuditMatch is a ranking how confident the CVE-ID was deteted correctly
var PkgAuditMatch = Confidence{100, PkgAuditMatchStr}
// OvalMatch is a ranking how confident the CVE-ID was deteted correctly
var OvalMatch = Confidence{100, OvalMatchStr}
// ChangelogExactMatch is a ranking how confident the CVE-ID was deteted correctly
var ChangelogExactMatch = Confidence{95, ChangelogExactMatchStr}
@@ -368,9 +391,50 @@ func (c CveInfos) Less(i, j int) bool {
return c[j].CveDetail.CvssScore(lang) < c[i].CveDetail.CvssScore(lang)
}
func (c CveInfos) Get(cveID string) (CveInfo, bool) {
for _, cve := range c {
if cve.VulnInfo.CveID == cveID {
return cve, true
}
}
return CveInfo{}, false
}
func (c *CveInfos) Delete(cveID string) {
cveInfos := *c
for i, cve := range cveInfos {
if cve.VulnInfo.CveID == cveID {
*c = append(cveInfos[:i], cveInfos[i+1:]...)
break
}
}
}
func (c *CveInfos) Insert(cveInfo CveInfo) {
*c = append(*c, cveInfo)
}
func (c CveInfos) Update(cveInfo CveInfo) (ok bool) {
for i, cve := range c {
if cve.VulnInfo.CveID == cveInfo.VulnInfo.CveID {
c[i] = cveInfo
return true
}
}
return false
}
func (c *CveInfos) Upsert(cveInfo CveInfo) {
ok := c.Update(cveInfo)
if !ok {
c.Insert(cveInfo)
}
}
// CveInfo has Cve Information.
type CveInfo struct {
CveDetail cve.CveDetail
CveDetail cve.CveDetail
OvalDetail goval.Definition
VulnInfo
}

109
oval/debian.go Normal file
View File

@@ -0,0 +1,109 @@
package oval
import (
"fmt"
"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"
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"
)
// Debian is the interface for Debian OVAL
type Debian struct{}
// NewDebian creates OVAL client for Debian
func NewDebian() Debian {
return Debian{}
}
// FillCveInfoFromOvalDB returns scan result after updating CVE info by OVAL
func (o Debian) FillCveInfoFromOvalDB(r models.ScanResult) (*models.ScanResult, error) {
util.Log.Debugf("open oval-dictionary db (%s)", config.Conf.OvalDBType)
ovalconf.Conf.DBType = config.Conf.OvalDBType
ovalconf.Conf.DBPath = config.Conf.OvalDBPath
if err := db.OpenDB(); err != nil {
return nil, fmt.Errorf("Failed to open OVAL DB. err: %s", err)
}
d := db.NewDebian()
for _, pack := range r.Packages {
// TODO: Set the correct release after implementing LIKE in goval-dictionary
definitions, err := d.GetByPackName("8.2", pack.Name)
if err != nil {
return nil, fmt.Errorf("Failed to get OVAL info by package name: %v", err)
}
for _, definition := range definitions {
current, _ := ver.NewVersion(pack.Version)
for _, p := range definition.AffectedPacks {
if pack.Name != p.Name {
continue
}
affected, _ := ver.NewVersion(p.Version)
if current.LessThan(affected) {
r = o.fillOvalInfo(r, definition)
}
}
}
}
return &r, nil
}
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 {
found = true
if cve.Confidence.Score < models.OvalMatch.Score {
cve.Confidence = models.OvalMatch
}
}
cves = append(cves, cve)
}
packageInfoList := getPackageInfoList(r, definition)
vuln := models.VulnInfo{
CveID: definition.Debian.CveID,
Confidence: models.OvalMatch,
Packages: packageInfoList,
}
if !found {
cves = append(cves, vuln)
}
r.ScannedCves = cves
// Update KnownCves by OVAL info
cveInfo, ok := r.KnownCves.Get(definition.Debian.CveID)
if !ok {
cveInfo.CveDetail = cve.CveDetail{
CveID: definition.Debian.CveID,
}
cveInfo.VulnInfo = vuln
}
cveInfo.OvalDetail = definition
if cveInfo.VulnInfo.Confidence.Score < models.OvalMatch.Score {
cveInfo.Confidence = models.OvalMatch
}
r.KnownCves.Upsert(cveInfo)
// Update UnknownCves by OVAL info
cveInfo, 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)
}
return r
}

24
oval/oval.go Normal file
View File

@@ -0,0 +1,24 @@
package oval
import (
"github.com/future-architect/vuls/models"
ovalmodels "github.com/kotakanbe/goval-dictionary/models"
)
// OvalClient is the interface of OVAL client.
type OvalClient interface {
FillCveInfoFromOvalDB(r models.ScanResult) (*models.ScanResult, error)
}
func getPackageInfoList(r models.ScanResult, d ovalmodels.Definition) models.PackageInfoList {
var packageInfoList models.PackageInfoList
for _, pack := range d.AffectedPacks {
for _, p := range r.Packages {
if pack.Name == p.Name {
packageInfoList = append(packageInfoList, p)
break
}
}
}
return packageInfoList
}

11
oval/redhat.go Normal file
View File

@@ -0,0 +1,11 @@
package oval
type redhat struct{}
func NewRedhat() redhat {
return redhat{}
}
func (o redhat) FillCveInfoFromOvalDB() {
}